PDF Library for Delphi vykresľuje HTML do stránky PDF so skutočným dvojrozmerným rozložením: display: flex a display: grid sa merajú a umiestňujú namiesto toho, aby sa degradovali na naskladané bloky, a poznámky pod čiarou sa rezervujú v dolnej časti rámca, ktorý nesie ich odkaz, s číslovaním, ktoré zostáva priebežné naprieč stĺpcami a stránkami. Vstupné body sú tie známe, DrawHTMLTextBox pre jeden rámec a DrawHTMLStory pre viacstĺpcový tok
Na tomto záleží, pretože HTML je dnes spôsob, akým prichádza väčšina obsahu reportov. Šablóny píšu ľudia, ktorí píšu CSS, dashboardy sa navrhujú ako karty, a renderer, ktorý ticho zrúti riadok flex na štyri naskladané bloky, produkuje dokument, ktorý sa vôbec nepodobá dizajnu. Kým táto schopnosť neexistovala, jediným dvojrozmerným kontajnerom, ktorý engine meral, bola tabuľka, takže každé rozloženie kariet sa muselo ručne prepísať ako tabuľka
Čo sa zmenilo v modeli rozloženia?
Predchádzajúca hlavná slučka udržiavala jeden riadkový rámec a postupovala nadol po stránke. Tento model dokonale spracúva inline obsah a naskladané bloky, no nedokáže vyjadriť kontajner, ktorého deti sú rozmerované vo vzájomnom vzťahu. Jedinou výnimkou boli tabuľky, s vlastným dvojpriechodovým meraním
Flex aj grid pridávajú ohraničený priechod merania nad deťmi kontajnera, a dôležité slovo je ohraničený. Kontajner flex meria až 256 priamych detí do pevného poľa. Grid používa maticu obsadenosti s maximálne 64 krát 64 bunkami pre deterministické automatické umiestnenie. Tieto stropy existujú, aby nepriateľský alebo vygenerovaný štýlopis nemohol vyvolať neobmedzenú rekurziu alebo kvadratickú pamäť umiestnenia, čo je reálna obava, keď HTML pochádza zo šablóny, ktorú upravuje zákazník
Ako flex položky dostávajú svoje rozmery
V smere riadku kontajner sčíta základ každej položky spolu s jej váhami rastu a zmenšenia, potom rozdelí zostávajúci priestor, kladný alebo záporný, podľa týchto váh. S flex-wrap sa každý riadok rieši nezávisle, takže riadok, ktorý sa zalomí na dva riadky, prideľuje voľný priestor na riadok, nie naprieč celým kontajnerom. V smere stĺpca beží rovnaká distribúcia po hlavnej osi buď voči explicitnej výške, alebo voči výške obsahu
justify-content, align-items, gap a obrátené smery pracujú s geometriou, ktorá už bola zmeraná. Presúvajú rámce; nikdy nespúšťajú opätovné meranie obsahu položky. Práve toto oddelenie zabraňuje tomu, aby zložitý dashboard meral svoje deti niekoľkokrát
uses
PDFlibrary;
var
Lib: TPDFlib;
Html, Remainder: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.SetPageSize('A4');
Lib.NewPage;
Html :=
'<div style="display:flex; gap:12px;">' +
' <div style="flex:2 1 0; background:#f4f6f8; padding:8px;">' +
' <b>Revenue</b><br/>EUR 4,182,300</div>' +
' <div style="flex:1 1 0; background:#f4f6f8; padding:8px;">' +
' <b>Margin</b><br/>18.4%</div>' +
' <div style="flex:1 1 0; background:#f4f6f8; padding:8px;">' +
' <b>Backlog</b><br/>92 days</div>' +
'</div>';
Remainder := Lib.DrawHTMLTextBox(40, 40, 515, 120, Html);
if Remainder <> '' then
Log('content did not fit - carry the remainder to the next box');
Lib.SaveToFile('dashboard.pdf');
finally
Lib.Free;
end;
end;
Návratová hodnota je reťazec pokračovania, ktorým každý vstupný bod kreslenia HTML hlási, čo sa nezmestilo. Odovzdajte ho ďalšiemu rámcu alebo ďalšej stránke a tok pokračuje tam, kde sa zastavil
Umiestnenie v gride, a čím môže byť trať
Trate gridu prijímajú pevné dĺžky, percentá, jednotku fr, jednoduché výrazy repeat() a minmax(). Automatické umiestnenie deterministicky vyplní maticu obsadenosti, takže rovnaké HTML vždy produkuje rovnaké usporiadanie. Explicitné súradnice sa smú prekrývať, a je to zámerné: dizajn, ktorý vrství odznak nad kartu, vyjadruje zámer, nie chybu. Keď je explicitne zadaná iba jedna os, umiestnenie prehľadáva iba druhú os
Položky, ktoré sa rozpínajú cez viacero riadkov, prispievajú svojou nameranou výškou späť do riadkov, ktoré pokrývajú, spriemerovanou naprieč nimi, čo zabraňuje tomu, aby vysoká rozpínajúca sa položka stlačila jeden riadok, zatiaľ čo jej susedia zostanú nízki:
Html :=
'<div style="display:grid; grid-template-columns:repeat(3, 1fr); ' +
' gap:10px;">' +
' <div style="grid-row:span 2; background:#eef;">Site plan</div>' +
' <div>Inspector</div>' +
' <div>Date</div>' +
' <div style="grid-column:2 / span 2;">Findings summary</div>' +
'</div>';
Remainder := Lib.DrawHTMLTextBox(40, 180, 515, 260, Html);
Deti flex a grid sa vykresľujú cez ten istý renderer HTML ako všetko ostatné, a práve táto vlastnosť robí funkciu použiteľnou namiesto samostatného sveta. Písma, kaskáda CSS, odkazy, obrázky, tabuľky a ďalšie vnorené kontajnery flex alebo grid sa vo vnútri flex položky správajú presne tak, ako na najvyššej úrovni, a vonkajší plán rozloženia zaznamenáva finálne príkazy textu a obdĺžnikov, takže opakované kreslenie znovu využíva existujúcu cache merania
Prečo sú poznámky pod čiarou problémom stránkovania?
Poznámka pod čiarou nie je obsah, ktorý plynie za odsekom obsahujúcim jej odkaz; je to obsah, ktorý sa musí objaviť v dolnej časti toho istého rámca ako jej odkaz. To obracia zvyčajné poradie merania, pretože priestor dostupný pre telo textu teraz závisí od obsahu, ktorý ešte nebol rozvrhnutý
Renderer preto zmeria poznámku, keď narazí na odkaz, a odpočíta plochu poznámky z rozpočtu výšky tela aktuálneho ohraničeného rámca. Ak sa odkaz, doterajší text tela a poznámka spolu nezmestia, značka poznámky pod čiarou a všetko za ňou sa spoločne presunú do reťazca pokračovania. Práve toto pravidlo zabraňuje dvom klasickým zlyhaniam: poznámke prekrývajúcej text tela a poznámke uviaznutej na stránke, ktorej odkaz je na predchádzajúcej
V ohraničenom rámci je plocha poznámky pripevnená k spodku s oddeľovacou čiarou nad ňou. Pri neohraničenom meraní, kde nie je žiadna výška rámca, ku ktorej by sa dalo pripevniť, plocha poznámky nasleduje bezprostredne za telom. Číslovanie sa nesie v rozširujúcom poli na zásobníku pokračovania, takže DrawHTMLTextBox a DrawHTMLStory udržujú sekvenciu bežiacu naprieč stĺpcami a stránkami, a reťazec pokračovania vytvorený pred existenciou tohto poľa sa aj tak správne obnoví
// Poznámky pod čiarou vo vnútri viacstĺpcového príbehu udržujú jednu priebežnú sekvenciu
Html := LoadTemplate('chapter.html'); // používa značky float:footnote
Remainder := Lib.DrawHTMLStory(40, 40, 515, 700,
2, // stĺpce
16, // medzera medzi stĺpcami v bodoch
20, // maximálny počet stránok pre tento príbeh
Html);
if Remainder <> '' then
Log('story exceeded its page budget');
Praktické odporúčania pre autorov šablón
Navrhujte v rámci zdokumentovaných stropov. Kontajner flex s viac než 256 priamymi deťmi je takmer vždy dátová tabuľka oblečená do kostýmu flex, a cesta tabuľky ju aj tak zmeria lepšie. Grid väčší než 64 krát 64 je tabuľkový hárok a platí rovnaká rada. Pre viacstĺpcový text tela riadi to, ako tok vyzerá vo vnútri každého stĺpca, správanie stĺpcov a delenia slov opísané v časti delenie slov a vyvážené textové stĺpce
Merajte pred kreslením, keď sa rozloženie musí zmestiť. GetHTMLTextHeight hlási výšku, akú by daná šírka potrebovala, čo je lacný spôsob, ako sa rozhodnúť medzi jedným a druhým rozložením ešte pred nanesením atramentu. A neprázdny reťazec pokračovania berte ako bežný stav, nie ako výnimočný: je to mechanizmus, ktorým sa dlhý obsah stránkuje, nie signál chyby
Tam, kde HTML pochádza z generátora reportov namiesto z ručne písaných šablón, sa s týmto dobre dopĺňa cesta riadená dátovou sadou v časti generátor reportov nad dátovou sadou, ktorá generuje markup, ktorý potom flex a grid usporiadajú. A keď ten istý obsah musí PDF aj znovu opustiť, cestu spätného prevodu uzatvára sémantická exportná cesta v časti export PDF do Markdownu a DOCX
Rozloženie HTML, generovanie reportov a sémantický export sú súčasťou jednej knižnice pre Delphi, C++Builder a Free Pascal; kompletný zoznam funkcií nájdete na stránke PDF Library for Delphi