PDF Library for Delphi renderira HTML u PDF stranicu s pravim dvodimenzionalnim rasporedom: display: flex i display: grid se mjere i postavljaju umjesto da se degradiraju u naslagane blokove, a fusnote se rezerviraju na dnu okvira koji nosi njihovu referencu, uz numeriranje koje ostaje neprekidno preko stupaca i stranica. Ulazne točke su poznate: DrawHTMLTextBox za jedan okvir i DrawHTMLStory za višestupčani tok
Ovo je važno jer HTML danas predstavlja način na koji stiže većina sadržaja izvještaja. Predloške pišu ljudi koji pišu CSS, nadzorne ploče dizajniraju se kao kartice, a renderer koji tiho sruši flex redak u četiri naslagana bloka proizvodi dokument koji uopće ne nalikuje dizajnu. Prije nego što je ova mogućnost postojala, jedini dvodimenzionalni kontejner koji je mehanizam mjerio bila je tablica, pa se svaki raspored kartica morao ručno ponovno napisati kao tablica
Što se promijenilo u modelu rasporeda?
Prethodna glavna petlja održavala je jedan okvir retka i napredovala niz stranicu. Taj model savršeno obrađuje ugrađeni sadržaj i naslagane blokove, a ne može izraziti kontejner čija se djeca dimenzioniraju u odnosu jedno na drugo. Tablice su bile jedina iznimka, sa svojim vlastitim mjerenjem u dva prolaza
Flex i grid svaki dodaju ograničen prolaz mjerenja nad djecom kontejnera, a važna riječ je ograničen. Flex kontejner mjeri do 256 izravne djece u fiksno polje. Grid koristi matricu zauzeća od najviše 64 puta 64 ćelije za deterministično automatsko postavljanje. Ti gornji limiti postoje kako zlonamjeran ili generiran stylesheet ne bi mogao pokrenuti neograničenu rekurziju ili kvadratnu memoriju postavljanja, što je stvarna briga kada HTML dolazi iz predloška koji kupac uređuje
Kako flex stavke dobivaju svoje veličine
U smjeru retka, kontejner zbraja osnovu (basis) svake stavke zajedno s njezinim težinama rasta i skupljanja, zatim raspodjeljuje preostali prostor, pozitivan ili negativan, prema tim težinama. S flex-wrap, svaki redak rješava se neovisno, pa redak koji se prelomi u dva retka dodjeljuje slobodan prostor po retku, a ne kroz cijeli kontejner. U smjeru stupca ista raspodjela po glavnoj osi odvija se naspram izričite visine ili visine sadržaja
justify-content, align-items, gap i obrnuti smjerovi djeluju na geometriju koja je već izmjerena. Oni pomiču okvire; nikada ne pokreću ponovno mjerenje sadržaja stavke. Upravo ta odvojenost sprječava da složena nadzorna ploča izmjeri svoju djecu nekoliko puta
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 vrijednost je niz nastavka, i tako svaka ulazna točka za crtanje HTML-a izvještava što nije stalo. Predajte ga sljedećem okviru ili sljedećoj stranici i tok se nastavlja ondje gdje je stao
Grid postavljanje i što trak može biti
Grid trakovi prihvaćaju fiksne duljine, postotke, jedinicu fr, jednostavne izraze repeat() i minmax(). Automatsko postavljanje deterministički popunjava matricu zauzeća, pa isti HTML uvijek proizvodi isti raspored. Izričitim koordinatama dopušteno je preklapanje, što je namjerno: dizajn koji sloji značku preko kartice izražava namjeru, a ne grešku. Kada je izričito zadana samo jedna os, postavljanje pretražuje samo drugu os
Stavke koje se protežu preko nekoliko redaka doprinose svoju izmjerenu visinu natrag redcima koje pokrivaju, usrednjenu među njima, čime se sprječava da visoka rastegnuta stavka stisne jedan redak dok njezini susjedi ostanu niski:
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);
Djeca flex i grid kontejnera renderiraju se kroz isti HTML renderer kao i sve ostalo, a to je svojstvo koje značajku čini upotrebljivom umjesto zasebnim svijetom. Fontovi, CSS kaskada, poveznice, slike, tablice i dodatni ugniježđeni flex ili grid kontejneri svi se ponašaju unutar flex stavke potpuno jednako kao na najvišoj razini, a vanjski plan rasporeda bilježi konačne naredbe teksta i pravokutnika tako da ponovljeno crtanje ponovno koristi postojeću predmemoriju mjerenja
Zašto su fusnote problem paginacije?
Fusnota nije sadržaj koji teče nakon odlomka koji sadrži njezinu referencu; to je sadržaj koji se mora pojaviti na dnu istog okvira kao i njezina referenca. To preokreće uobičajeni redoslijed mjerenja, jer prostor dostupan za tekst tijela sada ovisi o sadržaju koji još nije raspoređen
Renderer stoga mjeri bilješku kada naiđe na referencu i oduzima područje bilješke od proračuna visine tijela trenutnog ograničenog okvira. Ako se referenca, dosadašnji tekst tijela i bilješka ne mogu svi smjestiti, oznaka fusnote i sve nakon nje zajedno prelaze u niz nastavka. To pravilo sprječava dva klasična kvara: bilješku koja se ispisuje preko teksta tijela i bilješku nasukanu na stranici čija je referenca na prethodnoj
U ograničenom okviru područje bilješke je pričvršćeno za dno s razdjelnom linijom iznad njega. U neograničenom mjerenju, gdje nema visine okvira za koju bi se pričvrstilo, područje bilješke slijedi odmah nakon tijela. Numeriranje se nosi u proširenom polju na stogu nastavka, pa DrawHTMLTextBox i DrawHTMLStory održavaju slijed neprekinutim preko stupaca i stranica, a niz nastavka proizveden prije nego što je to polje postojalo i dalje se ispravno nastavlja
// Fusnote unutar višestupčane priče zadržavaju jedan neprekinut slijed
Html := LoadTemplate('chapter.html'); // koristi oznake float:footnote
Remainder := Lib.DrawHTMLStory(40, 40, 515, 700,
2, // stupci
16, // razmak (gutter) u točkama
20, // maksimalan broj stranica za ovu priču
Html);
if Remainder <> '' then
Log('story exceeded its page budget');
Praktične smjernice za autore predložaka
Dizajnirajte unutar dokumentiranih gornjih granica. Flex kontejner s više od 256 izravne djece gotovo je uvijek podatkovna tablica u flex kostimu, a put tablice je ionako bolje mjeri. Grid veći od 64 puta 64 proračunska je tablica, i vrijedi isti savjet. Za višestupčani tekst tijela, ponašanje stupaca i rastavljanja opisano u rastavljanju riječi i uravnoteženim tekstualnim stupcima upravlja time kako tok izgleda unutar svakog stupca
Izmjerite prije nego što crtate kada raspored mora stati. GetHTMLTextHeight izvještava visinu koju bi zadana širina trebala, što je jeftin način odlučivanja između jednog i drugog rasporeda prije nego što se posvetite ispisu. I tretirajte neprazan niz nastavka kao normalan, a ne iznimku: to je mehanizam kojim se dugi sadržaj paginira, a ne signal greške
Kada HTML dolazi iz mehanizma za izvještaje, a ne iz ručno pisanih predložaka, put vođen skupom podataka u mehanizmu za izvještaje temeljenom na skupu podataka dobro se nadopunjuje s ovim, generirajući oznake koje flex i grid zatim raspoređuju. A kada isti sadržaj mora ponovno napustiti PDF, semantički put izvoza u izvozu PDF-a u Markdown i DOCX zatvara krug
Raspored HTML-a, generiranje izvještaja i semantički izvoz dio su jedne knjižnice za Delphi, C++Builder i Free Pascal; cjelokupni popis značajki nalazi se na stranici PDF Library for Delphi