Невозможно загрузить файл TTF напрямую из ZIP с помощью libzip и FreeType.

Я пытаюсь загрузить файл TTF непосредственно из ZIP-архива, используя libzip и FreeType.

В частности, я использую функцию FT_Open_Face, которая может читать из пользовательского чтения /close функции (ft_zip_read и ft_zip_close). Но хотя файл явно прочитан полностью, FT_Open_Face возвращает FT_Err_Unknown_File_Format. Открытие того же файла напрямую с диска работает нормально.

Я действительно не знаю, как отладить это, может кто-нибудь помочь?

Единственное, что я могу себе представить сейчас, это то, что моя функция ft_zip_read не поддерживает поиск, документация говорит:

Эта функция может быть вызвана для выполнения операции поиска или пропуска с «счетчиком», равным 0. Ненулевое возвращаемое значение указывает на ошибку.

И он действительно вызывается пару раз со счетом 0, но я не вижу способа выполнить поиск с помощью libzip.

unsigned long ft_zip_read(FT_Stream stream, unsigned long offset,
                          unsigned char* buffer, unsigned long count)
{
    zip_file* file = static_cast<zip_file*>(stream->descriptor.pointer);
    return zip_fread(file, buffer + offset, count);
}

void ft_zip_close(FT_Stream stream)
{
    zip_file* file = static_cast<zip_file*>(stream->descriptor.pointer);
    zip_fclose(file);
}

FT_Face load_zipped_face(const std::string& name, unsigned int size,
                         const std::string& zip_path)
{
    FT_Library library;
    FT_Error error = FT_Init_FreeType(&library);
    if (error)
        throw freetype_error_string("Failed to initialise FreeType", error);

    int zip_error;
    zip* zip = zip_open(zip_path.c_str(), 0, &zip_error);
    if (!zip) {
        std::ostringstream message_stream;
        message_stream << "Error loading ZIP (" << zip_path <<  "): "
                       << zip_error;
        throw message_stream.str();
    }

    std::string face_path = name + ".ttf";

    struct zip_stat stat;
    if (zip_stat(zip, face_path.c_str(), 0, &stat))
        throw std::string("zip_stat failed");

    zip_file* file = zip_fopen(zip, face_path.c_str(), 0);
    if (file == 0)
        throw face_path + ": " + strerror(errno);

    FT_StreamDesc descriptor;
    descriptor.pointer = file;

    FT_StreamRec* stream = new FT_StreamRec;
    stream->base = 0;
    stream->size = stat.size;
    stream->descriptor = descriptor;
    stream->read = &ft_zip_read;
    stream->close = &ft_zip_close;

    FT_Open_Args open_args;
    open_args.flags = FT_OPEN_STREAM;
    open_args.stream = stream;

    FT_Face face;
    error = FT_Open_Face(library, &open_args, 0, &face);

    zip_close(zip);

    if (error == FT_Err_Unknown_File_Format)
        throw std::string("Unsupported format");
    else if (error)
        throw freetype_error_string("Unknown error loading font", error);

    error = FT_Set_Pixel_Sizes(face, 0, size);
    if (error)
        throw freetype_error_string("Unable to set pixel sizes", error);

    return face;
}

person futlib    schedule 14.01.2013    source источник


Ответы (1)


В поисках правды

Чтобы иметь возможность искать в сжатом потоке данных, вам необходимо распаковать поток до точки, к которой вы хотите обратиться (существуют некоторые исключения, такие как потоки со сбросом маркеров и индексов, которые должны распаковываться только после предыдущего маркера). Это просто неэффективно, если делать это часто (не говоря уже о том, что вам нужно писать для этого код самостоятельно).

Если подумать, единственная причина, по которой вы не хотите загружать все лицо в память и иметь собственный ввод-вывод для файла шрифта, заключается в том, что он слишком велик для хранения в памяти; так что это делает поиск обязательным для интерфейса потокового ввода-вывода FT.

Что ты можешь сделать?

Если файл достаточно мал: прочитайте его все в память и используйте FT_New_Memory_Face для загрузки лица из памяти.

Если файл слишком велик и вам не нужно сразу помещать в память все лицо, извлеките файл шрифта во временный файл и прочитайте его. (Используйте API временных файлов windows/unix/cstdio, чтобы иметь хорошо работающий временный файл)

Если ни один из вышеперечисленных вариантов вам не подходит, вы можете реализовать свой собственный zip-поток с кэшированием и возможностью поиска поверх libzip и передать его в FT. Это, вероятно, громоздко и требует некоторой работы, поэтому я бы лично пошел с одним из двух других :)

person Emily L.    schedule 12.08.2013