Drie rasterizers kunnen hetzelfde PDF-bestand lezen en het oneens zijn over wat erin staat. De ingebouwde engine in PDF Library for Delphi wordt zonder extra bestanden geleverd en rendert alles degelijk, waardoor hij de standaardpositie verdient. Cairo brengt een andere transparantie- en anti-aliasing-pijplijn mee en is vaak de keuze wanneer zachte maskers of overvloeimodi elders verkeerd uitkomen. PDFium bevat de rendercode van Chrome, dus een pagina die er in een browser goed uitziet, ziet er doorgaans ook onder PDFium goed uit, tegen de prijs van een flinke DLL en een bitness die exact moet overeenkomen. Geen van de drie is in het abstracte correct. Correctheid is documentgebonden en de enige eerlijke manier om te leren welke engine een bepaald corpus aankan, is dat corpus door elk van hen te voeren
Daarom moet u de engine als een runtimekeuze behandelen in plaats van als een keuze bij het bouwen. PDF Library for Delphi, de PDF-bibliotheek voor Delphi en C++Builder van losLab, plaatst alle drie achter één renderoppervlak, zodat de beslissing één geheel getal kost in plaats van een codevertakking. De rest komt neer op veilig tussen de engines kiezen, bevestigen welke engines een geïmplementeerd binair bestand werkelijk bevat en voorkomen dat de renderstatus stilletjes de volgende taak vergiftigt
Drie rasterizers achter één aanroepoppervlak
De bibliotheek nummert haar engines. Engine 1 is de ingebouwde renderer, de standaard, met GDI+-afvlakkingsopties op Windows. Engine 2 is Cairo en engine 3 is PDFium; beide worden tijdens runtime geselecteerd via SelectRenderer. De twee externe engines laden uit DLL's waarvan u de paden opgeeft met SetCairoFileName en SetPDFiumFileName voordat u ze selecteert. Welke engine ook actief is, het werk verloopt via dezelfde aanroepen: RenderPageToFile, RenderPageToStream, RenderDocumentToFile. Het wisselen van engine verandert één getal; de rest van uw rendercode merkt daar niets van
Het bestemmingsmodel reikt veel verder dan bitmaps. De rendererklasse richt zich ook op metabestanden (WMF, EMF, EMF+), EPS, directe device contexts, printers en HTML5, waarbij Cairo en PDFium alleen als extra bestemmingen verschijnen wanneer ze zijn meegecompileerd. Rasteruitvoer is waar de drie engines het zichtbaarst uiteenlopen, dus dat gebruiken de voorbeelden hier
Ga nooit uit van het bestaan van een engine: onderzoek bij opstarten
Cairo en PDFium zijn functies voor conditionele compilatie, wat betekent dat een binair bestand volledig zonder hen kan zijn gebouwd. Wanneer dat gebeurt, veroorzaakt het vragen om engine 2 of 3 niets. SelectRenderer retourneert eenvoudig een andere waarde dan de gevraagde ID, en code die de retourwaarde negeert, blijft renderen met de al actieve engine. De verdediging is een opstartonderzoek dat elke engine vraagt zichzelf te identificeren en het antwoord vastlegt:
function ProbeEngines(PDF: TPDFlib): string;
begin
Result := 'built-in'; // engine 1 is altijd aanwezig
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); // herstel de standaard voordat het echte werk begint
end;
Voer dat onderzoek één keer bij het opstarten uit en schrijf het resultaat naast elke rendertaak in het logboek. De meest voorkomende vraag wanneer een klant een renderverschil meldt, is welke engines hun installatie werkelijk heeft, en een antwoord van één regel in het logboek beslecht die vraag zonder remote-desktopsessie. Een nuttig neveneffect: als SetPDFiumFileName zelf 0 retourneert, weet u al dat de DLL het probleem is (verkeerd pad, verkeerde bitness, een ontbrekende afhankelijkheid) in plaats van een binair bestand dat zonder PDFium-ondersteuning is gecompileerd, omdat de padaanroep niets kon oplossen voordat SelectRenderer ooit draaide
Tien uitvoerformaten achter één Options-geheel getal
De parameter Options bij de renderaanroepen kiest de uitvoercodering: 0 is BMP, 1 JPEG, 2 WMF, 3 EMF, 4 EPS, 5 PNG, 6 GIF, 7 TIFF, 8 EMF+ en 9 HTML5. PNG (5) is de verstandige standaard voor voorvertoningen en gearchiveerde pagina-afbeeldingen. JPEG (1), gecombineerd met SetJPEGQuality, is beter voor fotografische scans waarbij bestandsgrootte belangrijker is dan scherpe randen
Eén formaat verbergt een vereiste voor de doelstream. Het BMP-pad schrijft eerst de afbeeldingsgegevens en zoekt daarna terug naar offset 0x26 om de resolutievelden in de header bij te werken. Richt u dit op een alleen-vooruit-stream, een compressiewrapper of een netwerksocket, dan mislukt de aanroep op een manier die op een enginefout lijkt maar dat niet is. Wanneer een niet-zoekbaar doel onvermijdelijk is, render dan PNG, of laat de BMP eerst via een geheugenstream lopen en kopieer hem vooruit zodra hij voltooid is
De DPI die u doorgeeft is niet de DPI die u krijgt
Elke renderaanroep neemt een DPI-argument, maar de resolutie die u werkelijk krijgt is die waarde vermenigvuldigd met de globale renderschaal. SetRenderScale begint op 1.0, en zodra u deze verandert, geldt de nieuwe factor stilletjes voor elke latere render op die instantie:
PDF.SetRenderScale(2.0); // elke latere render wordt verdubbeld
PDF.RenderPageToFile(150, 1, 5, 'p1.png'); // effectief 300 DPI
PDF.SetRenderScale(1.0); // reset, anders komen uw thumbnails enorm binnen
Dezelfde hardnekkigheid geldt voor SetRenderCropType en de JPEG-kwaliteitsinstelling. In een service die thumbnails, voorvertoningen en afbeeldingen met printresolutie uit één gedeelde instantie produceert, zijn deze achtergebleven instellingen de werkelijke oorzaak achter het incidentele ticket "thumbnails zijn plotseling 40 MB". Er zijn twee nette uitwegen: reset de relevante status aan het begin van elke bewerking, of reserveer een afzonderlijke instantie voor elk uitvoerprofiel zodat niets tussen taken door lekt
De standaardengine afstellen voordat u naar een andere grijpt
Een verrassend groot deel van de verzoeken "we hebben een andere engine nodig" blijken instellingenproblemen met een vermomming. De ingebouwde renderer maakt zijn afvlakkingsgedrag toegankelijk via SetGDIPlusOptions en de bredere SetRenderOptions-familie, en met SetGDIPlusFileName kunt u hem richten op een specifieke GDI+-runtime wanneer een implementatieomgeving een ongebruikelijke versie levert. Kartelige lijntekeningen bij lage DPI, wazige tekst in thumbnails, strepen over verlopen: ze reageren allemaal op die knoppen, en eraan draaien kost niets in het installatieprogramma. Cairo of PDFium toevoegen betekent daarentegen meer DLL's leveren, een tweede of derde bitness-variant bijhouden en de verplichting op u nemen om die bij te werken
Een kwaliteitsklacht heeft dus een natuurlijke volgorde van handelingen. Reproduceer deze eerst op de exacte DPI en schaal van de klant, want de helft van de tijd verdwijnt het verschil zodra die overeenkomen. Probeer daarna de afvlakkingsopties van de ingebouwde engine. Zet pas dan de pagina naast elkaar in engines 1, 2 en 3 met elke andere variabele constant: render deze via elke engine naar PNG op identieke DPI en voeg alle drie toe. Gewoonlijk stemmen twee van de drie overeen, en die meerderheid vertelt u of de uitschieter het document anders interpreteert of dat uw eigen basisverwachting niet klopt. Drie concrete afbeeldingen beslechten een geschil over "rendert verkeerd" veel sneller dan een alinea bijvoeglijke naamwoorden
Een fallbackketen die zichzelf verklaart
Zodra onderzoek en statusdiscipline aanwezig zijn, is de fallbackketen zelf kort. Het detecteren van een fout steunt op LastRenderError, dat de eigen berichttekst van de engine voor de meest recente render bevat en leeg is wanneer de render slaagde:
procedure RenderPageWithFallback(PDF: TPDFlib; Page: Integer; const OutFile: string);
begin
PDF.SelectRenderer(1); // eerst de ingebouwde
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 als de zware 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;
Twee ontwerppunten wegen hier zwaar. De keten legt vast waarom elke wissel plaatsvond, omdat een logregel met "deze pagina viel sinds release 3.7 terug op PDFium" een regressiesignaal is dat u in monitoring wilt volgen in plaats van verliezen. De fallbackvolgorde zelf is beleid dat u per werklast moet kiezen. De ingebouwde engine wordt zonder extra DLL's geïmplementeerd, wat hem in de meeste installaties de juiste eerste poging maakt, terwijl documenten met veel transparantiegroepen of ongebruikelijke schaduwen de gebruikelijke reden zijn waarom een team überhaupt een alternatieve engine aansluit. Geen enkele engine is in het algemeen het snelst; dat is precies het punt van per aanroep kiezen: benchmark elk ervan op een steekproef van uw echte documenten op uw echte DPI, en herhaal die meting wanneer de engine-DLL's of de documentmix veranderen. Het corpus wint de discussie, elke keer weer
Verder dan losse pagina's: TIFF-batches en live device contexts
Twee buren van de aanroepen per pagina maken de gereedschapskist compleet. RenderAsMultipageTIFFToFile rendert een paginabereikexpressie rechtstreeks naar een meerpagina-TIFF, de natuurlijke vorm voor archiefoverdrachten aan documentbeheersystemen die ouder zijn dan PDF. RenderPageToDC tekent rechtstreeks op een Windows-device context voor voorvertoningscontrols, bestuurd door zijn eigen drietal hardnekkige instellingen (SetRenderDCOffset, SetRenderDCErasePage, plus het croptype) die dezelfde resetdiscipline nodig hebben als de schaalfactor. Renderen voor schermvoorvertoning en het printpad hebben elk genoeg eigen valkuilen om een afzonderlijk artikel te rechtvaardigen, waarnaar hieronder wordt gelinkt
Waar u hierna naartoe gaat
Eén gewoonte die het waard is om mee te nemen: omdat SelectRenderer voor elke latere aanroep op de instantie effect heeft, kan één hardnekkige pagina op een andere engine worden herprobeerd terwijl de rest van het document op de standaard blijft. Ga voor voorvertoningstekenen, printerselectie en DevMode-afhandeling verder met het artikel over afdrukvoorbeeld en device context. Wanneer renders een hoogvolume-pijplijn voor zeer grote bestanden voeden, past de handlegebaseerde aanpak in de direct-access-handleiding vanzelfsprekend bij rendering per pagina via DARenderPageToFile
Engineverpakking, ondersteunde formaten en proefbuilds staan beschreven op de PDF Library for Delphi-productpagina