Technical Article

Višestruki mehanizmi iscrtavanja PDF-a u Delphiju: Ugrađeni, Cairo i PDFium s PDFlibPas-om

Tri rasterizatora mogu čitati isti PDF i razlikovati se u tome kako ga prikazuju. Ugrađeni mehanizam u PDFlibPas-u je onaj koji se isporučuje bez dodatnih datoteka i sve iscrtava kompetentno, zbog čega dobiva zadano mjesto. Cairo donosi drugačiji cjevovod za prozirnost i zaglađivanje (anti-aliasing) i obično je onaj za kojim ljudi posežu kada meke maske ili načini miješanja (blend modes) ispadnu pogrešni drugdje. PDFium nosi Chromeov kôd za iscrtavanje, pa stranica koja izgleda dobro u pregledniku obično izgleda dobro i pod PDFium-om, po cijeni prilično velikog DLL-a i bitnosti koju inzistira uskladiti. Nijedan od ta tri mehanizma nije točan sam po sebi. Točnost ovisi o dokumentu, a jedini pošten način da saznate koji mehanizam najbolje rukuje određenim skupom dokumenata je da ga pokrenete kroz svaki od njih.

To je razlog zašto mehanizam iscrtavanja treba tretirati kao izbor u vremenu izvršavanja (runtime), a ne u vremenu izgradnje (build-time). PDFlibPas, losLab-ova PDF knjižnica za Delphi i C++Builder, stavlja sva tri mehanizma iza jedne površine za iscrtavanje, tako da odluka košta jedan cijeli broj umjesto grane koda. Ostatak se svodi na siguran odabir među njima, potvrdu koje mehanizme isporučena binarna datoteka stvarno nosi i sprječavanje da stanje iscrtavanja tiho kontaminira sljedeći posao.

Tri rasterizatora iza jedne površine poziva

Knjižnica označava svoje mehanizme brojevima. Mehanizam 1 je ugrađeni iscrtavač, zadani, s GDI+ opcijama zaglađivanja na Windowsima. Mehanizam 2 je Cairo, a mehanizam 3 je PDFium, pri čemu se oba biraju u vremenu izvršavanja pomoću SelectRenderer. Dva vanjska mehanizma se učitavaju iz DLL-ova čije staze prosljeđujete pomoću SetCairoFileName i SetPDFiumFileName prije nego što ih odaberete. Koji god mehanizam bio aktivan, posao ide kroz iste pozive: RenderPageToFile, RenderPageToStream, RenderDocumentToFile. Promjena mehanizma mijenja samo jedan broj; ostatak vašeg koda za iscrtavanje to nikada ne primjećuje.

Model odredišta doseže daleko izvan bitmapa. Klasa iscrtavača također cilja meta-datoteke (WMF, EMF, EMF+), EPS, izravne kontekste uređaja, pisače i HTML5, pri čemu se Cairo i PDFium pojavljuju kao dodatna odredišta samo kada su kompajlirani. Rasterizirani izlaz je točka u kojoj se tri mehanizma najuočljivije razlikuju, pa se u ovim primjerima koristi upravo on.

Nikada ne pretpostavljajte da mehanizam postoji: provjerite pri pokretanju

Cairo i PDFium su značajke uvjetnog prevođenja, što znači da binarna datoteka može biti izgrađena u potpunosti bez njih. Kada se to dogodi, traženje mehanizma 2 ili 3 ne podiže nikakvu pogrešku. SelectRenderer jednostavno vraća vrijednost različitu od ID-a koji ste zatražili, a kod koji zanemaruje povratnu vrijednost nastavlja iscrtavanje s mehanizmom koji je već bio aktivan. Obrana je provjera pri pokretanju koja traži od svakog mehanizma da se identificira i bilježi odgovor:

function ProbeEngines(PDF: TPDFlib): string;
begin
  Result := 'built-in';                        // engine 1 is always present
  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);                       // restore the default before real work
end;

Pokrenite tu provjeru jednom pri pokretanju i zapišite njezin rezultat u dnevnik uz svaki posao iscrtavanja. Najčešće pitanje kada korisnik prijavi razliku u iscrtavanju je koje mehanizme njegova instalacija stvarno ima, a jednolinijski odgovor u dnevniku rješava to bez sesije udaljene radne površine. Koristan nuspojava: ako sam SetPDFiumFileName vrati 0, već znate da je problem u DLL-u (pogrešna staza, pogrešna bitnost, nedostajuća ovisnost), a ne u binarnoj datoteci prevedenoj bez podrške za PDFium, jer poziv staze nije razriješio ništa prije nego što se SelectRenderer uopće pokrenuo.

Deset izlaznih formata iza jednog cijelog broja Options

Parametar Options u pozivima za iscrtavanje odabire izlazno kodiranje: 0 je BMP, 1 JPEG, 2 WMF, 3 EMF, 4 EPS, 5 PNG, 6 GIF, 7 TIFF, 8 EMF+ i 9 HTML5. PNG (5) ispunjava ulogu razumne zadane postavke za preglede i arhivske slike stranica. JPEG (1), u paru s SetJPEGQuality, bolji je izbor za fotografske skenove gdje je veličina datoteke važnija od oštrih rubova.

Jedan format skriva zahtjev o ciljnom toku. BMP staza prvo zapisuje slikovne podatke, a zatim se vraća na pomak 0x26 kako bi ispravila polja razlučivosti u zaglavlju. Usmjerite to na tok koji dopušta samo kretanje prema naprijed (forward-only), kompresijski omot ili mrežni socket, i poziv ne uspijeva na način koji izgleda kao pogreška mehanizma, ali to zapravo nije. Kada se ne-traživi cilj ne može izbjeći, umjesto toga iscrtajte PNG ili provedite BMP kroz memorijski tok i kopirajte ga prema naprijed nakon što završi.

DPI koji prosljeđujete nije onaj koji dobivate

Svaki poziv za iscrtavanje prima DPI argument, ali razlučivost koju stvarno dobivate je ta vrijednost pomnožena s globalnom skalom iscrtavanja. SetRenderScale počinje na 1.0, a nakon što ga promijenite, novi se faktor tiho primjenjuje na svako kasnije iscrtavanje na toj instanci:

PDF.SetRenderScale(2.0);                    // every later render is doubled
PDF.RenderPageToFile(150, 1, 5, 'p1.png');  // effectively 300 DPI
PDF.SetRenderScale(1.0);                    // reset, or your thumbnails arrive huge
```

Isto ljepljivo ponašanje vrijedi za SetRenderCropType i postavku kvalitete JPEG-a. U usluzi koja proizvodi sličice (thumbnails), preglede i slike u razlučivosti za ispis iz jedne zajedničke instance, ove preostale postavke su ono što zapravo stoji iza povremenih problema gdje "sličice odjednom imaju 40 MB". Dva su čista izlaza: resetirajte relevantno stanje na početku svake operacije ili posvetite zasebnu instancu svakom izlaznom profilu kako ništa ne bi curilo između njih.

Podešavanje zadanog mehanizma prije nego što posegnete za drugim

Iznenađujući udio zahtjeva "treba nam drugi mehanizam" zapravo su problemi s postavkama u krivom obliku. Ugrađeni iscrtavač izlaže svoje ponašanje zaglađivanja kroz SetGDIPlusOptions i širu obitelj SetRenderOptions, a SetGDIPlusFileName vam omogućuje da ga usmjerite na specifično GDI+ vrijeme izvršavanja kada okruženje za implementaciju isporučuje neobično. Nazubljeni crteži na niskom DPI-ju, mutan tekst u sličicama, prugasti prijelazi na gradijentima: sve to reagira na te kontrole, a njihovo podešavanje ne košta ništa u instalacijskom programu. Dodavanje Cairo ili PDFium mehanizma, nasuprot tome, znači isporuku više DLL-ova, praćenje druge ili treće bitnosti i preuzimanje obveze njihova ažuriranja.

Dakle, pritužba na kvalitetu ima prirodan redoslijed operacija. Prvo je reproducirajte na točnom DPI-ju i skali kupca, jer se pola vremena razlika izgubi čim se oni podudare. Zatim isprobajte opcije zaglađivanja ugrađenog mehanizma. Tek tada postavite stranicu jednu pored druge kroz različite mehanizme s konstantnim svim ostalim varijablama: iscrtajte je u PNG kroz mehanizme 1, 2 i 3 pri identičnom DPI-ju i priložite sve tri slike. Obično se dva od tri mehanizma slažu, a ta većina vam govori je li odstupanje u tome što se dokument tumači drugačije ili je vaša osnovna pretpostavka pogrešna. Tri konkretne slike rješavaju spor oko "pogrešnog iscrtavanja" puno brže od odlomka punog pridjeva.

Lanac pričuvnih opcija (fallback) koji sam sebe objašnjava

Nakon što su provjera i disciplina stanja uspostavljene, sam lanac pričuvnih opcija (fallback) je kratak. Otkrivanje neuspjeha oslanja se na LastRenderError, koji drži tekst poruke samog mehanizma za najnovije iscrtavanje i prazan je kada je iscrtavanje uspjelo:

procedure RenderPageWithFallback(PDF: TPDFlib; Page: Integer; const OutFile: string);
begin
  PDF.SelectRenderer(1);                            // built-in first
  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 as the heavy 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;

Dvije točke dizajna imaju težinu ovdje. Lanac bilježi zašto se svaki prelazak dogodio, jer linija dnevnika koja glasi "ova je stranica vraćena na PDFium od verzije 3.7" je signal regresije koji želite pratiti u nadzoru, a ne izgubiti. Sam redoslijed pričuvnih opcija je politika koju vrijedi odabrati po opterećenju. Ugrađeni mehanizam se implementira bez dodatnih DLL-ova, što ga čini pravim prvim pokušajem u većini instalacija, dok su dokumenti bogati grupama prozirnosti ili neobičnim sjenčanjem obično razlog zašto tim uopće uvodi alternativni mehanizam. Nijedan mehanizam nije najbrži općenito, što je i smisao odabira po pozivu: usporedite performanse svakog s uzorkom vaših stvarnih dokumenata pri vašem stvarnom DPI-ju i ponovite to mjerenje kad god se DLL-ovi mehanizama ili mješavina dokumenata promijene. Skup dokumenata uvijek dobiva raspravu.

Izvan pojedinačnih stranica: skupni TIFF poslovi i aktivni konteksti uređaja

Dva srodnika poziva po stranici zaokružuju alatni okvir. RenderAsMultipageTIFFToFile iscrtava izraz raspona stranica izravno u višestranični TIFF, što je prirodan oblik za arhivsku predaju sustavima za upravljanje dokumentima koji su stariji od PDF-a. RenderPageToDC crta izravno na Windows kontekst uređaja (DC) za kontrole pregleda, pod utjecajem vlastitog trojstva ljepljivih postavki (SetRenderDCOffset, SetRenderDCErasePage plus vrsta obrezivanja) koje trebaju istu disciplinu resetiranja kao i faktor skaliranja. Pregled na zaslonu i iscrtavanje staze za ispis nose dovoljno vlastitih zamki da zaslužuju posvećeni članak, povezan u nastavku.

Povezani članci

Ako vaš spojeni izlaz mora ostati pristupačan, pozadina stabla strukture pokrivena je u članku o pristupačnosti označenog PDF-a, koji objašnjava točno što bi Fast varijanta spajanja odbacila. Za izvlačenje sadržaja iz raspona koje podijelite, pogledajte vodič za ekstrakciju teksta, slika i fontova.

Cjeloviti popis funkcija Direct Access dolazi s knjižnicom; izdanja i probna preuzimanja nalaze se na PDFlibPas stranici proizvoda.