Trys skirtingi rastravimo varikliai gali perskaityti tą patį PDF failą ir nevienodai jį atvaizduoti. Integruotas „PDFlibPas“ variklis platinamas be jokių papildomų failų ir viską atvaizduoja pakankamai gerai – būtent todėl jis parinktas kaip numatytasis. „Cairo“ turi kitokį skaidrumo ir glotninimo (anti-aliasing) apdorojimą, todėl jį verta rinktis, jei švelnios kaukės (soft masks) ar maišymo režimai kitur atvaizduojami klaidingai. „PDFium“ naudoja „Chrome“ naršyklės vaizdo braižymo kodą, todėl naršyklėje gerai atrodantis puslapis paprastai teisingai atvaizduojamas ir su PDFium, tačiau tam reikalingas nemažas DLL failas, kurio bitų skaičius privalo atitikti programos tipą. Nė vienas iš šių trijų variklių nėra absoliūčiai teisingas. Atvaizdavimo teisingumas priklausos nuo konkretaus dokumento, o patikimiausias būdas sužinoti, kuris variklis geriausiai tinka jūsų dokumentų rinkiniui, yra išbandyti juos visus.
Būtent todėl atvaizdavimo variklio parinkimą verta nustatyti programos veikimo metu (runtime), o ne kompiliavimo metu. „PDFlibPas“ – „losLab“ sukurta PDF biblioteka, skirta „Delphi“ ir „C++Builder“, patalpina visus tris variklius po viena vaizdo braižymo sąsaja, todėl sprendimas atlikti pakeitimą kainuoja tik vieną sveikąjį skaičių vietoj kodo išsišakojimo. Likusi dalis susijusi su saugiu variklių perjungimu, atvaizdavimo modulių prieinamumo patikra ir užtikrinimu, kad ankstesnės užduoties nustatymai nesugadintų sekančio darbo.
Trys rastravimo varikliai po viena sąsaja
Biblioteka sunumeruoja savo variklius. 1-asis variklis yra integruotas atvaizdavimo modulis (numatytasis), turintis GDI+ glotninimo parinktis „Windows“ sistemoje. 2-asis variklis yra „Cairo“, o 3-asis – „PDFium“, abu pasirenkami programos veikimo metu per SelectRenderer. Du išoriniai varikliai įkeliami iš DLL failų, kurių kelius nurodote su SetCairoFileName ir SetPDFiumFileName prieš juos pasirinkdami. Kad ir kuris variklis būtų aktyvus, darbas atliekamas per tuos pačius iškvietimus: RenderPageToFile, RenderPageToStream, RenderDocumentToFile. Variklių perjungimas pakeičia tik vieną skaičių, o likusi jūsų atvaizdavimo kodo dalis to net nepastebi.
Išvesties modelis apima kur kas daugiau nei rastrinius paveikslėlius. Atvaizdavimo klasė taip pat palaiko metafailus (WMF, EMF, EMF+), EPS, tiesioginius įrenginių kontekstus (device contexts), spausdintuvus ir HTML5, o „Cairo“ ir „PDFium“ atsiranda kaip papildomi išvesties formatai tik tada, kai jie įtraukti kompiliavimo metu. Rastrinių vaizdų kūrimas yra ta sritis, kurioje šių trijų variklių skirtumai matomi geriausiai, todėl pavyzdžiuose naudosime būtent juos.
Niekada nemanykite, kad variklis egzistuoja: patikrinkite paleidimo metu
„Cairo“ ir „PDFium“ yra sąlyginio kompiliavimo funkcijos (conditional compilation), o tai reiškia, kad programa gali būti sukurta visiškai be jų. Tokiu atveju bandymas pasirinkti 2-ąjį arba 3-iąjį variklį nesukelia jokių klaidų. Metodas SelectRenderer tiesiog grąžina kitą reikšmę nei jūsų prašomas ID, o kodas, kuris ignoruoja grąžinamą reikšmę, toliau atvaizduoja vaizdą naudodamas anksčiau aktyvų variklis. Apsauga nuo to yra paleidimo metu atliekamas patikrinimas (probe), kuris užklausia kiekvieno variklio identifikacijos ir išsaugo atsakymą:
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;
Paleiskite šią patikrą programos starto metu ir įrašykite jos rezultatą į žurnalą šalia kiekvieno atvaizdavimo darbo. Dažniausias klausimas, kai klientas praneša apie atvaizdavimo netikslumus, yra: kokius variklius jo įdiegta programa iš tikrųjų turi? Viena eilutė žurnale atsako į šį klausimą be jokio prisijungimo prie nuotolinio darbalaukio. Naudingas šalutinis poveikis: jei funkcija SetPDFiumFileName grąžina 0, iškart sužinosite, kad problema yra DLL failas (neteisingas kelias, nesutampanti bitų versija ar trūkstama priklausomybė), o ne tai, kad programa sukurta be PDFium palaikymo, nes kelio nustatymas nepavyko dar prieš iškviečiant SelectRenderer.
Dešimt išvesties formatų po vienu Options skaičiumi
Parametras Options vaizdo braižymo funkcijose nurodo išvesties formatą: 0 – BMP, 1 – JPEG, 2 – WMF, 3 – EMF, 4 – EPS, 5 – PNG, 6 – GIF, 7 – TIFF, 8 – EMF+ ir 9 – HTML5. PNG (5) is the sensible default for previews and archival page images. JPEG (1), naudojamas kartu su SetJPEGQuality, labiau tinka nuotraukų skenavimui, kur failo dydis yra svarbesnis už itin ryškius kraštus.
Vienas iš formatų turi specifinį reikalavimą tiksliniam srautui. BMP kūrimo eiga pirmiausia įrašo paveikslėlio duomenis, o tada grįžta atgal (seek) į poslinkį 0x26, kad pakoreguotų raiškos laukus antraštėje. Nukreipkite tai į srautą, kuris leidžia rašyti tik į priekį (pavyzdžiui, suspaudimo apvalkalą ar tinklo lizdą), ir iškvietimas nepavyks – tai atrodys kaip variklio klaida, nors taip nėra. Kai nepersukamas srautas yra privalomas, geriau kurkite PNG failą arba nukreipkite BMP į atminties srautą (memory stream) ir nukopijuokite jį toliau, kai jis bus pilnai paruoštas.
Perduodamas DPI nėra tas DPI, kurį gaunate
Kiekvienas vaizdo braižymo iškvietimas priima DPI argumentą, tačiau tikroji gaunama raiška yra ši vertė, padauginta iš bendro vaizdo braižymo mastelio. Savybės SetRenderScale pradinė reikšmė yra 1.0, o ją pakeitus naujas koeficientas tyliai taikomas visiems vėlesniems atvaizdavimams šiame egzemplioriuje:
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
Toks pat išliekantis poveikis galioja ir SetRenderCropType bei JPEG kokybės nustatymui. Tarnyboje, kuri iš to paties bendro egzemplioriaus kuria miniatiūras (thumbnails), peržiūras ir spaudos raiškos vaizdus, būtent šie užsilikę nustatymai sukelia problemas, kai „miniatiūra staiga užima 40 MB“. Yra du būdai to išvengti: iš naujo nustatykite reikiamą būseną kiekvienos operacijos pradžioje arba priskirkite atskirus bibliotekos egzempliorius kiekvienam išvesties profiliui, kad nustatymai nepersiduotų kitiems darbams.
Numatytojo variklio suderinimas prieš renkantis kitą
Didelė dalis prašymų „mums reikia kito vaizdo braižymo variklio“ iš tikrųjų yra netinkamų nustatymų problemos. Integruotas atvaizdavimo modulis leidžia valdyti glotninimą per SetGDIPlusOptions ir platesnę SetRenderOptions šeimą, o SetGDIPlusFileName leidžia nukreipti jį į konkrečią GDI+ vykdymo aplinką, jei diegimo sistemoje naudojama nestandartinė versija. Kampuotos linijos esant žemam DPI, neryškus miniatiūrų tekstas, perėjimų (gradients) juostos – visa tai galima pakoreguoti šiais valdikliais, o tai nieko nekainuoja diegimo paketo dydžiui. Tuo tarpu „Cairo“ ar „PDFium“ pridėjimas reiškia daugiau platinamų DLL failų, bitų versijų sekimą ir pareigą juos nuolat atnaujinti.
Todėl nusiskundimų kokybe nagrinėjimas turi savo natūralią eigą. Pirmiausia atkurkite problemą su tiksliu kliento nurodytu DPI ir masteliu, nes pusę laiko skirtumai išnyksta jiems sutapus. Tada išbandykite integruoto variklio glotninimo parinktis. Tik po to palyginkite gautą vaizdą tarp skirtingų variklių, išlaikydami visus kitus kintamuosius vienodus: sukurkite PNG failą su 1, 2 ir 3 varikliais esant identiškam DPI ir pateikite visus tris. Paprastai du iš trijų variantų sutampa, o ši dauguma parodo, ar problema yra skirtingas dokumento interpretavimas, ar jūsų pačių pradiniai lūkesčiai. Trys konkretūs paveikslėliai išsprendžia ginčą „atvaizduoja klaidingai“ kur kas greičiau nei pastraipos su apibūdinimais.
Atsarginių variklių grandinė, kuri paaiškina savo veikimą
Kai patikra ir būsenos kontrolė suderinta, atsarginių variklių perjungimo grandinė yra trumpa. Klaidos aptikimas remiasi savybe LastRenderError, kurioje saugomas paskutinio vaizdo braižymo klaidos pranešimas (ji būna tuščia, jei darbas atliktas sėkmingai):
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;
Čia svarbūs du architektūros aspektai. Grandinė fiksuoja, kodėl įvyko kiekvienas perjungimas, nes įrašas žurnale „šiame puslapyje pritaikytas atsarginis PDFium variklis nuo versijos 3.7“ yra svarbus regresijos signalas, kurį norite matyti stebėsenos sistemoje, o ne prarasti. Pačių atsarginių žingsnių tvarka yra pasirinkimas, kurį verta pritaikyti pagal darbo pobūdį. Integruotam varikliui nereikia papildomų DLL failų, todėl jis yra tinkamas pirmasis pasirinkimas daugumoje sistemų. Tuo tarpu dokumentai su sudėtingomis skaidrumo grupėmis ar nestandartiniais šešėliais yra priežastis, kodėl komanda nusprendžia naudoti papildomus variklius. Nė visi varikliai yra greičiausi visais atvejais – būtent todėl variklį verta pasirinkti kiekvienam iškvietimui: atlikite kiekvieno iš jų spartos testą su savo dokumentų pavyzdžiais esant reikiamam DPI, ir peržiūrėkite šiuos matavimus pasikeitus DLL failams ar dokumentų tipams. Dokumentų rinkinys visada laimi argumentą.
Daugiau nei vienas puslapis: TIFF paketai ir tiesioginiai įrenginių kontekstai
Du papildomi metodai papildo vaizdo braižymo įrankių rinkinį. Metodas RenderAsMultipageTIFFToFile sukuria daugialapį TIFF failą tiesiai iš puslapių diapazono išraiškos – tai natūralus formatas perduoti dokumentus į archyvines visas, sukurtas dar prieš PDF standartą. Metodas RenderPageToDC piešia tiesiai ant „Windows“ įrenginio konteksto (device context) peržiūros valdikliams; jį valdo trys nustatymai (SetRenderDCOffset, SetRenderDCErasePage ir apkarpymo tipas), kuriems reikalinga tokia pati valymo tvarka kaip ir mastelio koeficientui. Ekrano peržiūra ir spausdinimo kelio braižymas turi pakankamai savų spąstų, kad nusipelnytų atskiro straipsnio, kurio nuoroda pateikiama žemiau.
Ką skaityti toliau
Vienas įprotis, kurį verta išlaikyti: kadangi SelectRenderer daro įtaką visiems vėlesniems to egzemplioriaus iškvietimams, vieną problemišką puslapį galima pabandyti atvaizduoti kitu varikliu, kol likusi dokumento dalis braižoma numatytuoju režimu. Norėdami sužinoti apie ekrano peržiūros piešimą, spausdintuvo parinkimą bei DevMode valdymą, skaitykite straipsnį Ekrano peržiūra ir įrenginio kontekstas. Kai atvaizdavimas vykdomas didelės apimties sistemose su dideliais failais, rankenomis paremtas metodas, aprašytas tiesioginės prieigos vadove, natūraliai dera su puslapių braižymu per DARenderPageToFile.
Variklių pakavimas, palaikomi išvesties formatai ir bandomosios versijos išsamiai aprašyti PDFlibPas produkto puslapyje.