PDFlibPas перетворює вміст PDF на два редаговані формати без автоматизації Office. ExportPageMarkdown і ExportDocumentMarkdown повертають семантичний Markdown із визначеними заголовками, впорядкованими й невпорядкованими списками та таблицями у форматі pipe, тоді як SaveDOCXToFile і SaveDOCXToStream записують пакет WordprocessingML з абзацами, заголовками, нативною нумерацією списків, розпізнаними таблицями, стилізацією шрифту, розривами сторінок і позиціонованими зображеннями PNG
Обидва працюють цілком на Pascal, на сервері, без встановленого Word і без COM. Саме це обмеження — причина, чому ця функція існує в PDF-бібліотеці, а не в десктопному інструменті
Чому «PDF у Word» — це справді складне завдання?
Тому що сторінка PDF не містить абзаців. Вона містить оператори відображення тексту, які розміщують послідовності гліфів за координатами в тому порядку, в якому їх видав виробник документа, без жодного обов'язку позначати, що дві послідовності належать до одного речення, не кажучи вже про той самий пункт списку. Формат був спроєктований так, щоб точно описувати надруковану сторінку, і досягає цього, відкидаючи структуру, яка цю сторінку породила
Тож кожен конвертер має відтворити те, що генератор викинув. Групування рядків спирається на вертикальні відступи та вирівнювання по базовій лінії. Межі абзаців визначаються змінами відступів і абзацним відступом. Заголовок — це рядок, шрифт якого більший або жирніший за основний текст і який відокремлений від того, що йде далі. Список — це послідовність абзаців, що починаються з маркера-кулі або числового шаблону. Таблиця — це сітка текстових блоків, краї яких вирівняні по рядках і стовпцях. Кожне з цього — висновок за непрямими ознаками, а такий висновок означає добрий результат на документах, що дотримуються звичайних типографських конвенцій, і посередній — на тих, що ні
Позначені (tagged) PDF — це виняток, і значний. Коли документ несе дерево структури, ролі абзаца, заголовка, списку й таблиці записані, а не вгадані, і саме тому робота над доступністю, описана в статті про структуру доступності позначеного PDF, окупається і в якості конвертації. Якщо ви керуєте виробником документів, тегування вашого виводу — це найефективніша окрема дія, яку ви можете зробити для будь-кого, кому пізніше доведеться його конвертувати
Експорт у Markdown, по одній сторінці
Шлях через Markdown варто обирати, коли пунктом призначення є текстовий конвеєр: сайт документації, пошуковий індекс, корпус для пошуку асистента. Опції — це бітова маска: PDF_MARKDOWN_INCLUDE_PAGE_MARKERS, PDF_MARKDOWN_DETECT_HEADINGS, PDF_MARKDOWN_PRESERVE_STYLES, а PDF_MARKDOWN_DEFAULT поєднує всі три
var
Pdf: TPDFlib;
Md: WideString;
begin
Pdf := TPDFlib.Create;
try
Pdf.LoadFromFile('handbook.pdf', '');
// Одна сторінка, як рядок
Md := Pdf.ExportPageMarkdown(1, PDF_MARKDOWN_DEFAULT);
// Діапазон сторінок, потоково записаний на диск у UTF-8 без BOM
Pdf.SaveMarkdownToFile('1-40',
PDF_MARKDOWN_DETECT_HEADINGS or PDF_MARKDOWN_PRESERVE_STYLES,
'handbook.md');
finally
Pdf.Free;
end;
end;
Маркери сторінок окуповують себе в задачах пошуку та вибірки. Фрагмент тексту, який несе інформацію про сторінку походження, можна точно процитувати, і читач, який переходить за цитатою, потрапляє саме туди, де насправді знаходиться твердження. Вимикайте їх, коли Markdown призначений для читання людиною, де межі сторінок з початкового макета — просто шум
Потокові точки входу мають значення для великих документів. SaveMarkdownToStream і SaveMarkdownToFile записують UTF-8 по одній сторінці й не буферизують повний вивід, тож посібник на 900 сторінок не перетворюється спершу на 900-сторінковий рядок у пам'яті. Відсутність позначки порядку байтів (BOM) теж навмисна: BOM у файлі Markdown спантеличує на диво велику кількість генераторів статичних сайтів та інструментів порівняння diff
DOCX без Office на машині
Записувач DOCX самостійно формує весь пакет: записи ZIP у форматі сирого Deflate з перевірками CRC, частини WordprocessingML і зв'язки, які їх поєднують. Нічого не звертається до Word, а це означає, що конвертація виконується на безголовому сервері, під сервісним обліковим записом, у контейнері — у всіх тих місцях, де автоматизація Office або нелегальна, або нестабільна, або взагалі заборонена
var
Pdf: TPDFlib;
Target: TFileStream;
begin
Pdf := TPDFlib.Create;
Target := TFileStream.Create('handbook.docx', fmCreate);
try
Pdf.LoadFromFile('handbook.pdf', '');
Pdf.SaveDOCXToStream('1-40',
PDF_DOCX_INCLUDE_IMAGES or PDF_DOCX_DETECT_HEADINGS or
PDF_DOCX_PRESERVE_STYLES or PDF_DOCX_PRESERVE_PAGE_BREAKS,
Target);
finally
Target.Free;
Pdf.Free;
end;
end;
Дані зображень записуються по мірі обробки кожної сторінки, а не збираються й додаються в кінці, тож пікове використання пам'яті відповідає одній сторінці, а не всьому документу. Явний порядок сторінок зберігається, а обрана сторінка PDF відновлюється після завершення, що має значення, коли експорт — лише один крок усередині довшого завдання, в якому сторінку було обрано з інших причин
Що дає детермінована упаковка?
Побайтову відтворюваність. Дві конвертації одного й того самого вхідного файлу з однаковими опціями створюють однаковий пакет, а це означає, що можна хешувати вивід для виявлення змін, порівнювати diff'ом дві збірки згенерованого документа й агресивно кешувати, не хвилюючись, що ідентичний вхід дав різний артефакт
Автоматизація Office такого не гарантує. Вона вбудовує часові мітки, ідентифікатори версій і метадані, залежні від конкретної машини, тож один і той самий документ, конвертований двічі, відрізняється у спосіб, що зводить хешування нанівець. Та сама логіка лежить в основі детермінованих ідентифікаторів файлів, розглянутих у статті про детерміновані ID PDF для відтворюваних збірок: коли вивід відтворюваний, перевірка стає порівнянням, а не інспекцією
Де вивід хороший, а де ні
Будьте чесними з користувачами щодо цього, адже якість конвертації залежить від вхідних даних більше, ніж від самого конвертера. Позначені PDF і охайно згенеровані ділові документи — рахунки-фактури, звіти, договори — конвертуються добре: заголовки стають заголовками, таблиці зберігаються, списки коректно перенумеровуються у Word. Двоколонкові академічні макети конвертуються прийнятно, якщо геометрія колонок регулярна. Таблиці, що охоплюють кілька сторінок, збираються заново за висновком за непрямими ознаками й іноді розбиваються. Насичено оформлені маркетингові матеріали, де текст розміщено заради візуального ефекту, а не в порядку читання, конвертуються погано, і жодна кількість висновків за непрямими ознаками цього не виправить
Скановані документи — це зовсім окремий випадок. Сторінка, що є одним великим зображенням, не містить текстових об'єктів, тож експортувати нічого, доки не з'явиться текстовий шар; шлях OCR, який його створює, — це передумова, а не опція. Перш ніж запускати велику партію, вибірково перевірте десяток репрезентативних файлів і подивіться на результат, а також розгляньте спершу перелічення елементів сторінки, як описано в статті про пошук тексту та перелічення елементів сторінки, щоб побачити, що насправді містять сторінки
Для конвеєрів асистентів і пошуку за вибіркою шлях через Markdown зазвичай є кращою ціллю: заголовки стають межами фрагментів, таблиці лишаються читабельними у форматі pipe, а маркери сторінок дають кожному фрагменту місце, яке можна процитувати. Для редагування людиною відповідь — DOCX, бо користувачеві потрібен не сам текст, а можливість його змінювати
PDFlibPas — це PDF-бібліотека для Delphi, C++Builder і Lazarus з відповідними інтерфейсами DLL та ActiveX, тож ті самі виклики експорту доступні з C#, C++ або скриптових хостів. Повна документація та пробна збірка — на сторінці PDFlibPas Delphi PDF library