Tre rasterisatorer kan læse den samme PDF og være uenige om, hvad der står. Den indbyggede motor i PDF Library for Delphi er den, der følger med uden ekstra filer og gengiver alting kompetent, og derfor har den fortjent standardpladsen. Cairo bringer en anden pipeline til gennemsigtighed og kantudjævning med sig og plejer at være den, folk griber efter, når soft masks eller blend modes kommer forkert ud andre steder. PDFium bærer Chromes gengivelseskode, så en side, der ser rigtig ud i en browser, ser som regel også rigtig ud under PDFium, til gengæld for en anselig DLL og en bitbredde, den insisterer på at matche. Ingen af de tre er korrekt i det abstrakte. Korrekthed er noget per dokument, og den eneste ærlige måde at finde ud af, hvilken motor der klarer et givet korpus, er at køre det korpus gennem dem hver især
Det er argumentet for at behandle motoren som et valg ved kørselstid frem for ved buildtid. PDF Library for Delphi, PDF-biblioteket til Delphi og C++Builder fra losLab, lægger alle tre bag én fælles gengivelsesflade, så beslutningen koster ét heltal i stedet for en kodeforgrening. Resten handler om at vælge mellem dem sikkert, om at bekræfte hvilke motorer en udrullet binær faktisk bærer, og om at holde gengivelsestilstanden fra stille og roligt at forgifte det næste job
Tre rasterisatorer bag én kaldeflade
Biblioteket nummererer sine motorer. Motor 1 er den indbyggede renderer, standarden, med GDI+-udjævningsindstillinger på Windows. Motor 2 er Cairo, og motor 3 er PDFium, begge valgt ved kørselstid gennem SelectRenderer. De to eksterne motorer indlæses fra DLL'er, hvis stier du angiver med SetCairoFileName og SetPDFiumFileName, før du vælger dem. Uanset hvilken motor der er aktiv, går arbejdet gennem de samme kald: RenderPageToFile, RenderPageToStream, RenderDocumentToFile. At skifte motor flytter ét tal; resten af din gengivelseskode opdager det aldrig
Destinationsmodellen rækker et godt stykke ud over bitmaps. Renderer-klassen kan også skrive til metafiler (WMF, EMF, EMF+), EPS, direkte enhedskontekster, printere og HTML5, hvor Cairo og PDFium kun dukker op som ekstra destinationer, når de er kompileret ind. Rasteroutput er dér, hvor de tre motorer afviger mest synligt, og det er derfor det, eksemplerne her bruger
Gå aldrig ud fra, at en motor findes: sondér ved opstart
Cairo og PDFium er features under betinget kompilering, hvilket betyder, at en binær kan bygges helt uden dem. Sker det, udløser en anmodning om motor 2 eller 3 ikke noget som helst. SelectRenderer returnerer bare en anden værdi end det ID, du bad om, og kode, der ignorerer returværdien, gengiver videre med hvilken motor der nu allerede var aktiv. Forsvaret er en opstartssondering, der beder hver motor identificere sig og noterer svaret:
function ProbeEngines(PDF: TPDFlib): string;
begin
Result := 'built-in'; // motor 1 er altid til stede
if (PDF.SetCairoFileName('cairo.dll') = 1) and (PDF.SelectRenderer(2) = 2) then
Result := Result + ', cairo';
if (PDF.SetPDFiumFileName('pdfium.dll') = 1) and (PDF.SelectRenderer(3) = 3) then
Result := Result + ', pdfium';
PDF.SelectRenderer(1); // gendan standarden før det rigtige arbejde
end;
Kør den sondering én gang ved opstart, og skriv dens resultat i loggen ved siden af hvert gengivelsesjob. Det absolut hyppigste spørgsmål, når en kunde melder om en forskel i gengivelsen, er, hvilke motorer deres installation faktisk har, og et svar på én linje i loggen afgør det uden en fjernskrivebordssession. En nyttig sidegevinst: returnerer SetPDFiumFileName selv 0, ved du allerede, at DLL'en er problemet (forkert sti, forkert bitbredde, en manglende afhængighed) frem for en binær kompileret uden PDFium-understøttelse, for stikaldet fandt ingenting, længe før SelectRenderer overhovedet kørte
Ti outputformater bag ét Options-heltal
Parameteren Options på gengivelseskaldene vælger outputkodningen: 0 er BMP, 1 JPEG, 2 WMF, 3 EMF, 4 EPS, 5 PNG, 6 GIF, 7 TIFF, 8 EMF+ og 9 HTML5. PNG (5) er det fornuftige standardvalg til forhåndsvisninger og arkivsidebilleder. JPEG (1), parret med SetJPEGQuality, er det bedre valg til fotografiske scanninger, hvor filstørrelsen betyder mere end skarpe kanter
Ét format skjuler et krav til målstrømmen. BMP-stien skriver først billeddataene og søger så tilbage til offset 0x26 for at lappe opløsningsfelterne i headeren. Peg den mod en strøm, der kun kan gå fremad, en komprimeringsindpakning eller en netværkssocket, og kaldet fejler på en måde, der læses som en motorfejl, men ikke er det. Når et mål uden søgning ikke kan undgås, så gengiv PNG i stedet, eller kør BMP'en gennem en hukommelsesstrøm og kopiér den videre, når den er færdig
Den DPI, du sender ind, er ikke den DPI, du får
Hvert gengivelseskald tager et DPI-argument, men den opløsning, du faktisk får, er den værdi ganget med den globale gengivelsesskala. SetRenderScale starter på 1,0, og når du først ændrer den, gælder den nye faktor stiltiende for enhver senere gengivelse på den instans:
PDF.SetRenderScale(2.0); // enhver senere gengivelse fordobles
PDF.RenderPageToFile(150, 1, 5, 'p1.png'); // reelt 300 DPI
PDF.SetRenderScale(1.0); // nulstil, ellers bliver dine miniaturer enorme
Den samme klæbrighed gælder SetRenderCropType og indstillingen for JPEG-kvalitet. I en tjeneste, der producerer miniaturer, forhåndsvisninger og billeder i trykopløsning fra én delt instans, er disse efterladte indstillinger det, der i virkeligheden ligger bag den lejlighedsvise sag om "miniaturerne er pludselig 40 MB". To rene udveje: nulstil den relevante tilstand i toppen af hver operation, eller afsæt en separat instans til hver outputprofil, så intet siver på tværs
At justere standardmotoren før man griber efter en anden
En overraskende stor andel af ønskerne om "vi har brug for en anden motor" viser sig at være indstillingsproblemer i forklædning. Den indbyggede renderer blotlægger sin udjævningsadfærd gennem SetGDIPlusOptions og den bredere SetRenderOptions-familie, og SetGDIPlusFileName lader dig pege den mod en bestemt GDI+-runtime, når et udrulningsmiljø leverer en usædvanlig en. Takkede streger ved lav DPI, uskarp tekst i miniaturer, båndsætning hen over gradienter: alt sammen reagerer det på de knapper, og at dreje på dem koster ingenting i installeringsprogrammet. At tilføje Cairo eller PDFium betyder derimod at levere flere DLL'er, at holde styr på en anden eller tredje bitbredde-variant og at påtage sig forpligtelsen til at opdatere dem
Så en klage over kvaliteten har en naturlig rækkefølge af handlinger. Reproducér den først ved kundens nøjagtige DPI og skala, for halvdelen af gangene fordamper forskellen, når de to stemmer. Prøv derefter den indbyggede motors udjævningsindstillinger. Først derefter stiller du siden op side om side på tværs af motorer med alle andre variable holdt konstante: gengiv den til PNG gennem motor 1, 2 og 3 ved identisk DPI, og vedhæft alle tre. Som regel er to af de tre enige, og det flertal fortæller dig, om afvigeren er dokumentet, der bliver fortolket anderledes, eller din egen forventede baseline, der er ved siden af. Tre konkrete billeder afgør en strid om "den gengiver forkert" langt hurtigere end et afsnit fyldt med tillægsord
En fallback-kæde, der forklarer sig selv
Når sondering og disciplin omkring tilstanden er på plads, er selve fallback-kæden kort. At opdage en fejl hviler på LastRenderError, som rummer motorens egen meddelelsestekst for den seneste gengivelse og er tom, når gengivelsen lykkedes:
procedure RenderPageWithFallback(PDF: TPDFlib; Page: Integer; const OutFile: string);
begin
PDF.SelectRenderer(1); // den indbyggede først
PDF.RenderPageToFile(200, Page, 5, OutFile); // 5 = PNG
if PDF.LastRenderError = '' then Exit;
LogEngineFailure('built-in', Page, PDF.LastRenderError);
if PDF.SelectRenderer(3) = 3 then // PDFium som den tunge fallback
begin
PDF.RenderPageToFile(200, Page, 5, OutFile);
if PDF.LastRenderError = '' then Exit;
LogEngineFailure('pdfium', Page, PDF.LastRenderError);
end;
raise Exception.CreateFmt('Page %d failed on all available engines', [Page]);
end;
To designpointer vejer tungt her. Kæden noterer, hvorfor hvert skift skete, for en loglinje, der lyder "denne side faldt tilbage til PDFium siden version 3.7", er et regressionssignal, du gerne vil have til at tegne en kurve i overvågningen frem for at miste. Selve fallback-rækkefølgen er en politik, det er værd at vælge per arbejdsbyrde. Den indbyggede motor udrulles uden ekstra DLL'er, hvilket gør den til det rigtige første forsøg i de fleste installationer, mens dokumenter tunge på gennemsigtighedsgrupper eller usædvanlig skygning er den sædvanlige grund til, at et team overhovedet kobler en alternativ motor ind. Ingen motor er hurtigst generelt, hvilket er hele pointen med at vælge per kald: benchmark hver enkelt mod en stikprøve af dine virkelige dokumenter ved din virkelige DPI, og tag den måling op igen, hver gang motor-DLL'erne eller dokumentsammensætningen ændrer sig. Korpusset vinder diskussionen hver gang
Ud over enkeltsider: TIFF-batches og levende enhedskontekster
To naboer til kaldene per side runder værktøjskassen af. RenderAsMultipageTIFFToFile gengiver et sideintervaludtryk direkte til en flersidet TIFF, den naturlige form til arkivoverleveringer til dokumenthåndteringssystemer, der er ældre end PDF. RenderPageToDC tegner direkte på en Windows-enhedskontekst til forhåndsvisningskontroller, styret af sin egen trio af klæbrige indstillinger (SetRenderDCOffset, SetRenderDCErasePage plus beskæringstypen), som kræver samme nulstillingsdisciplin som skalafaktoren. Forhåndsvisning på skærm og gengivelse i printstien rummer nok fælder i sig selv til at fortjene en dedikeret artikel, der er linket nedenfor
Hvor man går hen herfra
Én vane er værd at tage med videre: fordi SelectRenderer træder i kraft for hvert senere kald på instansen, kan en enkelt genstridig side forsøges igen på en anden motor, mens resten af dokumentet bliver på standarden. For tegning af forhåndsvisninger, valg af printer og håndtering af DevMode kan du fortsætte med artiklen om forhåndsvisning ved udskrivning og enhedskontekst. Når gengivelser fodrer en pipeline med stort volumen over meget store filer, passer den handle-baserede tilgang i direct access-guiden naturligt sammen med gengivelse per side gennem DARenderPageToFile
Motorpakning, understøttede formater og prøvebuilds er beskrevet på PDF Library for Delphi-produktsiden