Это мой персональный дневник. Пишу, по большей части, про историю, свою жизнь и немного про программирование.

Определение ширины и высоты файла JPEG

В «Эгее» есть какой-то плавающий баг, из-за которого иногда не заливаются файлы JPEG. Илья, автор «Эгеи», пытался с ним разобраться, но до конца понять проблему не смог.

Пока рабочая теория такая, что функция ПХП getimagesize (она возвращает тип изображения и его высоту с шириной) иногда выдаёт неправильные значения.

Опровергнуть или подтвердить эту теорию не так-то и просто — баг проявляется очень редко, но можно попытаться считывать размеры каким-то другим способом и посмотреть исчезнет проблема или нет.

Вот Илья и попросил написать меня такой код на чистом ПХП. Надеюсь это позволит продвинуться в изучении проблемы — код вошёл в одиннадцатую версию «Эгеи».

Выложу его у себя тоже, чтобы не потерялся, ну и может кому-то ещё пригодится:

function readJPEGWxH(string $filename): array
{
    $file = new SplFileObject($filename);

    if (!$file->flock(LOCK_SH)) {
        throw new RuntimeException('Cannot lock the file');
    }

    if ($file->fread(2) !== "\xFF\xD8") {
        throw new RuntimeException('Unknown format');
    }

    for (;;) {
        $decoded = @unpack('H4segment/nlen', $file->fread(4));

        if ($decoded === false) {
            throw new RuntimeException('Unknown format');
        }

        ['segment' => $segment, 'len' => $len] = $decoded;

        // SOFn (Start Of Frame)?
        if (preg_match('/^ffc[0-3]/', $segment)) {
            $segdata = @unpack('Cbits/nheight/nwidth', $file->fread(5));

            if ($segdata === false) {
                throw new RuntimeException('Unknown format');
            }

            return [$segdata['width'], $segdata['height']];
        }

        // len = full lenght of segment except segment magic code
        if ($file->fseek($len - 2, SEEK_CUR) !== 0) {
            throw new RuntimeException('Cannot find start of frame');
        }
    }
}