Műszaki cikk

PDF-oldalak renderelése Bitmappé Delphi-ben a HotPDF segítségével

A HotPDF egyetlen hívással rendereli a betöltött PDF-oldalt Delphi TBitmap objektummá: RenderLoadedPageToBitmap(PageIndex, DPI); a függvény értelmezi az oldal tartalomfolyamát, és a hívó által megadott felbontású, 24 bites RGB bitmapet ad vissza, ami pontosan az, amire egy bélyegkép-sávnak, nyomtatási előnézetnek vagy egy PDF-kép exportálási folyamatnak szüksége van; ez a cikk végigvezeti Önt az API-n, majd azon a részen, ami megkülönbözteti a használható renderelőt a játékszertől: a szöveg rajzolása magukból a beágyazott betűtípus-programokból történik, ahelyett, hogy hasonló rendszer-betűtípusokat használna

Miért nehezebb egy PDF-oldal renderelése, mint egy kép kirajzolása?

A PDF-oldal nem kép; ez egy program: operátorok olyan adatfolyama, amelyek útvonalakat építenek, betűtípusokat választanak ki, színeket állítanak be és karaktereket helyeznek el, az ISO 32000-1 §8-ban meghatározott grafikai modell szerint végrehajtva; a fájlban semmi sem mondja meg, hogyan néz ki egy képpont; a bitmap előállításához futtatnia kell ezt a programot — karban kell tartania az aktuális transzformációs mátrixot, a grafikai állapot vermét a q/Q számára, a vágási útvonalat, a kitöltési és kontúr színtéreket — és rasztereznie kell az eredményt; ezért az „egyszerűen jelenítse meg a 3. oldalt képként” valójában egy tartalomfolyam-értelmező, nem pedig fájlformátum-konverzió

A HotPDF v2.253.0-ban bevezetett renderelője hat különálló egységként épül fel, amelyek ezt a modellt tükrözik: egy affin-mátrix mag a PDF [a b c d e f] transzformációs algebrájához, egy grafikai állapot verem, egy színtér-feloldó (DeviceRGB, DeviceGray, DeviceCMYK, Indexed), egy útvonalépítő, amely összeköti a PDF útvonal-operátorokat a GDI-vel, egy betűmetrika-réteg, amely a /Widths tömböket olvassa be a helyes elmozdulásokhoz, és az értelmező, amely elosztja az operátorokat és meghajtja a másik ötöt; a kép-XObject-ek ugyanazon a dekódolási vermen mennek keresztül, amelyet a könyvtár a kinyeréshez használ, így minden olyan képszűrő, amelyet a HotPDF dekódolni tud a kinyeréshez — beleértve a JPXDecode-tömörített JPEG 2000 képeket is — megjelenik a renderelt kimenetben

Betöltött oldal renderelése TBitmap-pé

A RenderLoadedPageToBitmap egy nulla alapú oldalindexet és egy DPI értéket vesz fel, ahol a 72 DPI egy PDF felhasználói térbeli egységet egy képpontnak feleltet meg; sikertelenség esetén (tartományon kívüli index, hiányzó erőforrások) nil értéket ad vissza kivétel dobása helyett, így a megjelenítő kihagyhatja a hibás oldalt és folytathatja a munkát; a hívó birtokolja a visszaadott bitmapet, és fel kell szabadítania azt

var
  Pdf: THotPDF;
  Bmp: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('report.pdf') > 0 then
    begin
      Bmp := Pdf.RenderLoadedPageToBitmap(0, 144);  // 1. oldal 144 DPI felbontásban
      if Bmp <> nil then
      try
        Image1.Picture.Assign(Bmp);
      finally
        Bmp.Free;  // a hívó birtokolja a bitmapet
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

A DPI argumentum elvégzi a méretezési munkát minden gyakori forgatókönyvhöz; a bélyegkép-sáv 36 vagy 48 DPI-vel renderelődik, így kicsi, gyors bitmap-eket kapunk; a képernyős előnézet 96 vagy 144 DPI-vel illeszkedik a tipikus kijelző-sűrűséghez; a 300 DPI-s exportálási útvonal nyomtatási minőségű képeket készít; a /Rotate bejegyzésből származó oldalforgatás és a /MediaBox kezdőpontjának megfordítása (a PDF a kezdőpontot a bal alsó sarokba helyezi, a GDI a bal felső sarokba) a lap-eszköz mátrixon alapszik, így egy US Letter méretű oldal 72 DPI-nél pontosan 612×792 képpontként érkezik vissza, a megfelelő tájolásban

Miért mutatnak a renderelt PDF-bélyegképek rossz karaktereket?

A renderelt PDF kimenetben megjelenő hibás vagy közelítő karakterek szinte mindig azt jelentik, hogy a renderelő rendszer-betűtípussal helyettesíti a fájlba beágyazott betűtípust; az első HotPDF renderelő pontosan ezt tette: eltávolította a részhalmaz előtagot a /BaseFont-ból (az ABCDEF+Arial-t Arial-lá alakítva), lekérte a GDI-től az ilyen nevű rendszer-betűtípust, és azzal rajzolta meg a szöveget; a standard kódolással Arialt vagy Times New Romant használó dokumentumok esetében az eredmény közelítőleg megfelelőnek tűnik; de ez csak egy közelítés, és jól meghatározható esetekben elromlik

A részhalmazként beágyazott betűtípusok jelentik a legrosszabb esetet; a részhalmaz betűtípus csak azt a negyven karaktert tartalmazhatja, amelyet a dokumentum valóban használ, a karakterkódokat pedig a fájlhoz tartozó egyedi sorrendben rendelik hozzá — az 1-es kód lehet a „T”, a 2-es a „h” és így tovább; a rendszer-betűtípus semmit sem tud erről az egyedi hozzárendelésről, így a szöveg vagy eltűnik, vagy teljesen rossz karakterekként jelenik meg; az egyedi kódolások, szimbólum-betűtípusok, vonalkód-betűtípusok és minden olyan betűkészlet, amely nincs telepítve a renderelő gépre, ugyanígy elbukik; az a renderelő, amely megáll a rendszer-betűtípusok helyettesítésénél, olyan bélyegképeket készít, amelyek felismerhetően az oldalt mutatják — egészen addig, amíg az oldal nem használja azokat a betűtípusokat, amelyek miatt a beágyazás eleve szükségessé vált

Beágyazott karakterek renderelése: rajzolás magából a betűtípus-programból

A HotPDF öt kiadáson keresztül (v2.268.0-tól v2.272.0-ig) hidalta át ezt a szakadékot a beágyazott betűtípus-programok értelmezésével, és karakterkörvonalaik kitöltött GDI vektoros útvonalként történő visszajátszásával; a renderelt oldalon lévő szöveg most már ugyanazokból a körvonaladatokból származik, amelyeket a szabványnak megfelelő megjelenítő használ, ami azt jelenti, hogy a részhalmaz-betűtípusok, az egyedi kódolások és a nem telepített betűkészletek a pontos alakjukkal jelennek meg; a lefedettséget betűtípus-típusonként alakították ki:

A beágyazott TrueType programmal (FontFile2) rendelkező Type0/CIDFontType2 betűtípusok esetében a renderelő közvetlenül értelmezi a glyf és loca táblákat: a másodfokú kontúrokat a GDI által érthető harmadfokú Bézier-görbékké alakítja át, rekonstruálja az egymást követő görbén kívüli pontok közötti implicit görbén lévő pontokat, és rekurzívan visszajátssza az összetett karakterket; támogatottak mind az Identity, mind a kifejezett stream CIDToGIDMap elrendezések, és a CID elmozdulások tiszteletben tartják a /W és /DW szélességi bejegyzéseket, így a kétbájtos Identity-H szöveg megfelelően lépked

A CFF programok (FontFile3, legyen az CIDFontType0C, Type1C vagy OpenType csomagolás) teljes Type 2 karaktersorozat-értelmezőt kapnak: vonalakat, görbéket, a flex családot, a segédjelölő maszkokat (hint masks), valamint a helyi/globális szubrutinhívásokat a megfelelő szubrutin-eltolással; a CID-kulcsos CFF programok a karakterkódokat a betűtípus karakterkészletén keresztül képezik le, ami fontos az olyan részhalmaz-betűtípusoknál, amelyek karakterrendje eltér a CID-rendtől, és a FDArray/FDSelect-en keresztüli karakterenkénti betűtípus-DICT kiválasztást is tiszteletben tartja; az egyszerű (nem CID) TrueType betűtípusok az egybájtos kódokat a beágyazott betűtípus saját cmap tábláján keresztül oldják fel egy robusztus rész-tábla lánccal — először a 4-es és 12-es Unicode formátumokat, majd a szimbólum rész-táblákat az F000 magánhasználatú tükörrel, végül a régi Macintosh formátumokat —, míg az egyszerű Type1 betűtípusok a CFF program beépített kódolásán keresztül oldódnak fel

Két finomítás teszi teljessé a képet; először is, az egyszerű betűtípusok /Encoding szótárai az ISO 32000-1 §9.6.6 által előírt prioritás szerint oldódnak fel: a /Differences tömbök felülírják az alapkódolást, ami felülírja a betűtípus-program saját térképét — ez az az út, amelytől a TeX és PostScript-alapú eszközláncok függnek, a karakternevek feloldása pedig az Adobe Glyph List-en, a CFF karakterkészleten vagy a TrueType cmap-en keresztül történik; másodszor, a Type3 betűtípusok, amelyek karakterei maguk is kis tartalomfolyamok, a renderelőn keresztül kerülnek visszajátszásra a betűmátrixszal, a betűmérettel és a szövegmátrixszal együtt; a karaktertérbeli /Widths értékeket az ISO 32000-1 §9.6.5 követelményeinek megfelelően a /FontMatrix-on keresztül értelmezi, és a d1 befoglaló keretet deklaráló karaktereljárások erre vágódnak le, így a hibás vonalkód-karakter nem rajzolhat a celláján kívülre; ha egy kód nem képezhető le — sérült program, leképezetlen karakter —, a renderelő rendszer-betűtípus használatára lép vissza az adott karakternél ahelyett, hogy elhagyná a szövegfuttatást

Hogyan tehetők gyorssá az ismételt renderelések?

A HotPDF válasza erre egy legutóbb használt oldal-gyorsítótár: a RenderLoadedPageToBitmapCached legfeljebb RenderCacheCapacity (alapértelmezés szerint 8) renderelt oldalt tart fenn oldalindex és DPI szerint indexelve, és a gyorsítótár-találat egy új, a hívó tulajdonában lévő másolatot ad vissza a tartalomfolyam érintése nélkül — jellemzően ezerszer gyorsabban, mint az oldal újraértelmezése; ez a minta pontosan illeszkedik a megjelenítőkhöz: a felhasználó két oldal között váltogat, vagy egy átméretezési esemény ugyanazt az oldalt kéri le ugyanazon a DPI felbontáson, a gyorsítótárat találja el minden alkalommal

// Bélyegkép-sáv: az első lépés renderel, a visszagörgetésnél a gyorsítótárból olvassa
for I := 0 to ThumbCount - 1 do
begin
  Bmp := Pdf.RenderLoadedPageToBitmapCached(I, 48);  // 1. oldal 48 DPI felbontásban
  if Bmp <> nil then
  try
    ThumbList.AddThumbnail(I, Bmp);
  finally
    Bmp.Free;
  end;
end;

// A betöltött oldal helyben történő szerkesztése után:
Pdf.InvalidateRenderedPageCache;  // a következő renderelés tükrözi a változást

Legyen tisztában a memóriaköltséggel, mielőtt növelné a kapacitást; egy US Letter méretű oldal 300 DPI felbontásnál 2550×3300 képpont, ami körülbelül 25 MB méretű 24 bites bitmapként, így nyolc gyorsítótárazott oldal exportálási felbontásban nagyjából 200 MB-ot foglal el; bélyegkép DPI felbontásnál ugyanez a nyolc bejegyzés jóval egy megabájt alatt marad; méretezze a RenderCacheCapacity-t a ténylegesen gyorsítótárazott DPI-hez, és hívja meg az InvalidateRenderedPageCache metódust minden helyben végzett szerkesztés után — a gyorsítótár csak oldal és DPI szerint van indexelve, és nem látja, hogy a mögöttes tartalom megváltozott-e; az új dokumentum betöltése automatikusan törli a gyorsítótárat

Egy második gyorsítótár is működik az oldal-gyorsítótár alatt: a dekódolt kép-XObject-eket egy bájt-alapú tárban tartja, amelyet a ImageCacheMaxBytes (alapértelmezés szerint 32 MB) határol, a legkevésbé használt elemek kiloltásával; a minden oldalon ismétlődő logó vagy fejléckép dokumentumbetöltésenként egyszer dekódolódik a Do operátoronkénti dekódolás helyett, ami nagyjából felére csökkenti a közös képeket tartalmazó oldalak renderelési idejét, és ugyanolyan mértékben gyorsítja a többoldalas TIFF-exportálást; az InvalidateRenderedPageCache ezt a gyorsítótárat is törli

Ami továbbra is csak közelítőleg renderelődik

A renderelő az elterjedt dokumentum-PDF részhalmazt célozza meg, és érdemes tudni, hol vannak a határok; a CalRGB, Lab és ICC-alapú színtér-kezelés inkább közelített, mintsem színkezelt — a közvetlen színtér, az indexelt paletták és a mintavételezett Type 0 szín-LUT keresések támogatottak, de az ICC renderelési szándékokra hagyatkozó nyomdai fájlok nem lesznek színmetrikailag pontosak; az árnyékolási minták (sh) és az egyszerű alfán túli keverési módok szintén kívül esnek a hatókörön, és a Form XObject rekurzió mélysége korlátozott a végtelen ciklusok megelőzése érdekében; a számlák, jelentések, szerződések és űrlapok esetében — amelyek szövegből, útvonalakból és képekből állnak — a kimenet hű; a színátmenetekkel és transzparenscsoportokkal teli tervezési tervek esetében a bitmapet tekintse előnézetnek, nem pedig próbanyomatnak (proof)

A gyakorlati olvasat: ha a munkafolyamata dokumentumokat hoz létre a HotPDF segítségével, vagy tipikus üzleti PDF-eket dolgoz fel, a RenderLoadedPageToBitmap pontosan a beágyazott karakterkörvonalakkal, a helyes CID elmozdulásokkal és a helyes oldalgeometriával adja vissza őket; a közelítések a grafikai modell azon sarkaiban találhatók, amelyeket az üzleti dokumentumok ritkán érintenek

A RenderLoadedPageToBitmap, annak gyorsítótárazott változata és az itt leírt beágyazott karaktereket renderelő folyamat a Delphi és C++Builder rendszerekhez készült HotPDF Component részeként érhető el — ez egy natív VCL könyvtár, külső DLL függőségek nélkül, amely a PDF-ek létrehozását, szerkesztését, szövegének kinyerését és oldalainak renderelését egyetlen csomagban fedi le