HotPDF konverterer HTML til PDF-sider via THPDFHTMLImporter.RenderHTML5, en versjonert paged-media-profil med en ekte DOM, en ekte CSS-kaskade, tabeller, sideskift og valgfri AcroForm-generering fra HTML-skjemakontroller. Den kjører helt in-process: ingen nettlesermotor, ingen Chromium, ingen ekstern konverterer, og som standard ingen fil- eller nettverkstilgang i det hele tatt
Grunnen til at det finnes et eget inngangspunkt i stedet for en forbedret RenderHTML er kompatibilitet. Den eldre metoden har gjengitt fakturaer og brev i produksjonsapplikasjoner i årevis, og en overbærende DOM med en full kaskade endrer utdataen til dokumenter som var finjustert mot den gamle oppførselen. Så den gamle metoden beholder sin nøyaktige semantikk, og alt nytt ligger bak RenderHTML5, hvis profilversjon rapporteres av HTML5ProfileVersion og hvis støttede milepæler listes av HTML5ProfileMilestones
Hva legger HTML5-profilen faktisk til?
Fire evner betyr noe i praksis. En strukturell DOM der html og body er beholdere i stedet for tekstnoder. En CSS-kaskade med korrekt presedens. Tabellayout. Og eksplisitt kontroll av sideskift, som er det «paged media» betyr: HTML ble designet for en rullbar visningsflate, og å gjøre det om til sider krever at man bestemmer hvor innhold har lov til å brytes
Poenget om beholdere høres ut som en implementasjonsdetalj, men er faktisk forskjellen mellom at det fungerer og at det ikke gjør det. Hvis rotelementene behandles som tekstbeholdere, kollapser hele DOM-en til én tekstblokk under parsing, og hver stilarkregel, hvert sideskift, hver tabell og hvert skjematre deretter anvendes på ingenting. Enhver HTML-renderer som produserer én udifferensiert tekstvegg fra et strukturert dokument, har gjort nøyaktig denne feilen
Kaskaderekkefølge, forklart i detalj
CSS-presedens er ikke én enkel sortering. HotPDF anvender normale deklarasjoner fra matchende regler i rekkefølge etter spesifisitet og deretter kilderekkefølge, anvender innebygde normale deklarasjoner på toppen, deretter !important-deklarasjonene fra matchende regler, og til slutt innebygd !important. Fire gjennomganger, i den rekkefølgen
Implementasjoner som forenkler dette til «sist vinner» får de vanlige tilfellene riktige og de interessante tilfellene feil, noe som er verre enn å få dem jevnt over feil, fordi feilen bare dukker opp i maler som bruker !important for å overstyre et grunnleggende stilark — nøyaktig de malene skrevet av folk som forventer at kaskaden fungerer:
uses
HPDFDoc, HPDFHTMLImport;
var
Pdf: THotPDF;
Importer: THPDFHTMLImporter;
Html, Css: WideString;
begin
Pdf := THotPDF.Create(nil);
Importer := THPDFHTMLImporter.Create(Pdf);
try
Pdf.FileName := 'statement.pdf';
Pdf.BeginDoc;
Importer.Margin := 56; // punkter
Importer.BaseFontName := 'Arial';
Importer.BaseFontSize := 10;
Importer.CreateAcroForms := False; // gjengi kontroller, ikke opprett felt
Css := '@page { margin: 56pt } ' +
'table { border-collapse: collapse } ' +
'tr.total { page-break-inside: avoid; font-weight: bold }';
Html := LoadTemplate('statement.html');
if not Importer.RenderHTML5(Html, Css) then
raise Exception.Create('nothing was emitted - check the markup');
Pdf.EndDoc;
Writeln(Format('profile=%d nodes=%d rules=%d layout ops=%d',
[Importer.Statistics.ProfileVersion,
Importer.Statistics.ParsedNodeCount,
Importer.Statistics.CSSRuleCount,
Importer.Statistics.LayoutOperationCount]));
finally
Importer.Free;
Pdf.Free;
end;
end;
Det andre argumentet er et forfatterstilark som anvendes ved siden av det dokumentet selv bærer med seg, som er den ryddige måten å håndheve interne regler som sidemarger og enkelinjekontroll uten å redigere hver mal
Hvorfor henter importøren aldri noe?
En HTML-til-PDF-konverterer som løser eksterne referanser, er en server-side request forgery-motor forkledd som dokumentgenerering. Pek den mot brukerlevert markup, og den vil villig hente http://169.254.169.254/ eller en fil-URL fra applikasjonsserveren din og bygge resultatet inn i en PDF som noen laster ned
HotPDF leser derfor verken filer eller nettverk som standard. Bilder og andre ressurser kommer inn på nøyaktig to måter: som data:-URI-er inne i markup-en, eller gjennom OnResource-tilbakekallet, der din kode bestemmer hva en URI betyr og leverer bytene:
procedure TReportForm.SupplyResource(Sender: TObject;
const URI: WideString; ResourceKind: THPDFHTMLResourceKind;
var Data: TBytes; var ContentType: string; var Handled: boolean);
begin
Handled := False;
// Server kun fra en tillatelseslistet lokal ressurskatalog. Aldri
// send URI-en til en HTTP-klient, og aldri sett den sammen med en sti
// uten å validere den først
if StartsText('asset:', URI) then
begin
Data := LoadAssetBytes(Copy(URI, 7, MaxInt));
ContentType := 'image/png';
Handled := Length(Data) > 0;
end;
end;
Hver ressurs som faktisk kommer inn, telles likevel mot tre separate tak: hvor mange ressurser, hvor mange byte hver, og hvor mange byte totalt. Et tilbakekall som trofast returnerer det det blir bedt om, kan ikke lures til å tømme minnet av et dokument som refererer det samme 50 MB-bildet to hundre ganger
Fire budsjetter, én feilmodus
Parsernoder, CSS-regler, layoutoperasjoner og ressurser måles uavhengig av hverandre, fordi de svikter uavhengig av hverandre. Et dokument kan være lite og patologisk kostbart å legge ut; et annet kan være grunt med et stilark designet for å eksplodere selektormatching. Å tømme noen av dem utløser EHPDFHTMLImportBudget, en dedikert unntaksklasse slik at budsjettuttømming kan fanges separat fra feilformet inndata
Statistikk nullstilles ved starten av hvert RenderHTML5-kall, slik at tallene beskriver én import i stedet for hele levetiden til importørobjektet. I en tjeneste som gjengir ubetrodde maler, logg ParsedNodeCount, LayoutOperationCount og ResourceRejectCount per dokument: et stigende avvisningstall betyr som regel at en malforfatter refererer ressurser tilbakekallet ditt ikke gjenkjenner, og et layoutoperasjonstall nær taket betyr at det neste, litt større dokumentet vil feile
Å velge mellom HTML-import og layout-API-et
HTML-import vinner når innholdet forfattes som HTML av folk som ikke er Delphi-utviklere, når maler endres oftere enn kode, eller når det samme innholdet også må sendes som e-post. Det deklarative layouttreet beskrevet i deklarativ PDF-layout med tagget utdata vinner når dokumentet genereres fra data av din egen kode, når du trenger PDF/UA-struktur generert automatisk, eller når presis kontroll over paginering betyr mer enn skrivebekvemmelighet
De utelukker ikke hverandre. En vanlig form er HTML-import for selve brødteksten i en rapport, med topptekst, bunntekst og signaturblokker tegnet direkte, siden dette er fast geometri ingen mal bør eie. Tabeller er det ene området der det er verdt å måle begge deler: komplekse finansielle tabeller pagineres noen ganger bedre gjennom den dedikerte tabellstien beskrevet i generering av tabeller i PDF
Sett CreateAcroForms bevisst. Av, gjengis HTML-skjemakontroller som statisk grafikk, som er det en utskrevet kontoutskrift ønsker. På, blir de ekte interaktive AcroForm-felter, som er det et utfyllbart skjema ønsker, og feltoppførselen som følger, dekkes i å legge til skjemafelter i et lastet PDF-dokument
HTML-import, deklarativ layout og direkte-tegning-API-et retter seg alle mot det samme dokumentobjektet i Delphi og C++Builder; den fullstendige funksjonslisten finnes på HotPDF Delphi PDF-komponentsiden