HotPDF renderuje HTML tabele kroz svoj HTML5 paged-media profil, koristeći pravu mrežu zauzeća za rowspan i colspan, izmerene visine redova umesto procena po broju znakova, i redove zaglavlja ponovljene na svakoj stranici nastavka. Dve situacije navode ga da odbije ponavljanje zaglavlja, i njihovo poznavanje unapred je jeftinije od debagovanja udvostrućene ćelije kasnije
Klasa dokumenta koji to nameće je ona koju svaki tim za izveštavanje eventualno isporuči: faktura ili izveštaj o usaglašenosti gde je izvor istine HTML, tabela se proteže preko četiri strane, i zaglavlje mora biti čitljivo na svakoj od njih. Išta manje od pravog rasporeda tabele proizvodi dva otkaza koje čitaoci odmah primeće: zaglavlje koje se pojavi jednom na prvoj strani i redovi čije su visine pogođene iz broja znakova
Zašto je mogućnost tabele prešla u HTML renderer?
Zato što alternativa gubi rich text, a rich text je razlog zašto je sadržaj uopšte HTML. Očigledan plan deluje kao ponovna upotreba: HotPDF već ima layout DOM objekat tabele sa pravom mrežom, pa mostom spojite HTML parser u njega i dobijete spaning džabe. Problem je u time čime taj objekat tabele crta. Njegove ćelije nose tekst i stil, a njegova putanja crtanja emituje čist tekstualni izlaz, pa sve što je HTML stvarno sadržao van fonta i boje — linkovi, superskripte, inline promene veličine, boja po segmentu — nestaje do trenutka kad stigne do strane
Smer koji preživi kontakt sa stvarnim dokumentima je obrnuti. Premestite mogućnosti table engine-a — mrežu zauzeća, pravo merenje, ponavljanje zaglavlja i teženje kolona — u HTML renderer, i ostavite rich-text renderovanje tamo gde već radi. To je veća izmena od mosta, i to je izmena koja hyperlink unutar ćelije tabele ostavlja hyperlinkom
Rowspan bez union-find strukture
Spaning ćelije prave atomske grupe redova, ali zatvaranje nad tim grupama ne treba opšta disjoint-set struktura, jer je zauzeće uvek kontinuiran interval. Ćelija sa rowspan="3" koja počinje u redu K zauzima redove K do K+2 i ništa drugo, pa se informacija o grupi svodi na oznaku kraja po redu
Algoritam su dve linije namere. Kad postavite spaning ćeliju koja počinje u K a završava u E, zapišite GroupEnd[K] := Max(GroupEnd[K], E). Zatim prođite redove jednom unazad i primenite G[R] := G[G[R]], što svaki kraj reda propagira unazad kroz preklapajuće spanove i daje tranzitivno zatvaranje u jednom prolazu. Ono što dobijete je, za svaki red, poslednji red koji mora ostati na istoj strani s njim, a to je upravo ono što korak paginacije treba da odluči gde sme da padne prelom
Raspodela visine je druga polovina. Kad spaning ćeliji treba više vertikalnog prostora nego što joj redovi koje pokriva trenutno daju, višak ide u poslednji red spana, ne raspoređuje se ravnomerno po njima. Obradite spaning ćelije posle nego što se obične visine redova izmire, pa dopunite završni red svakog spana. Ravnomerna raspodela viška deluje pravednije i proizvodi vidljivo pogrešan izlaz: redovi koji sadrže samo kratke jednoredne ćelije se naduju jer se neka nepovezana ćelija tri reda više slučajno pokazala visokom
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 uzima opcion author style sheet kao drugi argument, i tamo pripadaju print pravila. Držite screen style sheet van toga. Profil je verzioniran, i HTML5ProfileMilestones prijavljuje koje grupe mogućnosti trenutni build implementira: himParserCascade, himPagedLayout, himTablesForms i himBoundedResources, pa aplikacija može da propada namerno umesto da otkrije rupu u produkciji
Merenje mora da se slaže sa crtanjem, tačno
Visina reda je ispravna samo kad kod koji meri prelomljene linije prelama istim pravilom kao kod koji ih crta. Zvuči očigledno i jeste najčešći pojedinačni uzrok tabela čije se ivice ne poklapaju sa sadržajem. HotPDF meri gramzivim brojačem linija, i taj brojač mora da se poklopi sa semantikom preloma rich-text izlazne putanje u tri konkretna pogleda: prelomi samo na razmacima, nikada ne seče reč, i reč šira od kolone dobija liniju za sebe
Drugi zahtev je font. Merenje mora da ide sa fontom same ćelije, postavljenim kroz SetFont sa stvarnim imenom, stilskim skupom i veličinom pre poziva funkcije širine, a ne sa kojim god fontom bio slučajno aktivan. Bold tekst je redovno više od deset procenata širi od običnog pri istoj veličini, a to je dovoljno da ćelija od tri linije postane ćelija od četiri. Tabela gde su ćelije zaglavlja bold, a ćelije tela nisu, merena jednim fontom, biće pogrešna upravo u redovima koje čitaoci prvo pogledaju
Ispravno urađeno, ovo menja šta možete da asertujete u testu. Vidljivi efekat tačnog merenja je prored, a ne broj glifova: jednoredni red je visok oko 20 tačaka dok procena po broju znakova za isti sadržaj predviđa dve linije i otprilike 35. Asertujte vertikalno rastojanje između redova. I setite se da u PDF user space-u Y raste naviše, pa zaglavlje iznad reda tela znači da je Y vrednost zaglavlja veća, što je suprotno od onoga što instinkt ekranskih koordinata napiše
Kada HotPDF odbija da ponovi zaglavlje?
U dva slučaja, od kojih bi oba proizvela vidljivo pogrešan izlaz da je krenula dalje. Prvi je blok zaglavlja koji sadrži spaning ćeliju koja se proteže preko zaglavlja u redove tela. Ponavljanje zaglavlja nacrtalo bi taj sadržaj ćelije drugi put na poziciji gde više ne pripada, pa se zaglavlje crta jednom i tabela nastavlja bez njega. Drugi je zaglavlje više od 90 procenata upotrebljive visine strane, gde bi ponavljanje ostavilo skoro nikakav prostor za podatke i tabela ne bi napredovala
Oba odbijanja su namerna i tiha po dizajnu, jer je alternativa gora. Ako se vaše zaglavlje ne ponavlja a očekivali ste da hoće, proverite markup na rowspan koji prelazi thead granicu pre nego što posumnjate na engine. Taj jedan markup obrazac objašnjava većinu iznenađenja
// Težine kolona dolaze iz markup-a, pa je print style sheet mesto
// gde ih kontrolišete. Širine se tretiraju kao težine, ne kao pikseli
const
PrintStyleSheet =
'table { width: 100%; }' +
'thead th { font-weight: bold; background: #eee; }' +
'td.amount { text-align: right; }';
// Red zaglavlja koji nosi rowspan koji prelazi u telo potiskuje
// ponavljanje zaglavlja. Držite spanove unutar jedne sekcije:
// <thead><tr><th rowspan="2">Item</th>...</tr></thead> u redu
// <tr><th rowspan="3">Item</th>... prelazi u tbody, bez ponavljanja
Širine kolona ponašaju se kao težine, a ne kao apsolutna merenja, i to je ponašanje koje tabelu drži upotrebljivom kad sadržaj ne odgovara proceni autora. Kolona deklarisana na 30 procenata dobija otprilike 30 procenata dostupne širine, ali raspodela poštuje minimalnu širinu koju svaka kolona stvarno treba, pa uska kolona sa dugim nesecivim tokenom tiho ne preplavljuje kutiju tabele
Gde se ovo uklopi u pipeline za dokumente
Rad na tabelama sedi unutar šireg paged-media profila, i pravila paginacije, budžeti resursa i CSS rukovanje opisani u članku o HTML5 paged-media import putanji primenjuju se nepromenjeno na dokumente koji sadrže tabele. Ako vaši podaci ne počinju kao HTML, ruta direktne izgradnje iz članka o gradnji tabela pravo u PDF zaobilazi sloj raščlanjivanja u potpunosti i daje vam isto ponašanje mreže kroz API. A pošto visina reda na kraju zavisi od toga gde se linije lome, razmatranje merenja u tekstualnom poravnanju i prelamanju linija je pratilac za svakoga ko šteluje gust tabelarni izlaz
Ponovo upotrebljiva lekcija ovde uopšte nije o tabelama. Kad novi podsistem treba mogućnost koju stari podsistem već ima, pitajte koji od ta dva poseduje ono što je najteže ponovo implementirati. Aritmetika mreže je nekoliko desetina linija i lako se premesti. Rich-text renderovanje sa inline linkovima, superskriptama i stilizovanjem po segmentu nije, pa se mreža preselila a tekst ostao. HotPDF isporučuje obe putanje kao deo HotPDF Delphi PDF komponente, pa je izbor između HTML ulaza i direktne izgradnje odluka projekta, a ne biblioteke