Стабильный C callback ABI HotPDF

HotPDFABI.dll раскрывает плоские cdecl-функции со статусными значениями фиксированной ширины, opaque handle, явными длинами в байтах и версионированной callback-структурой hpdf_io_v1, объявленной в Lib/hotpdf_abi.h

Результаты сборки

Запустите build-HotPDF-ABI.cmd, чтобы собрать DLL Win32 и Win64 плюс import library под Lib/ABI/<platform>/Release

Публичный ABI использует стабильные строчные имена экспортов, а hpdf_abi_version и hpdf_abi_io_v1_size позволяют вызывающим проверить контракт до создания документа

Основные функции

uint32_t hpdf_abi_version(void);
uint32_t hpdf_abi_io_v1_size(void);
hpdf_status hpdf_document_create(hpdf_document *handle);
hpdf_status hpdf_document_destroy(hpdf_document handle);
hpdf_status hpdf_document_load_from_io(
    hpdf_document handle,
    const hpdf_io_v1 *io,
    const char *password,
    size_t password_length);
hpdf_status hpdf_document_page_count(
    hpdf_document handle,
    uint32_t *page_count);
hpdf_status hpdf_document_save_to_io(
    hpdf_document handle,
    const hpdf_io_v1 *io);

Версионированная структура I/O

hpdf_io_v1 io = {0};
io.struct_size = sizeof(io);
io.abi_version = HPDF_ABI_VERSION_1;
io.user_data = state;
io.read_at = read_at;
io.get_size = get_size;
io.write = write;
io.is_cancelled = is_cancelled;
io.progress = progress;
io.diagnostic = diagnostic;

struct_size равен 48 байтам на Win32 и 72 байтам на Win64, flags для ABI v1 должны быть нулём, а более крупные будущие структуры различимы без угадывания раскладки полей

Все callback и экспортируемые функции используют cdecl, каждый текстовый указатель имеет явную длину в байтах, и через границу не проходит ни одна строка, класс, интерфейс, enum, исключение или Boolean Delphi

Вход с произвольным доступом

read_at получает абсолютное 64-битное смещение и пишет напрямую в буфер назначения парсера, а get_size один раз сообщает неизменяемый размер источника при его подключении

Частичные успешные чтения принимаются и завершаются повторными вызовами callback, поэтому range-хранилища и ограниченные сетевые читатели могут не выделять промежуточный буфер под весь файл

Состояние callback и байты источника обязаны оставаться валидными, пока handle документа не уничтожен или пока в него не загружен другой источник

Потоковый вывод

write получает последовательные chunk вывода напрямую от SaveLoadedDocumentToStream; частичные успешные записи повторяются, пока chunk целиком не принят

Успешная запись нуля байтов отклоняется как ошибка I/O, потому что она не продвигает работу вперёд

Для операций JSON execute и сравнения двух handle упавший write callback не вызывается снова, чтобы опубликовать ошибочный результат через ту же пару writer и user-data, в том числе когда OutputIO и ResultIO разделяют эту пару; частичные байты кандидата, уже полученные вызывающей стороной, остаются её ответственностью

Статусные значения вне определённого диапазона ненулевых статусов, а также нулевое или чрезмерное число успешно записанных байтов, превращаются в HPDF_STATUS_IO_ERROR; раскладка записи V1 и поведение успешной частичной записи остаются неизменными

Статусы и жизненный цикл

СтатусЗначение
HPDF_STATUS_OKОперация завершена
HPDF_STATUS_INVALID_ARGUMENTОтсутствует требуемый указатель или callback, либо диапазон байтов некорректен
HPDF_STATUS_INVALID_HANDLEHandle нулевой, неизвестен или уже уничтожен
HPDF_STATUS_INCOMPATIBLE_ABIРазмер структуры, версия или flags не поддерживаются
HPDF_STATUS_CANCELLEDCallback отмены запросил завершение
HPDF_STATUS_IO_ERRORCallback ввода или вывода упал либо нарушил контракт по количеству
HPDF_STATUS_PARSE_ERRORВход прочитан успешно, но не принят как PDF
HPDF_STATUS_INTERNAL_ERRORВнутренний сбой перехвачен до пересечения границы ABI

Каждый успешный hpdf_document_create должен быть парен одному успешному hpdf_document_destroy; повторное уничтожение возвращает HPDF_STATUS_INVALID_HANDLE

Opaque handle — монотонные локальные для процесса токены, а не адреса объектов, поэтому переиспользование аллокатором не может воскресить устаревший handle

Исключения никогда не пересекают C-границу, а опциональный диагностический callback получает соответствующие байты сообщения до того, как операция вернёт статус ошибки

Конкурентность

Разные handle можно использовать конкурентно, но операции и уничтожение одного handle вызывающие обязаны сериализовать

Устаревшие Pascal callback

THPDFABICallbacks и Pascal-хелперы HPDFDoc* остаются совместимыми по исходникам для приложений Delphi и C++Builder, но нативные C-интеграции должны использовать hotpdf_abi.h и строчные экспорты v1