Technický článek

PDF do Markdown a DOCX v Delphi pomocí PDFlibPas

PDFlibPas převádí obsah PDF do dvou upravitelných formátů bez automatizace Office. Metody ExportPageMarkdown a ExportDocumentMarkdown vrací sémantický Markdown s odvozenými nadpisy, číslovanými i nečíslovanými seznamy a tabulkami se svislítky, zatímco SaveDOCXToFile a SaveDOCXToStream zapisují balíček WordprocessingML obsahující odstavce, nadpisy, nativní číslování seznamů, rozpoznané tabulky, formátování písma, zalomení stránek a umístěné obrázky PNG

Obojí běží celé v Pascalu, na serveru, bez nainstalovaného Wordu a bez COM. Právě toto omezení je důvodem, proč tato funkce existuje v knihovně pro PDF, a ne v desktopovém nástroji

Proč je „PDF do Wordu" opravdu obtížné?

Protože stránka PDF neobsahuje odstavce. Obsahuje operátory pro zobrazení textu, které umísťují běhy glyfů na souřadnice, v pořadí, v jakém je producent vygeneroval, bez jakékoli povinnosti naznačit, že dva běhy patří do stejné věty, natož do stejné položky seznamu. Formát byl navržen tak, aby přesně popisoval vytištěnou stránku, a v tom uspívá právě tím, že zahazuje strukturu, ze které stránka vznikla

Každý konvertor tedy musí zpětně sestavit to, co generátor zahodil. Seskupení do řádků vychází ze svislých mezer a zarovnání na účaří. Hranice odstavců vychází ze změn mezer a odsazení. Nadpis je řádek, jehož písmo je větší nebo tučnější než tělo textu a který se od následujícího textu odděluje. Seznam je řada odstavců začínajících odrážkou nebo číselným vzorem. Tabulka je mřížka textových bloků, jejichž okraje se zarovnávají napříč řádky a sloupci. Každá z těchto věcí je odvození, a odvození znamená dobrý výsledek u dokumentů, které dodržují běžné typografické konvence, a průměrný u dokumentů, které je nedodržují

Značkovaná PDF jsou výjimkou, a to velkou. Když dokument nese strom struktury, role odstavce, nadpisu, seznamu i tabulky se zaznamenávají, ne odhadují, a proto se práce na přístupnosti popsaná v článku o struktuře přístupnosti značkovaného PDF vyplácí i v kvalitě konverze. Pokud máte kontrolu nad producentem, značkování výstupu je jediná věc s nejvyšší pákou, kterou můžete udělat pro každého, kdo bude výstup později konvertovat

Export do Markdown po jedné stránce

Cesta přes Markdown je ta, po které sáhnete, když je cílem textový pipeline: dokumentační web, vyhledávací index, korpus pro vyhledávání u asistenta. Volby jsou bitová maska: PDF_MARKDOWN_INCLUDE_PAGE_MARKERS, PDF_MARKDOWN_DETECT_HEADINGS, PDF_MARKDOWN_PRESERVE_STYLES, přičemž PDF_MARKDOWN_DEFAULT kombinuje všechny tři

var
  Pdf: TPDFlib;
  Md: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.LoadFromFile('handbook.pdf', '');

    // jedna stránka jako řetězec
    Md := Pdf.ExportPageMarkdown(1, PDF_MARKDOWN_DEFAULT);

    // rozsah stránek, streamovaný na disk jako UTF-8 bez BOM
    Pdf.SaveMarkdownToFile('1-40',
      PDF_MARKDOWN_DETECT_HEADINGS or PDF_MARKDOWN_PRESERVE_STYLES,
      'handbook.md');
  finally
    Pdf.Free;
  end;
end;

Značky stránek se vyplatí zejména při vyhledávání. Kus textu, který u sebe nese stránku, ze které pochází, lze přesně citovat, a čtenář, který citaci sleduje, přistane přesně tam, kde se dané tvrzení skutečně nachází. Vypněte je, když je Markdown určený k lidskému čtení, kde jsou hranice stránek z původního rozvržení jen šumem

Streamovací vstupní body mají význam u rozsáhlých dokumentů. SaveMarkdownToStream a SaveMarkdownToFile zapisují UTF-8 po jedné stránce a neukládají celý výstup do bufferu, takže z 900stránkové příručky se v paměti nejdřív nestane 900stránkový řetězec. I absence značky pořadí bajtů je záměrná: BOM u souboru Markdown mate překvapivé množství generátorů statických webů i nástrojů pro diff

DOCX bez Office na stroji

Zapisovač DOCX vytváří balíček sám: položky ZIP zapsané jako čistý Deflate s kontrolou CRC, části WordprocessingML a vztahy, které je svazují. Nikam do Wordu se nevolá, což znamená, že konverze běží na headless serveru, pod servisním účtem, v kontejneru – všude tam, kde je automatizace Office buď nelicencovaná, nestabilní, nebo přímo zakázaná

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;

Obrazová data se zapisují v okamžiku, kdy se zpracovává příslušná stránka, ne až se posbírají a připojí na konec, takže špičková spotřeba paměti odpovídá jedné stránce, ne celému dokumentu. Explicitní pořadí stránek se zachovává a vybraná stránka PDF se poté obnoví, což má význam ve chvíli, kdy je export jen jedním krokem v delší úloze, která měla stránku vybranou z jiných důvodů

Co vám přinese deterministické balení?

Bajtově přesnou reprodukovatelnost. Dvě konverze stejného vstupu se stejnými volbami vyprodukují stejný balíček, což znamená, že výstup můžete hashovat pro detekci změn, porovnávat dvě sestavení vygenerovaného dokumentu a agresivně cachovat, aniž byste se museli obávat, že identický vstup vyprodukoval jiný artefakt

Automatizace Office tohle slíbit nemůže. Vkládá časová razítka, identifikátory revizí a metadata závislá na konkrétním stroji, takže stejný dokument převedený dvakrát se liší způsobem, který znemožňuje hashování. Stejná úvaha stojí i za deterministickými identifikátory souborů popsanými v článku o deterministických PDF ID pro reprodukovatelná sestavení: když je výstup reprodukovatelný, ověření se stává porovnáním místo inspekce

Kde je výstup dobrý a kde ne

V tomto ohledu buďte k uživatelům upřímní, protože kvalita konverze závisí víc na vstupu než na konvertoru. Značkovaná PDF a čistě vygenerované obchodní dokumenty – faktury, reporty, smlouvy – se převádí dobře: nadpisy zůstávají nadpisy, tabulky přežijí, seznamy se ve Wordu správně přečíslují. Dvousloupcová akademická rozvržení se převádí přijatelně, pokud je geometrie sloupců pravidelná. Tabulky přesahující přes zlom stránky se skládají zpět pomocí odvození a někdy se rozdělí. Silně designovaný marketingový materiál, kde je text umístěn kvůli vizuálnímu efektu, a ne v pořadí čtení, se převádí špatně, a žádné množství odvození to nespraví

Skenované dokumenty jsou úplně samostatný případ. Stránka, která je jeden velký obrázek, neobsahuje žádné textové objekty, takže není co exportovat, dokud neexistuje textová vrstva; cesta přes OCR, která ji vytvoří, je předpoklad, ne volitelná možnost. Než spustíte velkou dávku, vezměte si tucet reprezentativních souborů a podívejte se na výstup, a zvažte nejdřív výčet prvků stránky, jak popisuje článek o fulltextovém vyhledávání a výčtu prvků stránky, abyste viděli, co stránky vlastně obsahují

Pro pipeline asistentů a vyhledávání je cesta přes Markdown obvykle lepším cílem: nadpisy se stávají hranicemi bloků, tabulky zůstávají čitelné jako tabulky se svislítky a značky stránek dávají každému bloku citovatelné umístění. Pro lidskou úpravu je odpovědí DOCX, protože to, co uživatel chce, není text samotný, ale možnost ho měnit

PDFlibPas je knihovna pro práci s PDF pro Delphi, C++Builder a Lazarus s odpovídajícím rozhraním DLL a ActiveX, takže stejná exportní volání jsou dostupná i z C#, C++ nebo skriptovacích hostitelů. Úplná dokumentace i zkušební verze jsou na stránce PDFlibPas Delphi PDF library