HotPDF rendert een geladen PDF-pagina naar een Delphi TBitmap via een enkele aanroep: RenderLoadedPageToBitmap(PageIndex, DPI). De functie interpreteert de inhoudsstream van de pagina en retourneert een door de aanroeper beheerde 24-bits RGB-bitmap met de door u gekozen resolutie. Dit is exact wat een miniatuurstrip (thumbnail strip), een afdrukvoorbeeld of een PDF-naar-afbeelding-exportpijplijn nodig heeft. Dit artikel behandelt de API en bespreekt vervolgens het deel dat een bruikbare renderer onderscheidt van speelgoed: tekst tekenen op basis van de ingesloten lettertypeprogramma's zelf in plaats van op basis van vergelijkbare systeemlettertypen
Waarom is het renderen van een PDF-pagina moeilijker dan het tekenen van een afbeelding?
Een PDF-pagina is geen afbeelding. Het is een programma: een stroom van operatoren die paden bouwen, lettertypen selecteren, kleuren instellen en glyphs plaatsen, uitgevoerd tegen het grafische model gedefinieerd in ISO 32000-1 §8. Niets in het bestand zegt hoe een pixel eruitziet. Om een bitmap te produceren, moet u dat programma uitvoeren — een actieve transformatiematrix bijhouden, een grafische-statusstack voor q/Q, een uitknip-pad (clipping path), vul- en streekkleurenruimten — en het resultaat rasteren. Daarom is "toon pagina 3 gewoon als afbeelding" een inhoudsstream-interpreter, geen bestandsformaatconversie
De renderer van HotPDF, geïntroduceerd in v2.253.0, is opgebouwd uit zes ontkoppelde eenheden die dit model weerspiegelen: een affiene matrixkern voor de PDF-transformatie-algebra [a b c d e f], een grafische-statusstack, een kleurenruimte-resolver (DeviceRGB, DeviceGray, DeviceCMYK, Indexed), een padbouwer die PDF-padoperatoren met GDI verbindt, een lettertypestatistiekenlaag die /Widths-tabellen leest voor correcte voorschotten (advances), en the interpreter die operatoren verzendt en de andere Siliconen (GDI) aanstuurt. Image XObjects doorlopen dezelfde decoderingsstack die de bibliotheek gebruikt voor extraction, dus elk afbeeldingsfilter dat HotPDF kan decoderen voor extractie — inclusief met JPXDecode gecomprimeerde JPEG 2000-afbeeldingen — verschijnt ook in de gerenderde uitvoer
Een geladen pagina renderen naar een TBitmap
RenderLoadedPageToBitmap accepteert een pagina-index (beginnend bij nul) en een DPI-waarde, waarbij 72 DPI één PDF-gebruikersruimte-eenheid koppelt aan één pixel. Het retourneert nil bij mislukking (index buiten bereik, ontbrekende bronnen) in plaats van een uitzondering te genereren, zodat een viewer een slechte pagina kan overslaan en door kan gaan. De aanroeper is eigenaar van de geretourneerde bitmap en moet deze vrijgeven
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('report.pdf') > 0 then
begin
Bmp := Pdf.RenderLoadedPageToBitmap(0, 144); // page 1 at 144 DPI
if Bmp <> nil then
try
Image1.Picture.Assign(Bmp);
finally
Bmp.Free; // caller owns the bitmap
end;
end;
finally
Pdf.Free;
end;
end;
Het DPI-argument voert het schaalwerk uit voor elk gebruikelijk scenario. Een miniatuurstrip rendert op 36 of 48 DPI en krijgt kleine, snelle bitmaps; een afdrukvoorbeeld op het scherm op 96 of 144 DPI komt overeen met de typische schermdichtheid; een exportpad op 300 DPI produceert afbeeldingen van printkwaliteit. Paginacontrole op basis van de vermelding /Rotate en de oorsprong-omkering van de /MediaBox (PDF plaatst de oorsprong linksonder, GDI linksboven) worden afgehandeld binnen de pagina-naar-apparaatmatrix, zodat een US Letter-pagina op 72 DPI terugkomt als exact 612×792 pixels met de juiste kant boven
Waarom tonen gerenderde PDF-miniaturen onjuiste glyphs?
Foutieve of bij benadering getekende glyphs in gerenderde PDF-uitvoer betekenen bijna altijd dat de renderer een systeemlettertype gebruikt in plaats van het in het bestand ingesloten lettertype. De eerste HotPDF-renderer deed precies dat: het stripte het subsetvoorvoegsel van /BaseFont (waardoor ABCDEF+Arial veranderde in Arial), vroeg GDI om een systeemlettertype met die naam en tekende de tekst daarmee. Voor een document dat Arial of Times New Roman gebruikt met een standaardcodering, ziet het resultaat er redelijk uit. Maar het is een benadering, en het gaat op specifieke manieren mis
In gesloten subset-lettertypen zijn het ergste geval. Een subset-lettertype bevat mogelijk alleen de veertig glyphs die een document daadwerkelijk gebruikt, met tekencodes die zijn toegewezen in een volgorde die specifiek is voor dat bestand — code 1 kan "T" zijn, code 2 "h", enzovoort. Een systeemlettertype weet niets van die specifieke toewijzing, dus tekst verdwijnt of komt er als volledig verkeerde tekens uit. Aangepaste coderingen, symboollettertypen, streepjescode-lettertypen en elk lettertype dat niet op de renderende machine is geïnstalleerd, mislukken op dezelfde manier. Een renderer die stopt bij de vervanging van systeemlettertypen produceert miniaturen die wel herkenbaar zijn als de pagina — totdat de pagina de lettertypen gebruikt die insluiting in de eerste plaats noodzakelijk maakten
Ingebedde glyph-rendering: tekenen vanuit het lettertypeprogramma zelf
HotPDF heeft die kloof over vijf releases (v2.268.0 tot v2.272.0) gedicht door de ingesloten lettertypeprogramma's te parsen en hun glyph-contouren af te spelen als gevulde GDI-vectorpaden. Tekst in een gerenderde pagina is nu afkomstig van dezelfde contourgegevens die een conforme viewer gebruikt, wat betekent dat subset-lettertypen, aangepaste coderingen en niet-geïnstalleerde lettertypen met hun exacte vormen renderen. De dekking is opgebouwd per lettertypetype:
Voor Type0/CIDFontType2 lettertypen met een ingebed TrueType-programma (FontFile2), parseert de renderer de tabellen glyf en loca rechtstreeks: kwadratische contouren worden geconverteerd naar de kubische Béziers die GDI begrijpt, impliciete on-curve punten tussen opeenvolgende off-curve punten worden gerecommuniceerd, en samengestelde glyphs worden recursief afgespeeld. Zowel Identity als expliciete CIDToGIDMap stream-lay-outs worden ondersteund, en CID-voorschotten respecteren de breedte-items /W and /DW, zodat twee-byte Identity-H tekst correct stapt
CFF-programma's (FontFile3, al dan niet CIDFontType0C, Type1C, of een OpenType-wrapper) krijgen een volledige Type 2 charstring-interpreter: lijnen, curven, the flex-familie, hintmaskers en lokale/globale subroutine-aanroepen met de juiste subroutine-bias. CID-gecodeerde CFF-programma's koppelen tekencodes via de charset van het lettertype, wat belangrijk is voor subset-lettertypen waarvan de glyph-volgorde verschilt van de CID-volgorde, en per-glyph font-DICT-selectie via FDArray/FDSelect wordt gerespecteerd. Eenvoudige (nicht-CID) TrueType-lettertypen lossen een-byte codes op via de eigen cmap-tabel van het ingesloten lettertype met een robuuste subtabelkaten — Unicode-formaten 4 en 12 eerst, dan symboolsubtabellen met de F000 private-use spiegel, en vervolgens verouderde Macintosh-formaten — terwijl eenvoudige Type1 lettertypen worden opgelost via de ingebouwde codering van het CFF-programma
Twee verfijningen maken het beeld compleet. Ten eerste worden /Encoding-woordenboeken van eenvoudige lettertypen opgelost volgens de prioriteit die ISO 32000-1 §9.6.6 voorschrijft: /Differences-arrays overrulen de basiscodering, die de eigen map van het lettertypeprogramma overrulet — het pad waarvan TeX- en PostScript-afgeleide toolchains afhankelijk zijn, waarbij glyph-namen worden opgelost via de Adobe Glyph List, de CFF-charset of the TrueType-cmap. Ten tweede worden Type3-lettertypen, waarvan de glyphs zelf kleine inhoudsstreams zijn, via de renderer afgespeeld waarbij de lettertypematrix, lettergrootte en tekstmatrix zijn samengesteld; glyph-ruimte /Widths worden geïnterpreteerd via de /FontMatrix zoals vereist door ISO 32000-1 §9.6.5, en glyph-procedures die een d1-begrenzingskader declareren worden daartoe afgeknipt, zodat een misvormde streepjescode-glyph niet buiten zijn cel kan tekenen. Wanneer een code niet kan worden gekoppeld — een beschadigd programma, een niet-gekoppeld teken — valt de renderer voor die glyph terug op het tekenen van het systeemlettertype in plaats van het weglaten van het tekstblok
Hoe maakt u herhaald renderen snel?
Het antwoord dat HotPDF levert, is een recentelijk gebruikte paginacache: RenderLoadedPageToBitmapCached bewaart tot RenderCacheCapacity gerenderde pagina's (standaard 8) met de pagina-index en DPI als sleutel, en een cache-treffer retourneert een nieuwe, door de aanroeper beheerde kopie zonder de inhoudsstream aan te raken — typisch duizenden keren sneller dan het opnieuw interpreteren van de pagina. Dat patroon sluit exact aan bij viewers: een gebruiker die schakelt tussen twee pagina's, of een formaatwijziging die dezelfde pagina op dezelfde DPI opnieuw opvraagt, raakt elke keer de cache
// Thumbnail strip: first pass renders, scrolling back hits the cache
for I := 0 to ThumbCount - 1 do
begin
Bmp := Pdf.RenderLoadedPageToBitmapCached(I, 48);
if Bmp <> nil then
try
ThumbList.AddThumbnail(I, Bmp);
finally
Bmp.Free;
end;
end;
// After editing a loaded page in place:
Pdf.InvalidateRenderedPageCache; // next render reflects the change
Wees eerlijk over het geheugenbeslag voordat u de capaciteit verhoogt. Een US Letter-pagina op 300 DPI is 2550×3300 pixels, ongeveer 25 MB als een 24-bits bitmap, dus acht gecachte pagina's op exportresolutie beslaan ongeveer 200 MB. Bij miniatuur-DPI kosten dezelfde acht items ruim minder dan een megabyte. Dimensioneer RenderCacheCapacity voor de DPI waarop u daadwerkelijk cachet, en roep InvalidateRenderedPageCache aan na elke in-place bewerking — de cache heeft alleen de pagina en DPI als sleutel en kan niet zien dat de onderliggende inhoud is gewijzigd. Het laden van een nieuw document wist de cache automatisch
Een tweede cache werkt onder de paginacache: gedecodeerde afbeelding-XObjects worden bewaard in een geheugengebonden opslag begrensd door ImageCacheMaxBytes (standaard 32 MB) met verwerping op basis van minst recent gebruikt (LRU). Een logo of briefhoofdafbeelding die op elke pagina wordt herhaald, decodeert eenmaal per documentlading in plaats van eenmaal per Do-operator, wat de rendertijd voor pagina's met gedeelde afbeeldingen ruwweg halveert en de TIFF-export van meerdere pagina's in gelijke mate versnelt. InvalidateRenderedPageCache wist deze cache ook
Wat nog steeds bij benadering wordt gerenderd
De renderer richt zich op de algemene subset van document-PDF's, en het is de moeite waard om te weten waar de grenzen liggen. CalRGB, Lab, en ICC-gebaseerde kleurenruimten worden bij benadering berekend in plaats van via kleurbeheer — apparaatkleurenruimten, Indexed-paletten en gesamplede Type 0 kleurenlookups worden verwerkt, maar een printproductiebestand dat vertrouwt op ICC-weergave-intents zal niet colorimetrisch exact zijn. Schaduwpatronen (sh) en mengmodi buiten eenvoudige alfa vallen eveneens buiten het bereik, en Form XObject-recursie is diepte-beperkt als cyclusbeveiliging. Voor facturen, rapporten, contracten en formulieren — pagina's bestaande uit tekst, paden en afbeeldingen — is de uitvoer getrouw; voor een ontwerpproef vol gradiënten en transparantiegroepen moet u de bitmap als een voorbeeld beschouwen, niet als een proef
De praktische les: als uw pijplijn documenten genereert met HotPDF of typische zakelijke PDF's consumeert, RenderLoadedPageToBitmap round-trips ze met de exacte ingesloten glyph-vormen, correcte CID-voorschotten en de juiste paginageometrie. De benaderingen bevinden zich in de hoeken van het grafische model die zakelijke documenten zelden bezoeken
RenderLoadedPageToBitmap, zijn gecachte variant en de hier beschreven renderingpijplijn voor ingebedde glyphs worden geleverd als onderdeel van de HotPDF Component voor Delphi en C++Builder — een native VCL-bibliotheek zonder externe DLL-afhankelijkheden, die PDF-creatie, bewerking, tekstextractie en paginarendering in één pakket dekt