HotPDF može da izgradi paginirani dokument iz deklarativnog stabla umesto iz koordinata. Sastavljate THPDFDOMDocument od sekcija, stekova, teksta, listi i tabela, predajete ga THPDFDOMRenderer-u, a renderer meri, paginira, crta nameštaj stranice i, kada se to traži, emituje PDF/UA strukturno stablo koje čini rezultat pristupačnim. Kod rasporeda nikada ne izračunava y koordinatu
Svako ko je održavao generator izveštaja vođen koordinatama zna zašto je ovo bitno. Prva verzija radi. Zatim adresa kupca naraste na tri reda, tabela dobije redove, lokalizovan naslov se prelama, i svaka nizvodna y pozicija je pogrešna. Popravke se gomilaju kao ručne provere preloma stranica raspršene po poslovnoj logici, a zahtev za označeni PDF koji stigne dve godine kasnije ne može se naknadno ugraditi u kod koji nema pojma šta je pasus
Šta stablo poseduje, i zašto je vlasništvo strogo?
DOM primorava jedinstveno vlasništvo na svakom nivou: dokument poseduje svoje sekcije, sekcija poseduje svoje telo, zaglavlje i podnožje, a stekovi, kontejneri i tabele poseduju svoju decu. Ponovna upotreba se dešava kroz Clone ili kroz registrovanu fabriku, nikada tako što se isti objekat prikači na dva roditelja. To pravilo nije ceremonija. Komponenta koja se pojavljuje dvaput u stablu bila bi izmerena dvaput sa različitim ograničenjima i oslobođena dvaput pri rušenju
Praktična posledica za pozivajući kod je da pomoćnici vraćaju nove instance. Registrovanje fabrike pomoću RegisterComponent i pozivanje CreateComponent daje vam imenovan recept koji proizvodi svežu komponentu svaki put, što je način na koji ponovljeni nameštaj poput bloka za potpis ili pravnog podnožja pripada stablu
uses
HPDFDoc, HPDFLayoutDOM;
var
Doc: THPDFDOMDocument;
Section: THPDFDOMSection;
Table: THPDFDOMTable;
Row: THPDFDOMTableRow;
I: Integer;
begin
Doc := THPDFDOMDocument.Create;
Doc.GenerateStructure := True; // emituj PDF/UA strukturno stablo
Doc.Language := 'en-US';
Section := Doc.AddSection;
Section.PageWidth := 595; // A4 u tačkama
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); // težine, ne apsolutne širine
Table.AddColumn(1);
Table.AddColumn(1);
Table.RepeatHeaders := True;
Row := Table.AddRow(18, True); // red zaglavlja
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;
Kako paginacija izbegava kvadratnu cenu?
Naivan način paginacije stabla je kloniranje svega što nije stalo i prenošenje na sledeću stranicu. Na tabeli sa deset hiljada redova, to klonira preostale redove jednom po stranici i pretvara linearan dokument u kvadratan
HotPDF umesto toga deli usko. Renderer najvišeg nivoa obilazi decu tela po indeksu i nikada ne klonira celu sekciju ili telo. Samo ugnježdeni stekovi i kontejneri koji zaista prelaze preko granice stranice dobijaju kloniranje pogođenog podstabla, a dva teška lista tipa nose kursor umesto kopije: nastavak teksta čuva izvorni opseg karaktera koji još duguje, a nastavak tabele čuva odsečak redova koji tek treba da postavi. Dugi dokumenti ostaju linearni, a dugi pasusi koštaju isto bez obzira da li se prelome jednom ili pet puta
Merenje ostaje pošteno u pogledu sporednih efekata. THPDFLayoutElement.Measure je obavezan da bude bez sporednih efekata crtanja, a stvarno postavljanje se uvek izvodi kroz THotPDF.PlaceLayoutElement, istu centralnu rutinu koja ponovo meri postavljen fragment, postavlja vlasništvo prekoračenja i beleži dijagnostiku. DOM renderer odlučuje samo politiku sveže stranice, nameštaj stranice, razmak i vek trajanja nastavaka
Pravila zaglavlja tabele koja sprečavaju beskonačan dokument
Ponavljanje zaglavlja tabele preko stranica zvuči jednostavno i krije dva otkaza. HotPDF zahteva da se redovi zaglavlja pojavljuju samo u prvom nizu uzastopnih redova, i da prvo deljenje smesti sve redove zaglavlja plus barem jedan red tela. Bez drugog pravila, zaglavlje više od preostalog prostora proizvodi stranicu koja ne sadrži ništa osim zaglavlja, praćenu drugom identičnom stranicom, zauvek
Stranice nastavka ponovo crtaju zaglavlje, a ta ponovo nacrtana kopija je označena kao artefakt, a ne kao sadržaj, što je ispravan odgovor i za pristupačnost i za izvlačenje teksta. Originalni red zaglavlja ostaje u logičkoj strukturi tabele tačno jednom. Preskočite ovo i čitač ekrana najavljuje naslove kolona ponovo usred podataka, a izvlakač teksta umeće duplirani red zaglavlja između redova tela
Postoji i odbrambeni plafon na dubinu nastavka, jer je prilagođena komponenta slobodna da implementira Split na način koji uvek vraća ekvivalentan rep. Renderer proverava ograničenje nakon odvajanja repa i pre početka sledeće stranice, a trenutna iteracija oslobađa rep u sopstvenom bloku finally, tako da komponenta treće strane koja se loše ponaša ne uspe sa dijagnostikovanom greškom umesto da puni disk
Jedan logički element, mnogo fragmenata stranice
Automatsko označavanje je mesto gde model paginacije i model strukture moraju da se slože. Pasus podeljen na dve stranice je jedan logički pasus, pa mora ostati jedan element strukture. Ali identifikatori obeleženog sadržaja su po stranici, pa svaki vidljivi fragment treba sopstveni MCID na stranici na kojoj se pojavljuje
HotPDF ovo rešava tako što drži jedan element strukture i dodaje referencu obeleženog sadržaja u njegov niz /K za svaki fragment, sa parom /Pg i /MCID koji identifikuje stranicu i identifikator. Slot ParentTree za taj MCID pokazuje nazad na isti element. Ovo je tačno ono što ISO 14289 očekuje, i to je razlog zašto se klonovi nastavka razlikuju od običnih klonova: obični Clone znači novi logički sadržaj i dobija novi semantički identitet, dok interni klon nastavka nasleđuje identitet komponente koju nastavlja
Ponovna upotreba elementa se traži preko indeksa semantičkih identiteta sortiranih po pokazivaču komponente i pretraženih binarnim poređenjem, što drži pretragu logaritamskom na velikim stablima. Indeks čuva samo reference bez vlasništva; vek trajanja samih objekata strukture ostaje uz graf objekata PDF-a
Pravila strukture koja renderer sprovodi unapred
Sa uključenim GenerateStructure, nekoliko pravila PDF/UA se proverava dok se stablo renderuje, a ne nakon što fajl već postoji. Naslovi počinju od nivoa 1 i ne smeju preskočiti nivoe. LI može da se pojavi samo unutar L, a Lbl i LBody samo unutar LI. TR pripada tabeli, a TH i TD redu. Figura bez alternativnog teksta se odbacuje u PDF/UA režimu
Odbacivanje rano je namerni izbor ovde. Validator koji prijavljuje nedostajući alternativni tekst nakon što je dokument napisan govori vam da je serija od deset hiljada izvoda potrebno ponovo generisati; renderer koji odbija komponentu govori vam koja komponenta, dok su podaci koji su je proizveli još uvek u opsegu. Provera usklađenosti i dalje pripada pipeline-u kao poseban korak, a mehanika toga je obrađena u validaciji PDF/A, PDF/X i PDF/UA
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;
Zapis statistike je korisniji nego što na prvi pogled izgleda. Naglo rastuće SplitCount nakon izmene šablona obično znači da je komponenta počela da se meri viša od svog kontejnera. Sporo rastuće MaximumContinuationDepth je rani znak upozorenja za komponentu čiji Split pravi premalo napretka po stranici. A poređenje ArtifactCount sa brojem stranica nastavka potvrđuje da su ponovljena zaglavlja zaista označena kao artefakti
Gde se DOM uklapa uz direktan API
DOM ne zamenjuje direktno crtanje; sedi na vrhu istih objekata stranice. Sve što renderer postavi može se isprepletati sa direktnim pozivima na THotPDF, što je bitno kada izveštaj treba jedan ručno pozicioniran element poput slike potpisa na tačnoj lokaciji. Zatvaranje stranice ostaje pod kontrolom AddPage i EndDoc, tako da režim trenutnog ispiranja ne drži nijednu završenu stranicu u memoriji, a rezidentna memorija ostaje upravljana trenutnim nastavcima, resursima fonta i običnim grafom objekata dokumenta
Izaberite DOM kada su podaci vođeni podacima, a raspored pravilima, i zadržite direktno crtanje za fiksnu grafiku. Ako je vaš trenutni problem specifično paginacija tabele, uži pristup u generisanju tabela u PDF-u vredi pročitati prvo, a ponašanje na nivou teksta poput poravnanja je opisano u poravnanju teksta
Deklarativni raspored, automatsko označavanje i API direktnog crtanja isporučuju se u istoj komponenti za Delphi i C++Builder; kompletna lista funkcija je na stranici HotPDF Delphi PDF komponente