Renderiranje PDF stranice u Windows kontekst uređaja (device context) za pregled ispisa postavlja tri koordinatna sustava u istu liniju koda, a oni se rijetko podudaraju. PDF stranica se mjeri u točkama (points) s ishodištem u donjem lijevom kutu. Zaslonski DC mjeri se u pikselima s ishodištem u gornjem lijevom kutu i faktorom zumiranja koji sami odaberete. DC pisača, onaj koji bi pregled trebao predvidjeti, mjeri piksele u razlučivosti uređaja, ali postavlja svoje ishodište u kut ispisnog područja, a ne u kut lista papira. Pogriješite li bilo koji od njih, pregled će izgledati u redu dok će ispisana stranica ispasti pomaknuta, skalirana ili izrezana duž ruba. Uobičajeni simptom je obrazac s obrubom koji se u pregledu prikazuje centrirano, ali se ispisuje s odrezanim gornjim i lijevim linijama, jer laserski pisač ne može nanijeti tintu na vanjskih nekoliko milimetara, a to pregledu nitko nije dojavio. losLab PDF Library (PDFlibPas) pokriva cijeli put s pozivima za renderiranje u kontekst uređaja, konfiguracijskim slojem virtualnog pisača i bitmape pregleda generiranim iz vlastitih metričkih podataka pisača, što je dio koji pregled čini točnim u vezi s tom marginom.
Geometrija papira nije geometrija ispisnog područja
Dva pravokutnika opisuju bilo koje odredište ispisa, a odmak između njih je mjesto gdje živi većina bugova u pregledu. Pravokutnik papira je fizički list. Pravokutnik ispisnog područja je manje područje koje mehanizam za ispis stvarno može dosegnuti, uvučeno za hardversku marginu koja se razlikuje ovisno o modelu pisača, a ponekad i o ladici. Ispisni sloj knjižnice mjeri oboje. Temeljna klasa TPLPrinter izlaže PageWidth i PageHeight za ispisno područje, FullPageWidth i FullPageHeight za cijeli list te PrintOffsetX s PrintOffsetY za razmak između njihovih ishodišta, sve u pikselima uređaja pri razlučivosti koju prijavljuje GetDPI. Točan pregled skalira te iste brojeve na razlučivost zaslona umjesto da iscrtava stranicu u bilo koji pravokutnik koji kontrola slučajno ima. Preskočite taj korak i pregled će tiho pretpostaviti marginu nula, što je jedina vrijednost koju nijedan stvarni pisač ne koristi.
Pregled na zaslonu putem RenderPageToDC
Za kontrolu pregleda na zaslonu, RenderPageToDC(DPI, Page, DC) crta stranicu učitanog dokumenta izravno na bilo koji GDI kontekst uređaja, bilo da se radi o platnu TPaintBox, izvanzaslonskoj bitmapi ili metafile DC-u. Argument DPI postavlja zumiranje. Vrijednost 96 približno odgovara prikazu od 100% na klasičnom zaslonu, a njezino udvostručenje udvostručuje veličinu renderiranja.
procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
// these three are sticky library state, not per-call parameters:
FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
FPdf.SetRenderDCErasePage(1);
FPdf.SetRenderCropType(0);
FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;Zamka je u tome što je DC staza renderiranja upravljana trajnim stanjem knjižnice, a ne parametrima po pozivu. Metode SetRenderDCOffset, SetRenderDCErasePage i SetRenderCropType ostaju aktivne dok ih nešto ne promijeni, tako da petlja sličica (thumbnail loop) koja se izvodi nakon što je korisnik prilagodio zumirani prikaz nasljeđuje onaj pomak ili izrezivanje koje je ostavila prethodna staza koda. Simptom je pregled koji odstupa samo u specifičnim sekvencama navigacije, što je iznimno teško reproducirati. Postavljanje svih relevantnih stanja na vrhu rukovatelja crtanja (paint handler), kao što je prikazano gore, ne košta ništa i uklanja cijelu klasu bugova. U blizini se krije i drugi množitelj. Efektivna izlazna razlučivost je renderirana ljestvica pomnožena s DPI argumentom, i iako je zadana vrijednost za SetRenderScale 1.0, ona također ostaje aktivna nakon promjene, pa značajka izvoza koja ju je povećala tiho mijenja razmjer svakog kasnijeg pregleda dok se ne vrati na zadano.
Klizni preglednici i djelomična ponovna iscrtavanja imaju namjensku varijantu. RenderPageToDCClip prima specifikaciju isječka (clip specification) zajedno s kontekstom uređaja, tako da poništavanje jedne trake prozora ponovno iscrtava samo tu traku umjesto ponovnog rasteriziranja cijele stranice. Pri velikom zumiranju na stranicama velikog formata to čini razliku između preglednika koji prati klizač i onog koji kasni za njim.
Zadatak ispisa koji odgovara pregledu
Ispisna strana radi putem virtualnog pisača. NewCustomPrinter klonira sistemski pisač u privatnu konfiguraciju knjižnice, a SetupPrinter prilagođava taj klon bez diranja DevMode-a na razini sustava: papir se postavlja kao postavka 1 (konstanta DMPAPER_*), a orijentacija kao postavka 11. Prednost je izolacija. Servis može ispisivati A4 naljepnice dok zadani pisač računala ostaje na formatu Letter, a nakon toga ništa ne treba vraćati na prethodne postavke.
var
Pdf: TPDFlib;
Virt: WideString;
Opt: Integer;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile('report.pdf', '') <> 1 then
raise Exception.Create('load failed');
Virt := Pdf.NewCustomPrinter(Pdf.GetDefaultPrinterName);
Pdf.SetupPrinter(Virt, 1, 9); // setting 1 = paper, DMPAPER_A4
Pdf.SetupPrinter(Virt, 11, 1); // setting 11 = orientation, 1 = portrait
Opt := Pdf.PrintOptions(1, 1, 'Monthly Report'); // fit to paper, auto-rotate + center
Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
finally
Pdf.Free;
end;
end;PrintOptions zaslužuje pažljivo čitanje. Vraća rukovatelj opcijama (options handle) koji morate proslijediti u PrintDocument ili PrintPages; it is not ambient state. Izgradnja opcija i potom zaboravljanje prosljeđivanja rukovatelja rezultira tihim neuspjehom. Zadatak se ispisuje sa zadanim postavkama i nitko ne primjećuje dok se ne očekuje pravilo prilagodbe papiru (fit-to-paper), a prevelika stranica ispadne izrezana. Argument skaliranja stranice je mjesto gdje to pravilo živi. Bez skaliranja čuva se točnost dimenzija, što je važno za obrasce koji se mjere ravnalom. Prilagodba papiru (Fit-to-paper) skalira sve prema listu. Smanjivanje velikih stranica (Shrink-large-pages) ostavlja normalne stranice nepromijenjenima i djeluje samo kada stranica premaši ispisno područje, što je obično ispravna zadana opcija za mješoviti skup dokumenata. Zastavica za automatsko rotiranje i centriranje rukuje pejzažnim stranicama bez potrebe za drugom stazom koda.
Aplikacije koje već upravljaju s TPrinter putem toka VCL dijaloga mogu ga predati izravno. PrintDocumentToPrinterObject and PrintPagesToPrinterObject prihvaćaju konfiguriranu instancu TPrinter, što zadržava standardni dijaloški okvir ispisa kao konfiguracijsko sučelje okrenuto korisniku dok knjižnica obrađuje renderiranje stranice. Miješanje ova dva pristupa u jednoj stazi koda obično ponovno uvodi odstupanje geometrije koje je ovaj rad trebao ukloniti, stoga odaberite jedan. Put virtualnog pisača odgovara uslugama bez nadzora, dok put TPrinter odgovara interaktivnim aplikacijama.
Selektivni izlaz radi na isti način. PrintPages prima niz raspona (range string), pa prosljeđivanje naziva virtualnog pisača, '2-5,12', i rukovatelja opcijama ispisuje stranice od 2 do 5 i 12 uz očuvanje ugovora o geometriji, a ista sintaksa pokreće varijante ispisa u datoteku. Te varijante ispisa u datoteku su praktičan odgovor za okruženja bez nadzora u kojima nije spojen fizički uređaj: regresijsko testiranje geometrije ispisa na poslužitelju za izgradnju koji uopće nema red čekanja za upravljačke programe. Renderirajte isti dokument kroz iste opcije u datoteku pri svakoj izgradnji, pa se regresija geometrije pretvara u razliku (diff) umjesto u prijavu kupca tri tjedna kasnije.
Bitmape pregleda s vlastitim metričkim podacima pisača
Pregled renderiran na 96 DPI u odnosu na pretpostavljenu veličinu stranice daje odgovor na krivo pitanje. Prikazuje kako stranica izgleda, a ne što će ovaj pisač staviti na ovaj papir. Metoda GetPrintPreviewBitmapToString premošćuje taj jaz stvaranjem pregleda iz istog prilagođenog pisača i istog rukovatelja opcijama kao i za kasniji zadatak, tako da veličina papira, orijentacija, pravilo skaliranja, rotacija i hardverski pomak utječu na bitmapu. Ono što se vraća je ono što će list papira stvarno prikazati.
procedure ShowPrinterTruePreview(Pdf: TPDFlib; const Virt: WideString; Opt: Integer);
var
Data: AnsiString;
Strm: TMemoryStream;
Bmp: TBitmap;
begin
Data := Pdf.GetPrintPreviewBitmapToString(Virt, 1, Opt, 1200, 0);
Strm := TMemoryStream.Create;
try
Strm.WriteBuffer(PAnsiChar(Data)^, Length(Data));
Strm.Position := 0;
Bmp := TBitmap.Create;
try
Bmp.LoadFromStream(Strm);
PreviewImage.Picture.Assign(Bmp);
finally
Bmp.Free;
end;
finally
Strm.Free;
end;
end;Argument MaxDimension ograničava duži rub bitmape. Veličina od 1200 piksela ostaje oštra za dijaloški okvir pregleda i održava skromnu potrošnju memorije čak i za inženjerske crteže E-veličine, gdje bi renderiranje u punoj razlučivosti pri 600 DPI pisača zahtijevalo gigabajte.
Pamćenje korisnikovih odabira pisača
Dijaloški okviri za ispis koji zaboravljaju svoje postavke između sesija generiraju vlastite prijave podrške. Par DevMode funkcija, GetPrinterDevModeToString and SetPrinterDevModeFromString, serijalizira potpunu konfiguraciju upravljačkog programa pisača u neprozirni niz koji možete spremiti u korisničke postavke i obnoviti u sljedećoj sesiji, uključujući opcije specifične za upravljački program koje niti jedan generički API ne modelira. Trajno spremite pisač po nazivu iz GetPrinterNames, nikada po indeksu popisa. Redoslijed indeksa se mijenja svaki put kada se pisač doda ili ukloni, pa spremljeni indeks tiho pokazuje na pogrešan uređaj sljedeći put kada se popis promijeni. GetDefaultPrinterName pokriva rezervnu opciju kada zapamćeni uređaj potpuno nestane.
Odabir ladice zaokružuje priču o trajnosti. Metoda GetPrinterBins prijavljuje izvore papira koje upravljački program izlaže, što je važno za tijekove rada s memorandumom gdje se prva stranica povlači iz ladice s memorandumom, a ostatak iz običnih zaliha papira. To je pravilo za koje korisnici očekuju da ga aplikacija zapamti zajedno sa svime ostalim, a zadatak ispisa koji završi na pogrešnom papiru doživljava se kao bug čak i kada je svaki bajt PDF-a bio ispravan.
Zadržite isti pogon za pregled i ispis
Jedna posljednja odluka tiho upravlja vjernošću prikaza. Odabir mehanizma za renderiranje primjenjuje se i na zaslonska i na odredišta pisača, pa je stoga primamljivo koristiti brzi mehanizam za pregled, a precizniji za ispis. Oduprite se tome. Pokretanje pregleda i stvarnog zadatka kroz različite mehanizme ponovno uvodi upravo ono odstupanje u vjernosti prikaza koje je pregled pisača trebao ukloniti, i to na način koji se vidi samo na papiru. Kompromisi između ugrađenog, Cairo i PDFium mehanizma razmatrani su u članku renderiranje PDF-a s više mehanizama u Delphiju; odaberite jedan i koristite ga na obje strane.
Dokumenti koji su preveliki za ugodno učitavanje prije ispisa mogu se otvoriti putem staze izravnog pristupa opisane u članku spajanje, dijeljenje i izravan pristup velikim PDF datotekama, što renderira stranice u kontekst uređaja iz datotečnog rukovatelja bez izgradnje stabla dokumenta. Potpuna referenca API-ja za ispis nalazi se na stranici proizvoda losLab PDF Library za Delphi.