Három raszterelő olvashatja ugyanazt a PDF-et, és mégis vitatkozhatnak azon, mit is mond. A PDF Library for Delphi beépített motorja az, amely extra fájlok nélkül érkezik és mindent hozzáértően kirajzol, ezért érdemli ki az alapértelmezett helyet. A Cairo más átlátszósági és élsimítási munkafolyamatot hoz, és jellemzően akkor nyúlnak hozzá, amikor a lágy maszkok vagy a keverési módok máshol rosszul jönnek ki. A PDFium a Chrome rajzolókódját viszi, tehát az az oldal, amely böngészőben jól néz ki, PDFium alatt is jól néz ki általában — cserébe egy tekintélyes DLL-t kér, és ragaszkodik a bitszélesség egyezéséhez. Elvont értelemben egyik sem helyes a három közül. A helyesség dokumentumfüggő, és az egyetlen őszinte mód annak megtudására, melyik motor bírja el az adott korpuszt, ha átfuttatja azt mindegyiken
Ez az érv amellett, hogy a motort futásidejű, ne fordítási idejű döntésnek tekintsük. A PDF Library for Delphi, a losLab Delphihez és C++Builderhez készült PDF-könyvtára, mind a hármat egyetlen rajzolási felület mögé teszi, így a döntés egyetlen egész számba kerül egy kódelágazás helyett. A többi arról szól, hogyan válasszon köztük biztonságosan, hogyan igazolja, mely motorokat hordozza valójában egy telepített bináris, és hogyan akadályozza meg, hogy a rajzolási állapot csendben megmérgezze a következő feladatot
Három raszterelő egyetlen hívási felület mögött
A könyvtár számozza a motorjait. Az 1-es motor a beépített rajzoló, az alapértelmezés, Windows alatt GDI+ simítási beállításokkal. A 2-es motor a Cairo, a 3-as a PDFium, mindkettő futásidőben választható a SelectRenderer hívással. A két külső motor olyan DLL-ekből töltődik be, amelyek elérési útját a SetCairoFileName és a SetPDFiumFileName hívással adja meg a kiválasztásuk előtt. Bármelyik motor is aktív, a munka ugyanazokon a hívásokon megy át: RenderPageToFile, RenderPageToStream, RenderDocumentToFile. A motorváltás egyetlen szám mozgatása; a rajzolási kódja többi része észre sem veszi
A célmodell messze túlmutat a bitképeken. A rajzolóosztály metafájlokat (WMF, EMF, EMF+), EPS-t, közvetlen eszközkörnyezeteket, nyomtatókat és HTML5-öt is céloz, a Cairo és a PDFium pedig csak akkor jelenik meg további célként, ha befordították őket. A raszteres kimenet az, ahol a három motor a leglátványosabban szétválik, ezért az itteni példák ezt használják
Soha ne feltételezze, hogy egy motor létezik: vizsgálja meg induláskor
A Cairo és a PDFium feltételes fordítási szolgáltatás, ami azt jelenti, hogy egy bináris teljesen nélkülük is elkészülhet. Ha ez történik, a 2-es vagy 3-as motor kérése nem vált ki semmit. A SelectRenderer egyszerűen a kért azonosítótól eltérő értéket ad vissza, és az a kód, amely figyelmen kívül hagyja a visszatérési értéket, továbbra is azzal a motorral rajzol, amely már aktív volt. A védekezés egy indulási vizsgálat, amely minden motort megkér, hogy azonosítsa magát, és rögzíti a választ:
function ProbeEngines(PDF: TPDFlib): string;
begin
Result := 'built-in'; // az 1-es motor mindig jelen van
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); // az alapértelmezés visszaállítása a valódi munka előtt
end;
Futtassa ezt a vizsgálatot egyszer induláskor, és írja az eredményét a naplóba minden rajzolási feladat mellé. A leggyakoribb kérdés, amikor egy ügyfél rajzolási eltérést jelent, az, hogy a telepítése valójában mely motorokkal rendelkezik, és egy egysoros válasz a naplóban ezt távoli asztali munkamenet nélkül tisztázza. Hasznos mellékhatás: ha maga a SetPDFiumFileName ad vissza 0 értéket, akkor már tudja, hogy a DLL a gond (rossz útvonal, rossz bitszélesség, hiányzó függőség), nem pedig egy PDFium-támogatás nélkül fordított bináris, mert az útvonalhívás semmit sem oldott fel, még mielőtt a SelectRenderer egyáltalán lefutott volna
Tíz kimeneti formátum egyetlen Options egész szám mögött
A rajzolási hívások Options paramétere választja ki a kimeneti kódolást: 0 a BMP, 1 a JPEG, 2 a WMF, 3 az EMF, 4 az EPS, 5 a PNG, 6 a GIF, 7 a TIFF, 8 az EMF+, 9 pedig a HTML5. Az 5-ös PNG az ésszerű alapértelmezés előnézetekhez és archiválási oldalképekhez. Az 1-es JPEG a SetJPEGQuality hívással párosítva jobb választás fényképszerű beolvasásokhoz, ahol a fájlméret többet számít, mint az éles kontúrok
Az egyik formátum követelményt rejt a célfolyammal szemben. A BMP-út először a képadatokat írja ki, majd visszaugrik a 0x26 eltoláshoz, hogy megfoltozza a fejléc felbontásmezőit. Irányítsa ezt egy csak előre haladó folyamra, egy tömörítő burkolóra vagy egy hálózati aljzatra, és a hívás úgy hal el, hogy az motorhibának olvasódik, pedig nem az. Ha a nem pozicionálható cél elkerülhetetlen, rajzoljon inkább PNG-t, vagy állomásoztassa a BMP-t egy memóriafolyamon, és másolja tovább, ha elkészült
Az átadott DPI nem az a DPI, amit kap
Minden rajzolási hívás kap egy DPI argumentumot, de a ténylegesen kapott felbontás ez az érték a globális rajzolási méretaránnyal megszorozva. A SetRenderScale 1,0 értékről indul, és ha egyszer megváltoztatja, az új szorzó némán érvényesül az adott példány minden későbbi rajzolására:
PDF.SetRenderScale(2.0); // minden későbbi rajzolás megkétszereződik
PDF.RenderPageToFile(150, 1, 5, 'p1.png'); // gyakorlatilag 300 DPI
PDF.SetRenderScale(1.0); // állítsa vissza, különben óriási bélyegképeket kap
Ugyanez a ragadósság vonatkozik a SetRenderCropType hívásra és a JPEG-minőség beállítására. Egy olyan szolgáltatásban, amely bélyegképeket, előnézeteket és nyomtatási felbontású képeket állít elő egyetlen közös példányból, ezek az ottfelejtett beállítások állnak valójában az alkalmankénti „a bélyegképek hirtelen 40 MB-osak” hibajegy mögött. Két tiszta kiút van: vagy visszaállítja az érintett állapotot minden művelet elején, vagy külön példányt szentel minden kimeneti profilnak, hogy semmi ne szivárogjon át közöttük
Hangolja az alapértelmezett motort, mielőtt másikhoz nyúlna
A „másik motorra van szükségünk” kérések meglepően nagy hányadáról derül ki, hogy álruhába bújt beállítási gondok. A beépített rajzoló a SetGDIPlusOptions hívással és a tágabb SetRenderOptions családdal teszi elérhetővé a simítási viselkedését, a SetGDIPlusFileName pedig lehetővé teszi, hogy egy adott GDI+ futtatókörnyezetre irányítsa, amikor a telepítési környezet szokatlant szállít. Recés vonalas ábrák alacsony DPI-n, homályos szöveg a bélyegképeken, sávosodás a színátmenetekben: mindegyik reagál ezekre a gombokra, és a tekergetésük semmibe sem kerül a telepítőben. A Cairo vagy a PDFium hozzáadása ezzel szemben több DLL szállítását, egy második vagy harmadik bitszélességű változat követését és a frissítésük kötelezettségét jelenti
Egy minőségi panasznak tehát természetes műveleti sorrendje van. Először reprodukálja az ügyfél pontos DPI-jén és méretarányán, mert az esetek felében az eltérés elpárolog, amint ezek egyeznek. Utána próbálja ki a beépített motor simítási beállításait. Csak ezután állítsa egymás mellé az oldalt a motorokon át, minden más változót rögzítve: rajzolja ki PNG-be az 1-es, a 2-es és a 3-as motorral azonos DPI-n, és csatolja mind a hármat. A háromból általában kettő egyetért, és ez a többség árulja el, hogy a kilógó eset a dokumentum eltérő értelmezése-e, vagy a saját kiindulási elvárása téves. Három konkrét kép sokkal gyorsabban zárja le a „rosszul rajzol” vitát, mint egy bekezdésnyi jelző
Tartaléklánc, amely megmagyarázza magát
Ha a vizsgálat és az állapotfegyelem a helyén van, maga a tartaléklánc rövid. A hiba észlelése a LastRenderError értékén múlik, amely a legutóbbi rajzolásra vonatkozóan a motor saját üzenetszövegét tartalmazza, és üres, ha a rajzolás sikerült:
procedure RenderPageWithFallback(PDF: TPDFlib; Page: Integer; const OutFile: string);
begin
PDF.SelectRenderer(1); // először a beépített
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 // a PDFium a nehézsúlyú tartalék
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;
Két tervezési szempont nyom itt a latban. A lánc rögzíti, miért történt az egyes váltások, mert az a naplósor, amely szerint „ez az oldal a 3.7-es kiadás óta a PDFiumra esik vissza”, olyan regressziós jelzés, amelyet inkább a felügyeletben szeretne trendként látni, mint elveszíteni. Maga a tartalék sorrendje munkaterhelésenként megválasztandó irányelv. A beépített motor extra DLL nélkül telepszik, ami a legtöbb telepítésben helyes első próbává teszi, míg az átlátszósági csoportokkal vagy szokatlan árnyalásokkal terhelt dokumentumok a szokásos ok arra, hogy egy csapat egyáltalán alternatív motort drótozzon be. Általánosságban egyik motor sem a leggyorsabb, és éppen ez a hívásonkénti választás lényege: mérje meg mindegyiket a valódi dokumentumainak egy mintáján, a valódi DPI-jén, és nézze meg újra ezt a mérést, valahányszor a motor-DLL-ek vagy a dokumentumösszetétel megváltozik. A korpusz mindig megnyeri a vitát
Az egyes oldalakon túl: TIFF-kötegek és élő eszközkörnyezetek
Az oldalankénti hívások két szomszédja teszi teljessé az eszköztárat. A RenderAsMultipageTIFFToFile egy oldaltartomány-kifejezést közvetlenül többoldalas TIFF-be rajzol, ami a természetes forma az archiválási átadásokhoz olyan dokumentumkezelő rendszerek felé, amelyek megelőzik a PDF-et. A RenderPageToDC közvetlenül egy Windows-eszközkörnyezetre fest előnézeti vezérlők számára, és a saját három ragadós beállítása (SetRenderDCOffset, SetRenderDCErasePage, plusz a vágási típus) irányítja, amelyek ugyanazt a visszaállítási fegyelmet igénylik, mint a méretarány. A képernyős előnézet és a nyomtatási úton történő rajzolás elég saját csapdát hordoz ahhoz, hogy külön cikket érdemeljen, amelyre lentebb hivatkozunk
Merre tovább?
Egy szokás, amelyet érdemes magával vinnie: mivel a SelectRenderer a példány minden későbbi hívására érvénybe lép, egyetlen makacs oldal újrapróbálható másik motoron, miközben a dokumentum többi része az alapértelmezésen marad. Az előnézeti festéshez, a nyomtatóválasztáshoz és a DevMode kezeléséhez folytassa a nyomtatási előnézetről és az eszközkörnyezetről szóló cikkel. Ha a rajzolás nagy áteresztésű munkafolyamatot lát el nagyon nagy fájlokon, a közvetlen eléréses útmutató leíró alapú megközelítése természetesen párosul a DARenderPageToFile oldalankénti rajzolásával
A motorok csomagolását, a támogatott formátumokat és a próbaverziókat a PDF Library for Delphi termékoldala részletezi