HotPDF gali sukurti puslapiuotą dokumentą iš deklaratyvaus medžio, o ne iš koordinačių. Sudarote THPDFDOMDocument iš skyrių, dėklų, teksto, sąrašų ir lentelių, perduodate jį THPDFDOMRenderer, o atvaizduotojas matuoja, puslapiuoja, piešia puslapio elementus ir, jei paprašoma, generuoja PDF/UA struktūros medį, kuris rezultatą padaro prieinamą. Maketo kodas niekada neapskaičiuoja y koordinatės
Kiekvienas, kas prižiūrėjo koordinatėmis grįstą ataskaitų generatorių, žino, kodėl tai svarbu. Pirma versija veikia. Tada kliento adresas išauga iki trijų eilučių, lentelė gauna daugiau eilučių, lokalizuota antraštė persikelia į kitą eilutę, ir kiekviena tolesnė y pozicija tampa neteisinga. Pataisymai kaupiasi kaip rankiniu būdu patikrinami puslapių lūžiai, išsibarstę po verslo logiką, o po dvejų metų atsiradęs žymėto PDF reikalavimas negali būti pritaikytas atgaline data kodui, kuris net nežino, kas yra pastraipa
Ką valdo medis ir kodėl nuosavybė yra griežta
DOM kiekviename lygyje užtikrina vienintelę nuosavybę: dokumentas valdo savo skyrius, skyrius valdo savo turinį, antraštę ir poraštę, o dėklai, konteineriai ir lentelės valdo savo vaikinius elementus. Pakartotinis naudojimas vyksta per Clone arba per registruotą gamyklą, niekada neprijungiant to paties objekto prie dviejų tėvinių elementų. Ši taisyklė nėra formalumas. Komponentas, pasirodantis medyje du kartus, būtų matuojamas du kartus su skirtingais apribojimais ir atlaisvintas du kartus išardymo metu
Praktinė pasekmė kviečiančiam kodui – pagalbinės funkcijos grąžina naujus egzempliorius. Gamyklos registravimas su RegisterComponent ir CreateComponent iškvietimas suteikia pavadintą receptą, kuris kiekvieną kartą sukuria naują komponentą – taip medyje atsiduria pasikartojantys elementai, tokie kaip parašo blokas ar teisinė poraštė
uses
HPDFDoc, HPDFLayoutDOM;
var
Doc: THPDFDOMDocument;
Section: THPDFDOMSection;
Table: THPDFDOMTable;
Row: THPDFDOMTableRow;
I: Integer;
begin
Doc := THPDFDOMDocument.Create;
Doc.GenerateStructure := True; // generuoti PDF/UA struktūros medį
Doc.Language := 'en-US';
Section := Doc.AddSection;
Section.PageWidth := 595; // A4 punktais
Section.PageHeight := 842;
Section.MarginLeft := 56;
Section.MarginTop := 56;
Section.MarginRight := 56;
Section.MarginBottom := 56;
Section.Style.FontName := 'Helvetica';
Section.Style.FontSize := 10;
Section.Body.AddHeading('Annual maintenance report', 1);
Section.Body.AddText('Every asset inspected during the reporting ' +
'period is listed below, grouped by site.');
Section.Body.AddSpacer(12);
Table := THPDFDOMTable.Create('assets');
Table.AddColumn(3); // svoriai, ne absoliutūs pločiai
Table.AddColumn(1);
Table.AddColumn(1);
Table.RepeatHeaders := True;
Row := Table.AddRow(18, True); // antraštės eilutė
Row[0].Text := 'Asset';
Row[1].Text := 'Last service';
Row[2].Text := 'Status';
for I := 0 to High(Assets) do
begin
Row := Table.AddRow(16);
Row[0].Text := Assets[I].Name;
Row[1].Text := Assets[I].ServiceDate;
Row[2].Text := Assets[I].Status;
end;
Section.Body.Add(Table);
end;
Kaip puslapiavimas išvengia kvadratinės kainos?
Naivus būdas puslapiuoti medį – klonuoti viską, kas netilpo, ir perkelti į kitą puslapį. Lentelėje su dešimt tūkstančių eilučių tai klonuoja likusias eilutes po vieną kartą kiekvienam puslapiui ir paverčia linijinį dokumentą kvadratiniu
HotPDF vietoj to skaido siaurai. Aukščiausio lygio atvaizduotojas apeina turinio vaikinius elementus pagal indeksą ir niekada neklonuoja viso skyriaus ar turinio. Klonuojamas tik įdėtų dėklų ir konteinerių paveiktas pomedis, jei jie iš tikrųjų kerta puslapio ribą, o du „sunkūs“ lapo tipai neša žymeklį, o ne kopiją: teksto tęsinys saugo šaltinio simbolių diapazoną, kurį dar liko atvaizduoti, o lentelės tęsinys saugo eilučių dalį, kurios dar reikia patalpinti. Ilgi dokumentai lieka linijiniai, o ilgos pastraipos kainuoja tiek pat, ar jos lūžta vieną kartą, ar penkis kartus
Matavimas išlieka sąžiningas šalutinio poveikio atžvilgiu. THPDFLayoutElement.Measure privalo neturėti piešimo šalutinio poveikio, o faktinis patalpinimas visada vyksta per THotPDF.PlaceLayoutElement – tą pačią centrinę procedūrą, kuri iš naujo pamatuoja patalpintą fragmentą, sutvarko perpildymo nuosavybę ir įrašo diagnostiką. DOM atvaizduotojas sprendžia tik naujo puslapio politiką, puslapio elementus, tarpus ir tęsinių gyvavimo trukmę
Lentelės antraštės taisyklės, saugančios nuo begalinio dokumento
Kartotinių lentelės antraščių rodymas per puslapius skamba paprastai, tačiau slepia du gedimo scenarijus. HotPDF reikalauja, kad antraštės eilutės pasirodytų tik pirmajame nuosekliame eilučių bloke ir kad pirmasis skaidymas sutalpintų visas antraštės eilutes plius bent vieną turinio eilutę. Be antrosios taisyklės, antraštė, aukštesnė už likusią vietą, sukurtų puslapį, kuriame yra tik antraštė, po jo – kitą tapatų puslapį, ir taip be galo
Tęsinio puslapiai perpiešia antraštę, ir ta perpiešta kopija pažymima kaip artefaktas, o ne kaip turinys – tai teisingas atsakymas ir prieinamumui, ir teksto ištraukimui. Originali antraštės eilutė lieka loginėje lentelės struktūroje lygiai vieną kartą. Praleidus tai, ekrano skaitytuvas paskelbtų stulpelių pavadinimus dar kartą duomenų viduryje, o teksto ištraukimo įrankis įterptų pasikartojančią antraštės eilutę tarp turinio eilučių
Taip pat yra apsauginė riba tęsinio gyliui, nes pasirinktinis komponentas gali laisvai įgyvendinti Split taip, kad visada grąžintų lygiavertę uodegą. Atvaizduotojas patikrina ribą atskyręs uodegą ir prieš pradėdamas kitą puslapį, o dabartinė iteracija atlaisvina uodegą savo finally bloke, todėl blogai besielgiantis trečiosios šalies komponentas sužlunga su diagnozuojama klaida, o ne prigrūda diską
Vienas loginis elementas, daug puslapio fragmentų
Automatinis žymėjimas yra ta vieta, kur puslapiavimo modelis ir struktūros modelis turi sutapti. Per du puslapius suskaidyta pastraipa yra viena loginė pastraipa, todėl ji turi likti vienu struktūros elementu. Bet pažymėto turinio identifikatoriai priklauso nuo puslapio, todėl kiekvienam matomam fragmentui reikia savo MCID tame puslapyje, kuriame jis pasirodo
HotPDF tai sprendžia palikdama vieną struktūros elementą ir kiekvienam fragmentui pridėdama pažymėto turinio nuorodą į jo /K masyvą, kur pora /Pg ir /MCID nurodo puslapį ir identifikatorių. To MCID ParentTree įrašas rodo atgal į tą patį elementą. Būtent to ir tikisi ISO 14289, ir būtent todėl vidiniai tęsinio klonai skiriasi nuo įprastų klonų: įprastas Clone reiškia naują loginį turinį ir gauna naują semantinę tapatybę, o vidinis tęsinio klonas paveldi to komponento, kurį jis tęsia, tapatybę
Elementų pakartotinis naudojimas ieškomas per semantinių tapatybių indeksą, surikiuotą pagal komponento rodyklę ir ieškomą dvejetaine paieška – tai palaiko logaritminę paieškos trukmę dideliuose medžiuose. Indeksas laiko tik nuorodas, nesančias savininku; pačių struktūros objektų gyvavimo trukmė lieka susieta su PDF objektų grafu
Struktūros taisyklės, kurias atvaizduotojas taiko iš anksto
Įjungus GenerateStructure, kelios PDF/UA taisyklės patikrinamos medžio atvaizdavimo metu, o ne po to, kai failas jau sukurtas. Antraštės prasideda 1 lygiu ir negali praleisti lygių. LI gali pasirodyti tik L viduje, o Lbl ir LBody – tik LI viduje. TR priklauso lentelei, o TH ir TD – eilutei. Iliustracija be alternatyvaus teksto PDF/UA režime atmetama
Atmesti iš anksto – tai sąmoningas pasirinkimas. Validatorius, praneša apie trūkstamą alternatyvų tekstą jau parašius dokumentą, tik pasako, kad partiją iš dešimt tūkstančių ataskaitų reikia generuoti iš naujo; atvaizduotojas, kuris atmeta komponentą, pasako, kuris konkrečiai komponentas, kol duomenys, iš kurių jis buvo sukurtas, dar pasiekiami. Atitikties patikrinimas vis tiek priklauso konvejeryje kaip atskiras žingsnis, o jo mechanika aprašyta straipsnyje PDF/A, PDF/X ir PDF/UA validavimas
var
Pdf: THotPDF;
Renderer: THPDFDOMRenderer;
Stats: THPDFDOMRenderStatistics;
begin
Pdf := THotPDF.Create(nil);
Renderer := THPDFDOMRenderer.Create;
try
Pdf.FileName := 'maintenance-report.pdf';
Pdf.BeginDoc;
Stats := Renderer.Render(Doc, Pdf);
Pdf.EndDoc;
Writeln(Format('%d page(s), %d placement(s), %d split(s)',
[Stats.PageCount, Stats.PlacementCount, Stats.SplitCount]));
Writeln(Format('structure elements=%d marked content=%d artifacts=%d',
[Stats.StructureElementCount, Stats.MarkedContentCount,
Stats.ArtifactCount]));
Writeln(Format('deepest continuation chain: %d',
[Stats.MaximumContinuationDepth]));
finally
Renderer.Free;
Doc.Free;
Pdf.Free;
end;
end;
Statistikos įrašas naudingesnis, nei atrodo iš pirmo žvilgsnio. Staigiai kylantis SplitCount po šablono pakeitimo dažniausiai reiškia, kad komponentas pradėjo matuotis aukštesnis nei jo konteineris. Palaipsniui kylantis MaximumContinuationDepth yra ankstyvas įspėjimas apie komponentą, kurio Split per puslapį pasistūmi pernelyg mažai. O ArtifactCount palyginimas su tęsinio puslapių skaičiumi patvirtina, kad pasikartojančios antraštės iš tikrųjų buvo pažymėtos kaip artefaktai
Kur DOM tinka šalia tiesioginio API
DOM nepakeičia tiesioginio piešimo; jis veikia virš tų pačių puslapio objektų. Bet ką, ką patalpina atvaizduotojas, galima pertraukti tiesioginiais THotPDF iškvietimais, o tai svarbu, kai ataskaitai reikia vieno rankiniu būdu pozicionuoto elemento, pavyzdžiui, parašo vaizdo tikslioje vietoje. Puslapio uždarymas lieka AddPage ir EndDoc kontrolėje, todėl tiesioginio išpylimo režimas atmintyje nelaiko jokių užbaigtų puslapių, o rezidenti atmintis lieka valdoma dabartinių tęsinių, šriftų resursų ir įprasto dokumento objektų grafo
Rinkitės DOM, kai turinys yra duomenimis grįstas, o maketas – taisyklėmis grįstas, ir palikite tiesioginį piešimą fiksuotai grafikai. Jei jūsų dabartinis skausmas yra konkrečiai lentelių puslapiavimas, verta pirmiau paskaityti siauresnį požiūrį straipsnyje lentelių generavimas PDF faile, o teksto lygmens elgsena, tokia kaip lygiavimas, aprašyta straipsnyje teksto lygiavimas
Deklaratyvus maketavimas, automatinis žymėjimas ir tiesioginio piešimo API pateikiami tame pačiame komponente Delphi ir C++Builder aplinkoms; pilnas funkcijų sąrašas pateikiamas HotPDF Delphi PDF komponento puslapyje