A PDF Library for Delphi valódi kétdimenziós elrendezéssel rendereli a HTML-t egy PDF-oldalra: a display: flex és a display: grid megmérésre és elhelyezésre kerül, nem pedig egymásra rakott blokkokká silányul, a lábjegyzetek pedig a hivatkozásukat hordozó doboz alján kerülnek fenntartásra, olyan számozással, amely folyamatos marad oszlopokon és oldalakon át. A belépési pontok az ismerősek, a DrawHTMLTextBox egyetlen dobozhoz, a DrawHTMLStory pedig többoszlopos folyáshoz
Ez azért számít, mert a legtöbb jelentéstartalom manapság HTML-ként érkezik. A sablonokat olyan emberek írják, akik CSS-t írnak, a dashboardokat kártyaként tervezik, egy olyan renderelő pedig, amely csendben négy egymásra rakott blokká omlaszt egy flex sort, egy olyan dokumentumot ad, amely egyáltalán nem hasonlít a tervre. Amíg ez a képesség nem létezett, az egyetlen kétdimenziós konténer, amelyet a motor mért, a táblázat volt, így minden kártyaelrendezést kézzel kellett táblázatként újraírni
Mi változott az elrendezési modellben?
A korábbi fő ciklus egyetlen sordobozt tartott fenn, és lefelé haladt az oldalon. Ez a modell tökéletesen kezeli a soron belüli tartalmat és az egymásra rakott blokkokat, és nem tud kifejezni egy olyan konténert, amelynek gyermekei egymáshoz viszonyítva vannak méretezve. A táblázatok voltak az egyetlen kivétel, a saját kétmenetes mérésükkel
A flex és a grid egyaránt egy korlátozott mérési menetet ad hozzá egy konténer gyermekei fölé, a fontos szó pedig a korlátozott. Egy flex konténer legfeljebb 256 közvetlen gyermeket mér egy fix tömbbe. Egy grid legfeljebb 64x64 cellás foglaltsági mátrixot használ a determinisztikus automatikus elhelyezéshez. Ezek a felső határok azért léteznek, hogy egy rosszindulatú vagy generált stíluslap ne tudjon korlátlan rekurziót vagy négyzetes elhelyezési memóriát kiváltani, ami valódi aggály, amikor a HTML egy olyan sablonból származik, amelyet egy ügyfél szerkeszt
Hogyan kapják meg a flex elemek a méretüket?
Sor irányban a konténer összegzi minden elem alapértékét a növekedési és zsugorodási súlyaival együtt, majd a maradék helyet, legyen az pozitív vagy negatív, ezen súlyok szerint osztja el. A flex-wrap-pal minden sor önállóan kerül megoldásra, így egy két sorra törő sor soronként osztja ki a szabad helyet, nem a teljes konténeren keresztül. Oszlop irányban ugyanaz a fő tengely szerinti elosztás fut le vagy egy explicit magassággal, vagy a tartalom magasságával szemben
A justify-content, az align-items, a gap és a fordított irányok már megmért geometrián dolgoznak. Dobozokat mozgatnak; soha nem váltanak ki elemtartalom-újramérést. Ez a szétválasztás akadályozza meg, hogy egy összetett dashboard többször is megmérje a gyermekeit
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;
A visszatérési érték a folytatás-string, ez az a mód, ahogy minden HTML-rajzoló belépési pont jelenti, mi nem fért el. Add át a következő doboznak vagy a következő oldalnak, és a folyás ott folytatódik, ahol megállt
Grid-elhelyezés, és mi lehet egy sáv
A grid sávok elfogadnak fix hosszúságokat, százalékokat, az fr egységet, egyszerű repeat() kifejezéseket és minmax()-ot. Az automatikus elhelyezés determinisztikusan tölti ki a foglaltsági mátrixot, így ugyanaz a HTML mindig ugyanazt az elrendezést adja. Az explicit koordináták átfedése megengedett, ez szándékos: egy terv, amely egy jelvényt réteg egy kártya fölé, szándékot fejez ki, nem hibát. Ha csak az egyik tengely van explicit módon megadva, az elhelyezés csak a másik tengelyen keres
Azok az elemek, amelyek több sort is átfognak, visszaadják mért magasságukat az általuk lefedett soroknak, azok közt átlagolva, ami megakadályozza, hogy egy magas, átfogó elem összenyomjon egy sort, miközben a szomszédai alacsonyak maradnak:
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);
A flex és grid gyermekek ugyanazon a HTML-renderelőn keresztül kerülnek megjelenítésre, mint minden más, ez az a tulajdonság, amely a funkciót használhatóvá teszi, nem pedig egy külön világgá. A betűtípusok, a CSS-kaszkád, a hivatkozások, a képek, a táblázatok és a további beágyazott flex vagy grid konténerek mind pontosan úgy viselkednek egy flex elemen belül, mint a legfelső szinten, a külső elrendezési terv pedig rögzíti a végleges szöveg- és téglalap-parancsokat, így az ismételt rajzolás újrahasznosítja a meglévő mérési gyorsítótárat
Miért lapozási probléma a lábjegyzet?
Egy lábjegyzet nem olyan tartalom, amely a hivatkozását tartalmazó bekezdés után folyik; olyan tartalom, amelynek a hivatkozásával azonos doboz alján kell megjelennie. Ez megfordítja a szokásos mérési sorrendet, mert a törzsszöveg számára elérhető hely most olyan tartalomtól függ, amelyet még nem rendeztek el
A renderelő ezért megméri a jegyzetet, amikor eléri a hivatkozást, és levonja a jegyzet területét az aktuális korlátozott doboz törzsmagasság-költségkeretéből. Ha a hivatkozás, az addigi törzsszöveg és a jegyzet nem fér el mind, a lábjegyzetjelölő és minden utána következő együtt kerül a folytatás-stringbe. Ez a szabály előzi meg a két klasszikus hibát: egy jegyzetet, amely a törzsszövegre nyomódik, és egy jegyzetet, amely egy olyan oldalon reked, amelynek hivatkozása az előzőn van
Egy korlátozott dobozban a jegyzetterület az aljához van rögzítve, egy elválasztó vonallal felette. Korlátlan mérésnél, ahol nincs dobozmagasság, amihez rögzíteni lehetne, a jegyzetterület közvetlenül a törzs után következik. A számozás egy kiterjesztési mezőben van hordozva a folytatási veremen, így a DrawHTMLTextBox és a DrawHTMLStory folyamatosan tartja a sorozatot oszlopokon és oldalakon át, egy olyan folytatás-string pedig, amely azelőtt jött létre, hogy ez a mező létezett volna, még mindig helyesen folytatódik
// A lábjegyzetek egy többoszlopos storyn belül egyetlen futó sorozatot tartanak fenn
Html := LoadTemplate('chapter.html'); // float:footnote jelölőket használ
Remainder := Lib.DrawHTMLStory(40, 40, 515, 700,
2, // oszlopok
16, // oszlopköz pontban
20, // e story maximális oldalszáma
Html);
if Remainder <> '' then
Log('story exceeded its page budget');
Gyakorlati útmutatás sablonszerzőknek
Tervezz a dokumentált felső határokon belül. Egy 256-nál több közvetlen gyermekkel rendelkező flex konténer szinte mindig egy flex jelmezbe öltözött adattáblázat, a táblázat útvonal pedig egyébként is jobban méri. Egy 64x64-nél nagyobb grid egy táblázatkezelő, és ugyanez a tanács érvényes. Többoszlopos törzsszöveghez a elválasztás és kiegyensúlyozott szövegoszlopok ismertette oszlop- és elválasztási viselkedés szabályozza, hogyan néz ki a folyás az egyes oszlopokon belül
Mérj a rajzolás előtt, amikor egy elrendezésnek el kell férnie. A GetHTMLTextHeight jelenti, milyen magasságra lenne szükség egy adott szélességnél, ami az olcsó módja annak, hogy egy elrendezés és egy másik közt döntsél, mielőtt tintát kötnél le. És kezeld normálisnak, ne kivételesnek egy nem üres folytatás-stringet: ez az a mechanizmus, amellyel a hosszú tartalom lapozódik, nem egy hibajelzés
Amikor a HTML egy jelentéskészítő motorból származik, nem kézzel írt sablonokból, a az adathalmaz-vezérelt jelentéskészítő motor adatvezérelt útvonala jól illeszkedik ehhez, olyan jelölést generálva, amelyet a flex és a grid ezután elrendez. Amikor pedig ugyanannak a tartalomnak ismét el kell hagynia a PDF-et, a PDF exportálása Markdownba és DOCX-be szemantikus exportútvonala zárja le a kört
A HTML-elrendezés, a jelentésgenerálás és a szemantikus export egyetlen könyvtár részei Delphihez, C++Builderhez és Free Pascalhoz; a teljes funkciólista a PDF Library for Delphi oldalán található