Tre rasteriserere kan lese den samme PDF-en og være uenige om hva den sier. Den innebygde motoren i PDF Library for Delphi er den som følger med uten ekstra filer og rendrer alt kompetent, og det er derfor den fortjener standardplassen. Cairo bringer med seg en annen pipeline for gjennomsiktighet og kantutjevning, og pleier å være den folk griper til når myke masker eller blandingsmoduser kommer galt ut andre steder. PDFium bærer Chromes rendringskode, så en side som ser riktig ut i en nettleser, ser vanligvis riktig ut under PDFium også, til prisen av en anselig DLL og en bitbredde den insisterer på å matche. Ingen av de tre er riktig i det abstrakte. Riktighet avgjøres per dokument, og den eneste ærlige måten å finne ut hvilken motor som takler et gitt korpus, er å kjøre det korpuset gjennom hver av dem
Det er argumentet for å behandle motoren som et valg ved kjøretid framfor ved byggetid. PDF Library for Delphi, PDF-biblioteket for Delphi og C++Builder fra losLab, plasserer alle tre bak én rendringsflate, slik at beslutningen koster ett heltall i stedet for en kodeforgrening. Resten koker ned til å velge trygt mellom dem, bekrefte hvilke motorer en utrullet binærfil faktisk bærer, og hindre at rendringstilstanden stilltiende forgifter neste jobb
Tre rasteriserere bak én kallflate
Biblioteket nummererer motorene sine. Motor 1 er den innebygde rendreren, standardvalget, med GDI+-utjevningsvalg på Windows. Motor 2 er Cairo og motor 3 er PDFium, begge valgt ved kjøretid gjennom SelectRenderer. De to eksterne motorene lastes fra DLL-er hvis stier du oppgir med SetCairoFileName og SetPDFiumFileName før du velger dem. Uansett hvilken motor som er aktiv, går arbeidet gjennom de samme kallene: RenderPageToFile, RenderPageToStream, RenderDocumentToFile. Å bytte motor flytter ett tall; resten av rendringskoden din merker det aldri
Målmodellen strekker seg langt forbi punktgrafikk. Rendrerklassen sikter også mot metafiler (WMF, EMF, EMF+), EPS, direkte enhetskontekster, skrivere og HTML5, der Cairo og PDFium bare dukker opp som ekstra mål når de ble kompilert inn. Rasterutdata er der de tre motorene skiller lag mest synlig, så det er det eksemplene her bruker
Anta aldri at en motor finnes: sonder ved oppstart
Cairo og PDFium er funksjoner styrt av betinget kompilering, noe som betyr at en binærfil kan bygges helt uten dem. Skjer det, utløser det ingenting å be om motor 2 eller 3. SelectRenderer returnerer bare en annen verdi enn ID-en du ba om, og kode som overser returverdien, fortsetter å rendre med den motoren som allerede var aktiv. Forsvaret er en oppstartssonde som ber hver motor identifisere seg og noterer svaret:
function ProbeEngines(PDF: TPDFlib): string;
begin
Result := 'built-in'; // motor 1 er alltid 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); // gjenopprett standarden før det virkelige arbeidet
end;
Kjør den sonden én gang ved oppstart og skriv resultatet inn i loggen ved siden av hver rendringsjobb. Det aller vanligste spørsmålet når en kunde melder om en rendringsforskjell, er hvilke motorer installasjonen deres faktisk har, og et svar på én linje i loggen avgjør det uten en fjernskrivebordsøkt. En nyttig bivirkning: returnerer SetPDFiumFileName selv 0, vet du allerede at DLL-en er problemet (feil sti, feil bitbredde, en manglende avhengighet) og ikke en binærfil kompilert uten PDFium-støtte, fordi stikallet ikke fant noe før SelectRenderer i det hele tatt kjørte
Ti utdataformater bak ett Options-heltall
Parameteren Options på rendringskallene velger utdatakodingen: 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 standardvalget for forhåndsvisninger og arkivbilder av sider. JPEG (1), sammen med SetJPEGQuality, er det bedre valget for fotografiske skanninger der filstørrelse betyr mer enn skarpe kanter
Ett format skjuler et krav til målstrømmen. BMP-veien skriver bildedataene først, og søker så tilbake til offset 0x26 for å lappe oppløsningsfeltene i filhodet. Rett den mot en strøm som bare går framover, en komprimeringsinnpakning eller en nettverkssokkel, og kallet feiler på en måte som leses som en motorfeil, men ikke er det. Når et mål uten søkemulighet ikke er til å unngå, rendr PNG i stedet, eller mellomlagre BMP-en gjennom en minnestrøm og kopier den videre når den er ferdig
DPI-en du sender inn, er ikke DPI-en du får
Hvert rendringskall tar et DPI-argument, men oppløsningen du faktisk får, er den verdien ganget med den globale rendringsskalaen. SetRenderScale starter på 1,0, og når du først endrer den, gjelder den nye faktoren stilltiende for hver senere rendring på den instansen:
PDF.SetRenderScale(2.0); // hver senere rendring dobles
PDF.RenderPageToFile(150, 1, 5, 'p1.png'); // i praksis 300 DPI
PDF.SetRenderScale(1.0); // nullstill, ellers blir miniatyrbildene enorme
Den samme klebrigheten gjelder SetRenderCropType og innstillingen for JPEG-kvalitet. I en tjeneste som produserer miniatyrbilder, forhåndsvisninger og bilder i utskriftsoppløsning fra én delt instans, er disse etterlatte innstillingene det som egentlig ligger bak den sporadiske saken om at «miniatyrbildene er plutselig 40 MB». To rene utveier: nullstill den relevante tilstanden øverst i hver operasjon, eller vie en egen instans til hver utdataprofil så ingenting lekker mellom dem
Å justere standardmotoren før du griper etter en annen
En overraskende andel av forespørslene om at «vi trenger en annen motor» viser seg å være innstillingsproblemer i forkledning. Den innebygde rendreren eksponerer utjevningsoppførselen sin gjennom SetGDIPlusOptions og den bredere SetRenderOptions-familien, og SetGDIPlusFileName lar deg peke den mot en bestemt GDI+-kjøretid når et utrullingsmiljø leverer en uvanlig en. Hakkete strektegninger ved lav DPI, uskarp tekst i miniatyrbilder, båndlegging i fargeoverganger: alt dette svarer på de rattene, og å vri på dem koster ingenting i installasjonspakken. Å legge til Cairo eller PDFium betyr derimot å levere flere DLL-er, følge en andre eller tredje bitbreddevariant og påta seg plikten til å oppdatere dem
En kvalitetsklage har altså en naturlig rekkefølge. Reproduser den først ved kundens nøyaktige DPI og skala, siden forskjellen halvparten av gangene fordamper når de stemmer overens. Prøv den innebygde motorens utjevningsvalg deretter. Først da setter du siden side om side på tvers av motorer med hver andre variabel holdt konstant: rendr den til PNG gjennom motor 1, 2 og 3 ved identisk DPI, og legg ved alle tre. Vanligvis er to av tre enige, og det flertallet forteller deg om avvikeren er dokumentet som tolkes forskjellig, eller din egen forventning som er på jordet. Tre konkrete bilder avgjør en tvist om «feil rendring» langt raskere enn et avsnitt med adjektiver
En reservekjede som forklarer seg selv
Når sondering og tilstandsdisiplin først er på plass, er selve reservekjeden kort. Å oppdage en feil hviler på LastRenderError, som holder motorens egen meldingstekst for den siste rendringen, og er tom når rendringen lyktes:
procedure RenderPageWithFallback(PDF: TPDFlib; Page: Integer; const OutFile: string);
begin
PDF.SelectRenderer(1); // den innebygde 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 reserven
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 designpoenger veier tungt her. Kjeden noterer hvorfor hvert bytte skjedde, for en loggline som lyder «denne siden falt tilbake til PDFium siden utgivelse 3.7», er et regresjonssignal du vil ha som trend i overvåkingen framfor tapt. Selve rekkefølgen i reservekjeden er en policy verdt å velge per arbeidsmengde. Den innebygde motoren rulles ut uten ekstra DLL-er, noe som gjør den til det rette første forsøket i de fleste installasjoner, mens dokumenter tunge på gjennomsiktighetsgrupper eller uvanlig skyggelegging er den vanlige grunnen til at et team i det hele tatt kobler inn en alternativ motor. Ingen motor er raskest generelt, og det er hele poenget med å velge per kall: mål hver av dem mot et utvalg av dine virkelige dokumenter ved din virkelige DPI, og gjenta den målingen hver gang motor-DLL-ene eller dokumentmiksen endrer seg. Korpuset vinner diskusjonen hver gang
Forbi enkeltsider: TIFF-batcher og levende enhetskontekster
To naboer til kallene per side runder av verktøykassen. RenderAsMultipageTIFFToFile rendrer et uttrykk for et sideområde rett inn i en flersidig TIFF, den naturlige formen for arkivoverleveringer til dokumenthåndteringssystemer som er eldre enn PDF. RenderPageToDC maler direkte på en Windows-enhetskontekst for forhåndsvisningskontroller, styrt av sin egen trio av klebrige innstillinger (SetRenderDCOffset, SetRenderDCErasePage, pluss beskjæringstypen) som trenger den samme nullstillingsdisiplinen som skalafaktoren. Forhåndsvisning på skjerm og rendring i utskriftsveien bærer nok av egne feller til å fortjene en egen artikkel, lenket nedenfor
Hvor du går videre
Én vane verdt å ta med seg: fordi SelectRenderer trer i kraft for hvert senere kall på instansen, kan én enkelt gjenstridig side prøves på nytt på en annen motor mens resten av dokumentet blir på standardvalget. For maling av forhåndsvisning, skrivervalg og DevMode-håndtering, fortsett med artikkelen om forhåndsvisning ved utskrift og enhetskontekst. Når rendringer mater en pipeline med høyt volum over svært store filer, passer den håndtaksbaserte tilnærmingen i guiden til direkte tilgang naturlig sammen med rendring per side gjennom DARenderPageToFile
Pakking av motorer, støttede formater og prøvebygg er beskrevet på produktsiden for PDF Library for Delphi