HotPDF vykresľuje HTML tabuľky cez svoj HTML5 paged-media profil s použitím skutočnej mriežky obsadenia pre rowspan a colspan, nameraných výšok riadkov namiesto odhadov z počtu znakov a hlavičkových riadkov opakovaných na každej pokračovacej strane. Dve situácie ho prinútia odmietnuť opakovanie hlavičky a poznať ich dopredu je lacnejšie než neskôr ladiť zdvojenú bunku
Trieda dokumentov, ktorá to vynúti, je tá, ktorú nakoniec vydá každý reportingový tím: faktúra alebo súladová správa, kde zdrojom pravdy je HTML, tabuľka beží cez štyri strany a hlavička musí byť čitateľná na každej z nich. Čokoľvek menej než skutočný tabuľkový layout vyprodukuje dve zlyhania, ktoré čitatelia všimnú okamžite: hlavičku, ktorá sa objaví raz na prvej strane, a riadky, ktorých výšky boli uhádnuté z počtov znakov
Prečo sa schopnosť tabuliek presunula do HTML renderera?
Pretože alternatíva stráca rich text a rich text je presne ten dôvod, prečo je obsah vôbec HTML. Zjavný plán vyzerá ako recyklácia: HotPDF už má tabuľkový objekt layout DOMu s poriadnou mriežkou, takže stačí premostiť HTML parser do neho a spanning dostanete zadarmo. Problém je v tom, čím ten tabuľkový objekt kreslí. Jeho bunky nesú text a štýl a jeho kresliaca cesta vydáva čistý textový výstup, takže všetko, čo HTML reálne obsahovalo nad rámec fontu a farby, odkazy, horné indexy, zmeny veľkosti v riadku, farbu po častiach, je preč, kým sa to dostane na stranu
Smer, ktorý prežije kontakt so skutočnými dokumentmi, je opačný. Premiestnite schopnosti tabuľkového enginu, mriežku obsadenia, skutočné meranie, opakovanie hlavičiek a váhovanie stĺpcov, do HTML renderera a nechajte vykresľovanie rich textu tam, kde už funguje. To je väčšia zmena než most a je to zmena, vďaka ktorej hyperlink vo vnútri tabuľkovej bunky zostáva hyperlinkom
Rowspan bez union-find
Spanujúce bunky tvoria atomické skupiny riadkov, ale uzáver nad týmito skupinami nepotrebuje všeobecnú štruktúru disjunktných množín, pretože obsadenie je vždy súvislý interval. Bunka s rowspan="3" začínajúca na riadku K obsadzuje riadky K až K+2 a nič iné, takže informácia o skupine sa redukuje na koncovú značku pre každý riadok
Algoritmus sú dva riadky úmyslu. Keď umiestnite spanujúcu bunku, ktorá začína na K a končí na E, zaznamenajte GroupEnd[K] := Max(GroupEnd[K], E). Potom prejdite riadky raz odzadu a aplikujte G[R] := G[G[R]], čo propaguje každý koniec riadku dozadu cez prekrývajúce sa spanu a dáva tranzitívny uzáver v jednom prechode. Výsledkom je, že pre každý riadok poznáte posledný riadok, ktorý musí zostať na tej istej strane ako on, a presne to potrebuje krok stránkovania na rozhodnutie, kde môže padnúť zlom
Rozdeľovanie výšky je druhá polovica. Keď spanujúca bunka potrebuje viac zvislého priestoru, než jej riadky aktuálne dávajú, prebytok ide do posledného riadku spanu, nerozdeľuje sa rovnomerne. Spracujte spanujúce bunky až po tom, čo sa urobia obyčajné výšky riadkov, a potom doplňte posledný riadok každého spanu. Rovnomerné rozdelenie prebytku sa javí spravodlivejšie a produkuje viditeľne zlý výstup: riadky obsahujúce len krátke jedoriadkové bunky nafúknu, pretože nejaká nesúvisiaca bunka tri riadky vyššie bola náhodou vysoká
var
Pdf: THotPDF;
Importer: THPDFHTMLImporter;
Stats: THPDFHTMLImportStatistics;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'audit-report.pdf';
Pdf.BeginDoc;
Importer := THPDFHTMLImporter.Create(Pdf);
try
Importer.Margin := 48;
Importer.BaseFontName := 'Arial';
Importer.BaseFontSize := 10;
Importer.MaxDOMNodes := 200000;
Importer.MaxLayoutOperations := 2000000;
if Importer.RenderHTML5(SourceHtml, PrintStyleSheet) then
begin
Stats := Importer.Statistics;
Writeln('tables ', Stats.TableCount,
' page breaks ', Stats.PageBreakCount);
end;
finally
Importer.Free;
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
RenderHTML5 berie voliteľný autorový style sheet ako druhý argument a presne tam patria pravidlá pre tlač. Screen style sheet nechajte mimo. Profil je verzovaný a HTML5ProfileMilestones hlási, ktoré skupiny schopností aktuálny build implementuje: himParserCascade, himPagedLayout, himTablesForms a himBoundedResources, takže aplikácia môže degradovať zámerne namiesto toho, aby diery objavila vo výrobe
Meranie sa musí presne zhodovať s kreslením
Výška riadku je správna len vtedy, keď kód merajúci zalamované riadky zalamuje podľa toho istého pravidla ako kód, ktorý ich kreslí. Znie to zjavne a je to jediný najčastejší zdroj tabuliek, ktorých okraje nelignujú s obsahom. HotPDF meria chamtivým počítadlom riadkov a toto počítadlo sa musí zhodovať so sémantikou zalamovania rich-text výstupnej cesty v troch konkrétnych ohľadoch: láme len na medzerách, nikdy nerozdelí slovo a slovo širšie než stĺpec dostane vlastný riadok
Druhou požiadavkou je font. Meranie musí bežať s vlastným fontom bunky, nastaveným cez SetFont so skutočným menom, sadou štýlov a veľkosťou pred volaním šírkovej funkcie, nie s tým fontom, ktorý sa práve náhodou aktivoval. Tučný text býva pri rovnakej veľkosti pravidelne viac než desať percent širší než obyčajný, čo stačí na to, aby trojriadková bunka bola štvoriadková. Tabuľka, kde sú hlavičkové bunky tučné a telové nie, meraná jedným fontom, bude zlá presne v riadkoch, na ktoré sa čitatelia pozerajú prvé
Urobiť to správne mení to, čo môžete v teste tvrdiť. Pozorovateľný efekt presného merania je riadkovanie, nie počty glyfov: jedoriadkový riadok je vysoký približne 20 bodov, kým odhad z počtu znakov toho istého obsahu predpovedá dva riadky a zhruba 35. Tvrďte zvislú vzdialenosť medzi riadkami. A pamätajte, že PDF user space má Y rastúce nahor, takže hlavička sediaca nad telovým riadkom znamená, že Y hodnota hlavičky je tá väčšia, čo je opak toho, čo napíše inštinkt z obrazovkových súradníc
Kedy HotPDF odmietne hlavičku opakovať?
V dvoch prípadoch, z ktorých oba by pri pokračovaní vyprodukovali viditeľne zlý výstup. Prvým je blok hlavičky obsahujúci spanujúcu bunku, ktorá presahuje za hlavičku do telových riadkov. Opakovanie hlavičky by nakreslilo obsah tej bunky druhýkrát na mieste, kam už nepatrí, takže hlavička sa nakreslí raz a tabuľka pokračuje bez nej. Druhým je hlavička vyššia než 90 percent použiteľnej výšky strany, kde by opakovanie nechalo takmer žiadny priestor pre dáta a tabuľka by nepostúpila
Oba odmietnutia sú zámerné a podľa návrhu tiché, pretože alternatíva je horšia. Ak sa vaša hlavička neopakuje, hoci ste čakali, že sa bude, skontrolujte v markupu rowspan prekračujúci hranicu thead, skôr než začnete podozrievať engine. Tento jediný markup vzor vysvetľuje väčšinu prekvapení
// Váhy stĺpcov pochádzajú z markupu, takže print style sheet je miesto,
// kde sa dajú riadiť. Šírky sa berú ako váhy, nie ako pixely
const
PrintStyleSheet =
'table { width: 100%; }' +
'thead th { font-weight: bold; background: #eee; }' +
'td.amount { text-align: right; }';
// Hlavičkový riadok nesúci rowspan prekračujúci do tela potlačí
// opakovanie hlavičky. Držte spanu v jednej sekcii:
// <thead><tr><th rowspan="2">Item</th>...</tr></thead> ok
// <tr><th rowspan="3">Item</th>... span do tbody, bez opakovania
Šírky stĺpcov sa správajú ako váhy, nie ako absolútné merania, a to je správanie, ktoré udrží tabuľku použiteľnú, keď obsah nesedí s odhadom autora. Stĺpec deklarovaný na 30 percent dostane zhruba 30 percent dostupnej šírky, ale rozdeľovanie rešpektuje minimálnu šírku, ktorú každý stĺpec skutočne potrebuje, takže úzky stĺpec držiaci dlhý nezalomiteľný token nepretečie poticho tabuľkový box
Kam sa to celé hodí do dokumentového pipeline
Tabuľková práca sedí vo vnútri širšieho paged-media profilu a pravidlá stránkovania, rozpočty resources a spracovanie CSS popísané v článku importná cesta HTML5 paged-media platia nezmenene aj pre dokumenty obsahujúce tabuľky. Ak vaše dáta nezačínajú ako HTML, cesta priamej konštrukcie v článku tabuľky stavite priamo do PDF obíde parsing vrstvu celkom a dá vám rovnaké mriežkové správanie cez API. A keďže výška riadku v konečnom dôsledku závisí od toho, kde sa riadky lámú, diskusia o meraní v článku zarovnávanie textu a lámanie riadkov je sprievodný kúsok pre každého, kto ladí hustý tabuľkový výstup
Znovu použiteľné ponaučenie odtiaľ sa vôbec netýka tabuliek. Keď nový subsystém potrebuje schopnosť, ktorú starý subsystém už má, spýtajte sa, ktorý z dvoch vlastní vec, ktorá sa najťažšie reimplementuje. Mriežková aritmetika je pár desiatok riadkov a presťahuje sa ľahko. Vykresľovanie rich textu s inline odkazmi, hornými indexmi a štýlovaním po častiach nie, takže sa presťahovala mriežka a text zostal. HotPDF dodáva obe cesty ako súčasť HotPDF Delphi PDF komponentu, takže voľba medzi HTML vstupom a priamou konštrukciou je rozhodnutie projektu, nie knižnice