HotPDF renderar en inläst PDF-sida till en Delphi-TBitmap genom ett enda anrop: RenderLoadedPageToBitmap(PageIndex, DPI). Funktionen tolkar sidans innehållsström och returnerar en användarägd 24-bitars RGB-bitmap med den upplösning du väljer, vilket är exakt vad en miniatyrbildsrad, en förhandsgranskning eller en pipeline för export av PDF till bild behöver. Den här artikeln går igenom API:et, och sedan genom den del som skiljer en användbar renderare från en leksak: att rita text från de inbäddade typsnittsprogrammen själva istället för från liknande systemtypsnitt
Varför är rendering av en PDF-sida svårare än att rita en bild?
En PDF-sida är inte en bild. Det är ett program: en ström av operatorer som bygger banor, väljer typsnitt, anger färger och placerar glyfer, exekverade mot den grafikmodell som definieras i ISO 32000-1 §8. Ingenting i filen anger hur någon pixel ser ut. För att producera en bitmap måste du köra det programmet — upprätthålla en aktuell transformationsmatris, en grafik-tillståndsstack för q/Q, en urklippsbana (clipping path), fyllnings- och linjefärgrymder — och rasterisera resultatet. Det är därför ”visa bara sida 3 som en bild” är en tolkare av innehållsströmmen, inte en filformatkonvertering
HotPDF:s renderare, som introducerades i v2.253.0, är byggd som sex frikopplade enheter som speglar den modellen: en affin-matris-kärna för PDF:s transformalgebra [a b c d e f], en stack för grafik-tillstånd, en färgrymdsanalysator (DeviceRGB, DeviceGray, DeviceCMYK, Indexed), a banbyggare som bryggar över PDF:s banoperatorer till GDI, ett lager för typsnittsmetrik som läser /Widths-matriser för korrekta frammatningar samt tolkaren som skickar ut operatorer och driver de andra fem. Bild-XObjects går igenom samma avkodningsstack som biblioteket använder för extrahering, så varje bildfilter som HotPDF kan avkoda för extrahering — inklusive JPXDecode-komprimerade JPEG 2000-bilder — visas också i renderade utdata
Rendera en inläst sida till en TBitmap
RenderLoadedPageToBitmap tar ett nollbaserat sidindex och ett DPI-värde, där 72 DPI mappar en användarrymdenhet i PDF till en pixel. Den returnerar nil vid misslyckande (index utanför intervallet, saknade resurser) istället för att kasta ett undantag, så att en visare kan hoppa över en dålig sida och fortsätta. Anroparen äger den returnerade bitmap-bilden och måste frigöra den
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('report.pdf') > 0 then
begin
Bmp := Pdf.RenderLoadedPageToBitmap(0, 144); // sida 1 vid 144 DPI
if Bmp <> nil then
try
Image1.Picture.Assign(Bmp);
finally
Bmp.Free; // anroparen äger bitmap-bilden
end;
end;
finally
Pdf.Free;
end;
end;
DPI-argumentet gör skalförändringsarbetet för varje vanligt scenario. En miniatyrbildsrad renderas i 36 eller 48 DPI och får små, snabba bitmappar; en förhandsgranskning på skärmen i 96 eller 144 DPI matchar typisk bildskärmstäthet; en exportsökväg i 300 DPI producerar bilder med utskriftskvalitet. Sidrotation från /Rotate-posten och /MediaBox-ursprungsomkastningen (PDF placerar ursprunget längst ner till vänster, GDI längst upp till vänster) hanteras inuti sida-till-enhet-matrisen, så att en US Letter-sida i 72 DPI returneras som exakt 612×792 pixlar med rätt sida upp
Varför visar renderade PDF-miniatyrer fel glyfer?
Felaktiga eller ungefärliga glyfer i renderade PDF-utdata betyder nästan alltid att renderaren ersätter med ett systemtypsnitt istället för att använda typsnittet som är inbäddat i filen. Den första HotPDF-renderaren gjorde exakt så: den tog bort delmängdsprefixet från /BaseFont (vilket förvandlade ABCDEF+Arial till Arial), bad GDI om ett systemtypsnitt med det namnet och ritade texten med det. För ett dokument som använder Arial eller Times New Roman with standard encoding, the result looks close. But it is an approximation, and it breaks in well-defined ways
Delmängdsinbäddade typsnitt är det värsta fallet. Ett delmängdstypsnitt kan bära på endast de fyrtio glyfer som ett dokument faktiskt använder, med teckenkoder tilldelade i en ordning som är privat för den filen — kod 1 kan vara ”T”, kod 2 ”h”, och så vidare. Ett systemtypsnitt vet ingenting om den privata tilldelningen, så texten antingen försvinner eller kommer ut som helt felaktiga tecken. Anpassade kodningar, symboltypsnitt, streckkodstypsnitt och alla teckensnitt som inte är installerade på renderingsdatorn misslyckas på samma sätt. En renderare som stannar vid systemtypsnittsersättning producerar miniatyrer som man känner igen som sidan — tills sidan använder de typsnitt som gjorde inbäddningen nödvändig från första början
Inbäddad glyfrendering: rita från själva typsnittsprogrammet
HotPDF stängde det gapet under fem utgåvor (v2.268.0 till v2.272.0) genom att tolka de inbäddade typsnittsprogrammen och spela upp deras glyfkonturer som fyllda GDI-vektorbanor. Text på en renderad sida kommer nu från samma konturdata som en kompatibel visare använder, vilket innebär att delmängdstypsnitt, anpassade kodningar och oinstallerade teckensnitt renderas med sina exakta former. Täckningen byggdes upp efter typsnittstyp:
För Type0/CIDFontType2-typsnitt med ett inbäddat TrueType-program (FontFile2) tolkar renderaren tabellerna glyf och loca direkt: kvadratiska konturer konverteras till de kubiska Bézier-kurvor GDI förstår, underförstådda punkter på kurvan mellan på varandra följande punkter utanför kurvan rekonstrueras, och sammansatta glyfer spelas upp rekursivt. Både Identity och explicita CIDToGIDMap-layouter i strömmen stöds, och CID-frammatningar respekterar breddposterna /W och /DW, så att två-byte Identity-H-text stegas korrekt
CFF-program (FontFile3, vare sig det är CIDFontType0C, Type1C eller ett OpenType-omslag) får en fullständig Type 2 charstring-tolkare: linjer, kurvor, flex-familjen, antydningsmasker (hint masks) och lokala/globala subrutinanrop med rätt subrutinbias. CID-kodade CFF-program mappar teckenkoder genom typsnittets teckenuppsättning (charset), vilket är viktigt för delmängdstypsnitt vars glyfordning skiljer sig från CID-ordningen, och val av font-DICT per glyf via FDArray/FDSelect respekteras. Enkla (icke-CID) TrueType-typsnitt löser upp en-byteskoder via det inbäddade typsnittets egna cmap-tabell med en robust kedja av deltabeller — Unicode-format 4 och 12 först, sedan symbol-deltabeller med F000-speglingen för privat bruk, och därefter äldre Macintosh-format — medan enkla Type1-typsnitt löser upp sig via CFF-programmets inbyggda kodning
Två förfiningar kompletterar bilden. För det första löses /Encoding-ordböcker för enkla typsnitt upp enligt den prioritet ISO 32000-1 §9.6.6 föreskriver: /Differences-matriser åsidosätter baskodningen, som i sin tur åsidosätter typsnittsprogrammets egna mappning — den väg TeX- och PostScript-härledda verktygskedjor är beroende av, där glyfnamn löses upp via Adobe Glyph List, CFF-charset eller TrueType-cmap. För det andra spelas Type3-typsnitt, vars glyfer i sig är små innehållsströmmar, upp via renderaren med sammansatt typsnittsmatris, typsnittsstorlek och textmatris; glyfrymdens /Widths tolkas via /FontMatrix enligt kraven i ISO 32000-1 §9.6.5, och glyfprocedurer som deklarerar en d1-avgränsningsbox klipps till den, så att en felaktig streckkodsglyf inte kan ritas utanför sin cell. När en kod inte kan mappas — ett skadat program, ett omappat tecken — faller renderaren tillbaka på att rita systemtypsnitt för den glyfen istället för och släppa textkörningen
Hur gör du upprepade renderingar snabba?
Svaret HotPDF levererar är en senast använda-sidcache: RenderLoadedPageToBitmapCached behåller upp till RenderCacheCapacity renderade sidor (standard 8) indexerade efter sidindex och DPI, och en cache-träff returnerar en ny kopia som ägs av anroparen utan att röra innehållsströmmen — vanligtvis tusentals gånger snabbare än att tolka om sidan. Det mönstret passar visare perfekt: en användare som växlar mellan två sidor, eller en storleksändringshändelse som begär samma sida igen vid samma DPI, träffar cachen varje gång
// Miniatyrbildsrad: första steget renderar, att bläddra tillbaka träffar cachen
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;
// Efter redigering av en inläst sida på plats:
Pdf.InvalidateRenderedPageCache; // nästa render reflekterar ändringen
Var ärlig med minneskostnaden innan du höjer kapaciteten. En US Letter-sida vid 300 DPI är 2550×3300 pixlar, ungefär 25 MB som en 24-bitars bitmap-bild, så åtta cachelagrade sidor i exportupplösning tar upp cirka 200 MB. Vid miniatyrbilds-DPI kostar samma åtta poster långt under en megabyte. Dimensionera RenderCacheCapacity efter den DPI du faktiskt cachelagrar vid, och anropa InvalidateRenderedPageCache efter varje redigering på plats — cachen indexeras endast efter sida och DPI, och den kan inte se om det underliggande innehållet ändrades. Inläsning av ett nytt dokument tömmer den automatiskt
En andra cache fungerar under sidcachen: avkodade bild-XObjects sparas i ett byte-budgeterat lager begränsat av ImageCacheMaxBytes (standard 32 MB) med rensning enligt senast använda (LRU). En logotyp eller brevhuvudsbild som upprepas på varje sida avkodas en gång per dokumentinläsning istället för en gång per Do-operator, vilket ungefär halverar renderingstiden för sidor med delade bilder och snabbar upp export till flersidiga TIFF:ar i motsvarande grad. InvalidateRenderedPageCache rensar även denna cache
Vad som fortfarande renderas ungefärligt
Renderaren riktar in sig på delmängden av vanliga dokument-PDF-filer, och det är värt att veta var gränserna går. CalRGB-, Lab- och ICC-baserade färgrymder approximeras snarare än färgstyrs — enhetsfärgrymder, indexerade paletter och samplade färgtabellssökningar av typ 0 hanteras, men en tryckproduktionsfil som förlitar sig på ICC-renderingsavsikter kommer inte att vara exakt färgmetriskt. Skuggningsmönster (sh) och blandningslägen utöver enkelt alpha faller likaså utanför ramarna, och rekursion för Form XObject är djupbegränsad som ett cykelskydd. För fakturor, rapporter, kontrakt och formulär — sidor gjorda av text, banor och bilder — är utdata trogen; för ett designprov fullt av toningar och transparensgrupper bör bitmappen betraktas som en förhandsgranskning, inte ett provtryck
Den praktiska tolkningen: om din pipeline genererar dokument med HotPDF eller konsumerar typiska affärs-PDF-filer, kommer RenderLoadedPageToBitmap att gå fram och tillbaka med exakta inbäddade glyfformer, korrekta CID-frammatningar och korrekt sidgeometri. Approximationerna finns i de hörn av grafikmodellen som affärsdokument sällan besöker
RenderLoadedPageToBitmap, dess cachelagrade variant och renderingspipelinen för inbäddade glyfer som beskrivs här levereras som en del av HotPDF Component för Delphi och C++Builder — ett inbyggt VCL-bibliotek utan externa DLL-beroenden, som täcker skapande, redigering, textextrahering och sidrendering i ett och samma paket