PDF Library for Delphi upodobi HTML na stran PDF s pravo dvodimenzionalno postavitvijo: display: flex in display: grid sta izmerjena in postavljena namesto degradirana v zložene bloke, sprotne opombe pa so rezervirane na dnu okvirja, ki nosi njihov sklic, z oštevilčenjem, ki ostane neprekinjeno čez stolpce in strani. Vstopne točke so znane: DrawHTMLTextBox za en okvir in DrawHTMLStory za večstolpčen tok
To je pomembno, ker danes večina vsebine poročil prispe kot HTML. Predloge pišejo ljudje, ki pišejo CSS, nadzorne plošče so oblikovane kot kartice, upodabljalnik, ki tiho zloži vrstico flex v štiri zložene bloke, pa ustvari dokument, ki sploh ne spominja na oblikovno zasnovo. Dokler te zmožnosti ni bilo, je bila tabela edini dvodimenzionalni vsebnik, ki ga je mehanizem meril, zato je bilo treba vsako postavitev kartic ročno na novo napisati kot tabelo
Kaj se je spremenilo v modelu postavitve?
Prejšnja glavna zanka je vzdrževala en sam okvir vrstice in napredovala navzdol po strani. Ta model brezhibno obravnava vrstično vsebino in zložene bloke, ne more pa izraziti vsebnika, katerega podrejeni elementi so dimenzionirani drug glede na drugega. Edina izjema so bile tabele s svojim lastnim dvoprehodnim merjenjem
Flex in grid vsak dodata omejen prehod merjenja čez podrejene elemente vsebnika, pomembna beseda pa je omejen. Vsebnik flex izmeri do 256 neposrednih podrejenih elementov v fiksno polje. Grid za deterministično samodejno postavitev uporablja matriko zasedenosti največ 64 krat 64 celic. Te zgornje meje obstajajo zato, da sovražna ali generirana slogovna datoteka ne more sprožiti neomejene rekurzije ali kvadratnega pomnilnika postavitve, kar je resnična skrb, kadar HTML prihaja iz predloge, ki jo ureja stranka
Kako elementi flex dobijo svoje velikosti
V smeri vrstice vsebnik sešteje osnovo vsakega elementa skupaj z njegovimi utežmi rasti in krčenja, nato pa razdeli preostali prostor, pozitiven ali negativen, glede na te uteži. Pri flex-wrap je vsaka vrstica rešena neodvisno, tako da vrsta, ki se prelomi v dve vrstici, prosti prostor dodeli po vrstici in ne čez celoten vsebnik. V smeri stolpca teče ista porazdelitev po glavni osi bodisi glede na izrecno višino bodisi glede na višino vsebine
justify-content, align-items, gap in obrnjene smeri delujejo na geometriji, ki je že izmerjena. Premikajo okvirje; nikoli ne sprožijo ponovnega merjenja vsebine elementa. Prav ta ločitev preprečuje, da bi zapletena nadzorna plošča svoje podrejene elemente izmerila večkrat
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;
Povratna vrednost je niz nadaljevanja, s katerim vsaka vstopna točka za risanje HTML javi, kaj se ni prilegalo. Predajte ga naslednjemu okvirju ali naslednji strani, tok pa se nadaljuje tam, kjer se je ustavil
Postavitev grid, in kaj je lahko sled
Sledi grid sprejemajo fiksne dolžine, odstotke, enoto fr, preproste izraze repeat() in minmax(). Samodejna postavitev deterministično zapolni matriko zasedenosti, tako da isti HTML vedno da isto razporeditev. Izrecne koordinate se lahko prekrivajo, kar je namerno: oblikovna zasnova, ki nasloni značko nad kartico, izraža namero in ne napake. Kadar je izrecno podana samo ena os, postavitev preišče samo drugo os
Elementi, ki se raztezajo čez več vrstic, svojo izmerjeno višino prispevajo nazaj vrsticam, ki jih pokrivajo, povprečeno čez njih, kar prepreči, da bi visok razpenjajoč se element stisnil eno samo vrstico, medtem ko bi njegovi sosedje ostali nizki:
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);
Podrejeni elementi flex in grid so upodobljeni prek istega upodabljalnika HTML kot vse drugo, kar je lastnost, zaradi katere je funkcija uporabna in ne ločen svet. Pisave, kaskada CSS, povezave, slike, tabele in nadalje gnezdeni vsebniki flex ali grid se znotraj elementa flex vsi obnašajo natanko tako, kot na najvišji ravni, zunanji načrt postavitve pa zabeleži končne ukaze za besedilo in pravokotnike, tako da ponovno risanje ponovno uporabi obstoječi predpomnilnik meritev
Zakaj so sprotne opombe problem paginacije?
Sprotna opomba ni vsebina, ki steče za odstavkom, ki vsebuje njen sklic; je vsebina, ki se mora pojaviti na dnu istega okvirja kot njen sklic. To obrne običajen vrstni red merjenja, ker je prostor, na voljo za besedilo telesa, zdaj odvisen od vsebine, ki še ni postavljena
Upodabljalnik zato opombo izmeri, ko naleti na sklic, in odšteje območje opombe od proračuna višine telesa trenutnega omejenega okvirja. Če se sklic, dosedanje besedilo telesa in opomba vsi skupaj ne prilegajo, se oznaka sprotne opombe in vse za njo skupaj premakneta v niz nadaljevanja. To pravilo prepreči dva klasična neuspeha: opombo, ki se natisne čez besedilo telesa, in opombo, obtičano na strani, katere sklic je na prejšnji
V omejenem okvirju je območje opombe pripeto na dno, nad njim pa je ločilna črta. Pri neomejenem merjenju, kjer ni višine okvirja, na katero bi se pripelo, območje opombe sledi neposredno za telesom. Oštevilčenje je preneseno v razširitvenem polju na skladu nadaljevanja, tako da DrawHTMLTextBox in DrawHTMLStory zaporedje ohranjata tekoče čez stolpce in strani, niz nadaljevanja, ustvarjen pred obstojem tega polja, pa se še vedno pravilno nadaljuje
// Sprotne opombe znotraj večstolpčne zgodbe ohranjajo eno tekoče zaporedje
Html := LoadTemplate('chapter.html'); // uporablja oznake float:footnote
Remainder := Lib.DrawHTMLStory(40, 40, 515, 700,
2, // stolpci
16, // zamik med stolpci v pikah
20, // največje število strani za to zgodbo
Html);
if Remainder <> '' then
Log('story exceeded its page budget');
Praktična navodila za avtorje predlog
Oblikujte znotraj dokumentiranih zgornjih mej. Vsebnik flex z več kot 256 neposrednimi podrejenimi elementi je skoraj vedno podatkovna tabela v kostumu flex, pot tabele pa jo tako ali tako izmeri bolje. Grid, večji od 64 krat 64, je preglednica, in enak nasvet velja tudi zanj. Za večstolpčno besedilo telesa vedenje stolpcev in deljenja besed, opisano v deljenju besed in uravnoteženih besedilnih stolpcih, ureja, kako je videti tok znotraj vsakega stolpca
Kadar se mora postavitev prilegati, merite, preden rišete. GetHTMLTextHeight javi višino, ki bi jo potrebovala dana širina, kar je poceni način, da se odločite med eno in drugo postavitvijo, preden zapravite črnilo. In neprazen niz nadaljevanja obravnavajte kot normalnega in ne izjemnega: to je mehanizem, s katerim se dolga vsebina paginira, ne signal napake
Kadar HTML prihaja iz generatorja poročil in ne iz ročno pisanih predlog, se s tem dobro poveže pot, vodena s podatkovnim naborom, v generatorju poročil iz podatkovnega nabora, ki ustvari oznako, ki jo nato uredita flex in grid. Kadar pa mora ista vsebina spet zapustiti PDF, pot semantičnega izvoza v izvozu PDF v Markdown in DOCX sklene krog
Postavitev HTML, generiranje poročil in semantični izvoz so del ene knjižnice za Delphi, C++Builder in Free Pascal; celoten seznam funkcij je na strani PDF Library for Delphi