Стабильный 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_HANDLE | Handle нулевой, неизвестен или уже уничтожен |
HPDF_STATUS_INCOMPATIBLE_ABI | Размер структуры, версия или flags не поддерживаются |
HPDF_STATUS_CANCELLED | Callback отмены запросил завершение |
HPDF_STATUS_IO_ERROR | Callback ввода или вывода упал либо нарушил контракт по количеству |
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