Техническа статия

PDF към Markdown и DOCX в Delphi с PDF Library for Delphi

PDF Library for Delphi превръща PDF съдържание в два редактируеми формата без Office automation. ExportPageMarkdown и ExportDocumentMarkdown връщат семантичен Markdown с изведени заглавия, номерирани и ненумерирани списъци и таблици с вертикална черта, докато SaveDOCXToFile и SaveDOCXToStream записват WordprocessingML пакет, съдържащ параграфи, заглавия, native номерация на списъци, разпознати таблици, стилизиране на шрифта, разделители на страници и позиционирани PNG изображения

И двата пътя работят изцяло в Pascal, на сървър, без инсталиран Word и без COM. Точно това ограничение е причината функцията да съществува в PDF библиотека, а не в настолен инструмент

Защо „PDF към Word“ наистина е трудно?

Защото PDF страница не съдържа параграфи. Тя съдържа оператори за показване на текст, които поставят поредици от глифи на координати, в какъвто ред производителят ги е изписал, без задължение да означи, че две поредици принадлежат на едно и също изречение, камо ли на един и същ елемент от списък. Форматът е проектиран да опише отпечатана страница точно и успява в това, като изхвърля структурата, произвела страницата

Затова всеки конвертор трябва да възстанови онова, което генераторът е изхвърлил. Групирането на редове идва от вертикалните разстояния и подравняването по базова линия. Границите на параграфи идват от промени в разстоянията и отстъпите. Заглавие е ред, чийто шрифт е по-голям или по-плътен от основния текст и се откроява от онова, което следва. Списък е поредица от параграфи, започващи с знак за водещ символ или числов образец. Таблица е решетка от текстови блокове, чиито ръбове се подравняват през редове и колони. Всяко от тези е предположение, а предположението означава добър резултат при документи, следващи обичайните типографски конвенции, и посредствен при документи, които не ги следват

PDF Library for Delphi диаграма на конвейера за изводи, превръщащ сурови PDF глифови проходи в групирани редове, абзаци, заглавки, списъци и таблици, експортирани като Markdown или DOCX
Всяко заглавие, списък и таблица в експорта се извеждат от разстояния, шрифтове и подравнени ръбове, защото самият PDF формат не записва нито едно от тях

Таг-ираните PDF документи са изключението, и то голямо. Когато документът носи дърво на структурата, ролите на параграф, заглавие, списък и таблица са записани, а не отгатнати, което е причината работата по достъпност, описана в структурата за достъпност на таг-иран PDF, да се отплаща и при конвертиране. Ако контролирате производителя, таг-ирането на изхода ви е единственото действие с най-голяма възвръщаемост за всеки, който по-късно трябва да го конвертира

Изнасяне в Markdown, страница по страница

Пътят през Markdown е онзи, към който да посегнете, когато крайната цел е текстов конвейер: сайт с документация, търсещ индекс, retrieval корпус за асистент. Опциите са битова маска: 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;

Маркерите за страници се отплащат в retrieval работата. Откъс текст, който носи страницата, от която идва, може да бъде цитиран точно, а читател, който следва цитата, попада точно там, където твърдението наистина се намира. Изключете ги, когато Markdown-ът е предназначен за човешко четене, където границите на страници от изходното оформление са шум

Поточните входни точки имат значение за големи документи. SaveMarkdownToStream и SaveMarkdownToFile записват UTF-8 страница по страница и не буферират целия изход, така че наръчник от 900 страници не се превръща първо в низ от 900 страници в паметта. Отсъствието на byte-order mark също е съзнателно: BOM в Markdown файл обърква изненадващо много генератори на статични сайтове и diff инструменти

DOCX без Office на машината

DOCX writer-ът произвежда самия пакет: ZIP записи, записани като суров Deflate с CRC проверки, WordprocessingML частите и връзките, които ги обвързват. Нищо не извиква Word, което означава, че конвертирането работи на headless сървър, вътре в service account, в контейнер, навсякъде, където Office automation е или нелицензиран, или нестабилен, или забранен

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 страница се възстановява след това, което има значение, когато изнасянето е една стъпка в по-дълга задача, при която е била избрана страница по други причини

Какво печелите от детерминистичното пакетиране?

Възпроизводимост байт по байт. Две конвертирания на един и същ вход с едни и същи опции произвеждат един и същ пакет, което означава, че можете да хеширате изхода, за да засечете промяна, да сравните две компилации на генериран документ и да кеширате агресивно, без да се притеснявате, че еднакъв вход е произвел различен артефакт

PDF Library for Delphi диаграма на поточния Markdown експорт, където битовата маска на опциите избира странични маркери, откриване на заглавки и запазване на стила преди UTF-8 запис страница по страница
Опциите се съчетават като битова маска, докато streaming записващите издават UTF-8 без BOM, поддържайки пикова памет равна и оставяйки всеки извлечен откъс с цитируем номер на страница

Office automation не може да обещае това. Тя вгражда времеви маркери, идентификатори на ревизии и машинно зависими метаданни, така че един и същ документ, конвертиран два пъти, се различава по начини, които осуетяват хеширането. Същото разсъждение стои зад детерминистичните идентификатори на файлове, разгледани в детерминистичните PDF ID за възпроизводими компилации: когато изходът е възпроизводим, проверката става сравнение, а не инспекция

Къде изходът е добър и къде не е

Бъдете честни с потребителите си по този въпрос, защото качеството на конвертиране варира повече в зависимост от входа, отколкото от конвертора. Таг-ирани PDF документи и чисто генерирани бизнес документи — фактури, отчети, договори — се конвертират добре: заглавията стават заглавия, таблиците оцеляват, списъците се пренумерират правилно в Word. Двуколонни академични оформления се конвертират приемливо, ако геометрията на колоните е регулярна. Таблици, разпростиращи се през прекъсвания на страници, се пресъбират чрез предположение и понякога се разделят. Силно дизайнерски маркетингови материали, при които текстът е поставен за визуален ефект, а не в ред на четене, се конвертират зле, и никакво количество предположения не поправя това

PDF Library for Delphi диаграма на детерминирано DOCX опаковане, изградено запис по запис на чист Pascal, до Office автоматизация, чиито вградени метаданни сменят всяко преобразуване
Две идентични конвертации хешират идентично за разлики и кеширане — свойство, което Office автоматизацията губи в момента, в който вгради свежи времеви печати

Сканираните документи са напълно отделен случай. Страница, която е едно голямо изображение, не съдържа текстови обекти, така че няма какво да се изнесе, докато не съществува текстов слой; пътят на OCR, който го произвежда, е предпоставка, а не опция. Преди да пуснете голяма партида, вземете десетина представителни файла и разгледайте изхода, а помислете и за изброяване на елементите на страницата, както е описано в търсенето на текст и изброяването на елементи на страница, за да видите какво реално съдържат страниците

За асистентски и retrieval конвейери пътят през Markdown обикновено е по-добрата цел: заглавията стават граници на откъси, таблиците остават четими като таблици с вертикална черта, а маркерите за страници дават на всеки откъс цитируемо местоположение. За човешко редактиране DOCX е отговорът, защото онова, което потребителят иска, не е текстът, а възможността да го промени

PDF Library for Delphi е PDF библиотека за Delphi, C++Builder и Lazarus със съответстващи DLL и ActiveX интерфейси, така че същите извиквания за изнасяне са достъпни от C#, C++ или скриптови хостове. Пълната документация и пробна версия са на страницата на PDF Library for Delphi PDF библиотеката за Delphi