Å rendre en PDF-side på en Windows-enhetskontekst for forhåndsvisning ved utskrift setter tre koordinatsystemer i samme kodelinje, og de er sjelden enige. PDF-siden måles i punkter med origo nederst til venstre. Skjerm-DC-en måles i piksler med origo øverst til venstre og en zoomfaktor du velger. Skriver-DC-en, den forhåndsvisningen skal forutsi, måler piksler ved enhetsoppløsningen, men plasserer origo i hjørnet av det utskrivbare området, ikke i hjørnet av arket. Bommer du på ett av dem, ser forhåndsvisningen fin ut mens den utskrevne siden kommer forskjøvet, skalert eller beskåret langs en kant. Det vanlige symptomet er et skjema med ramme som forhåndsvises sentrert og skrives ut med topp- og venstrelinjen skåret bort, fordi laserskriveren ikke får blekk ut i de ytterste millimeterne og ingen fortalte forhåndsvisningen det. losLab PDF Library (PDF Library for Delphi) dekker hele veien med rendringskall mot enhetskontekst, et konfigurasjonslag for virtuelle skrivere og forhåndsvisningsbilder generert fra skriverens egne mål, og det er den delen som gjør forhåndsvisningen ærlig om den margen
Papirgeometri er ikke utskrivbar geometri
To rektangler beskriver ethvert utskriftsmål, og forskyvningen mellom dem er der de fleste forhåndsvisningsfeilene bor. Papirrektangelet er det fysiske arket. Det utskrivbare rektangelet er det mindre området utskriftsmotoren faktisk når, trukket inn av en maskinvaremarg som varierer mellom skrivermodeller og noen ganger mellom skuffer. Bibliotekets utskriftslag måler begge. Den underliggende klassen TPLPrinter eksponerer PageWidth og PageHeight for det utskrivbare området, FullPageWidth og FullPageHeight for hele arket, og PrintOffsetX sammen med PrintOffsetY for gapet mellom origoene deres, alt i enhetspiksler ved den oppløsningen GetDPI melder. En ærlig forhåndsvisning skalerer de samme tallene ned til skjermoppløsning i stedet for å male siden inn i det rektangelet kontrollen tilfeldigvis har. Hopper du over det trinnet, antar forhåndsvisningen stilltiende null marg, som er den ene verdien ingen virkelig skriver bruker
Skjermforhåndsvisning gjennom RenderPageToDC
For en forhåndsvisningskontroll på skjermen tegner RenderPageToDC(DPI, Page, DC) en side av det lastede dokumentet rett på en hvilken som helst GDI-enhetskontekst, enten det er et TPaintBox-lerret, en punktgrafikk utenfor skjermen eller en metafil-DC. DPI-argumentet setter zoomen. 96 tilnærmer en 100 %-visning på en klassisk skjerm, og en dobling av tallet dobler den rendrede størrelsen
procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
// disse tre er klebrig bibliotekstilstand, ikke parametre per kall:
FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
FPdf.SetRenderDCErasePage(1);
FPdf.SetRenderCropType(0);
FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;
Fellen er at DC-rendringsveien styres av klebrig bibliotekstilstand, ikke av parametre per kall. SetRenderDCOffset, SetRenderDCErasePage og SetRenderCropType vedvarer hver for seg til noe endrer dem, så en miniatyrbildesløyfe som kjører etter at brukeren justerte den zoomede visningen, arver den forskyvningen eller beskjæringen den forrige kodeveien la igjen. Symptomet er en forhåndsvisning som driver bare i bestemte navigasjonssekvenser, omtrent så elendig å reprodusere som en feil kan bli. Å sette all relevant tilstand øverst i tegnebehandleren, som ovenfor, koster ingenting og fjerner hele klassen. En annen multiplikator gjemmer seg like ved. Den effektive utdataoppløsningen er rendringsskalaen ganget med DPI-argumentet, og selv om SetRenderScale har 1,0 som standard, vedvarer også den når den først er endret, så en eksportfunksjon som skrudde den opp, skalerer stilltiende om hver senere forhåndsvisning til noe setter den tilbake
Visninger med rulling og delvise omtegninger har en egen variant. RenderPageToDCClip tar en klippespesifikasjon sammen med enhetskonteksten, slik at det å ugyldiggjøre ett bånd av vinduet bare tegner det båndet om i stedet for å rasterisere hele siden på nytt. Ved høy zoom på sider i storformat er det forskjellen på en visning som følger rullefeltet, og en som smører seg ut bak det
En utskriftsjobb som stemmer med forhåndsvisningen
Utskriftssiden virker gjennom en virtuell skriver. NewCustomPrinter kloner en systemskriver inn i en bibliotekprivat konfigurasjon, og SetupPrinter justerer den klonen uten å røre den maskinomfattende DevMode: papir går inn som innstilling 1 (en DMPAPER_*-konstant) og retning som innstilling 11. Gevinsten er isolasjon. En tjeneste kan skrive ut A4-etiketter mens vertens standardskriver blir stående på Letter, og ingenting må gjenopprettes etterpå
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); // innstilling 1 = papir, DMPAPER_A4
Pdf.SetupPrinter(Virt, 11, 1); // innstilling 11 = retning, 1 = stående
Opt := Pdf.PrintOptions(1, 1, 'Monthly Report'); // tilpass til papir, autoroter + sentrer
Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
finally
Pdf.Free;
end;
end;
PrintOptions fortjener en nøye lesing. Den returnerer et opsjonshåndtak du må sende videre til PrintDocument eller PrintPages; det er ikke omgivende tilstand. Å bygge opsjonene og så glemme å sende håndtaket feiler stille. Jobben skrives ut med standardverdier, og ingen merker det før en policy om tilpasning til papir var forventet, og en overdimensjonert side kom ut beskåret i stedet. Argumentet for sideskalering er der den policyen bor. Ingen skalering bevarer dimensjonsnøyaktighet, som betyr noe for skjemaer som blir målt mot en linjal. Tilpass til papir skalerer alt om til arket. Krymp store sider lar normale sider være i fred og griper først inn når en side går utenfor det utskrivbare området, som vanligvis er det rette standardvalget for et blandet dokumentsett. Flagget for autorotering og sentrering håndterer liggende sider uten en egen kodevei
Applikasjoner som allerede styrer en TPrinter gjennom VCL-dialogflyten, kan levere den direkte videre. PrintDocumentToPrinterObject og PrintPagesToPrinterObject tar imot den konfigurerte TPrinter-instansen, noe som beholder standard utskriftsdialog som den brukervendte konfigurasjonsflaten mens biblioteket håndterer siderendringen. Å blande de to tilnærmingene i én kodevei pleier å gjeninnføre nettopp den geometridriften resten av dette arbeidet skulle fjerne, så velg én. Veien med virtuell skriver passer for tjenester uten tilsyn; TPrinter-veien passer for interaktive applikasjoner
Selektiv utskrift virker på samme måte. PrintPages tar en områdestreng, så å sende inn navnet på den virtuelle skriveren, '2-5,12' og opsjonshåndtaket skriver ut side 2 til 5 og 12 med geometrikontrakten intakt, og den samme syntaksen driver variantene som skriver til fil. De filvariantene er det praktiske svaret for et miljø uten tilsyn og uten en fysisk enhet tilkoblet: regresjonstesting av utskriftsgeometri på en byggeserver som ikke har noen driverkø i det hele tatt. Rendr det samme dokumentet gjennom de samme opsjonene til et filartefakt ved hvert bygg, så blir en geometriregresjon en diff i stedet for en kundemelding tre uker senere
Forhåndsvisningsbilder med skriverens egne mål
En forhåndsvisning rendret ved 96 DPI mot en antatt sidestørrelse besvarer feil spørsmål. Den viser hvordan siden ser ut, ikke hva denne skriveren vil legge på dette papiret. GetPrintPreviewBitmapToString lukker det gapet ved å bygge forhåndsvisningen fra den samme egendefinerte skriveren og det samme opsjonshåndtaket som den endelige jobben, slik at papirstørrelse, retning, skaleringspolicy, rotasjon og maskinvareforskyvningen alle mater inn i punktgrafikken. Det som kommer tilbake, er det arket vil vise
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;
Argumentet MaxDimension setter tak på punktgrafikkens lange side. 1200 piksler holder seg skarpt for en forhåndsvisningsdialog og holder minnebruken beskjeden selv for tekniske tegninger i E-format, der en rendring i full oppløsning ved skriverens 600 DPI ville løpe opp i gigabyte
Å huske brukerens skrivervalg
Utskriftsdialoger som glemmer innstillingene sine mellom økter, genererer sine egne støttesaker. DevMode-paret, GetPrinterDevModeToString og SetPrinterDevModeFromString, serialiserer en skrivers fulle driverkonfigurasjon til en ugjennomsiktig streng du kan gjemme i brukerinnstillingene og gjenopprette neste økt, inkludert de driverspesifikke valgene ingen generisk API gidder å modellere. Lagre skriveren ved navn fra GetPrinterNames, aldri ved listeindeks. Rekkefølgen i listen endrer seg hver gang en skriver legges til eller fjernes, så en lagret indeks peker stilltiende på feil enhet neste gang listen forskyver seg. GetDefaultPrinterName dekker reserveløsningen når den huskede enheten har forsvunnet helt
Skuffvalg runder av historien om lagring. GetPrinterBins melder hvilke papirkilder en driver eksponerer, noe som betyr noe for arbeidsflyter med brevark der side én hentes fra brevarkskuffen og resten fra vanlig papir. Det er en policy brukerne forventer at applikasjonen husker sammen med alt annet, og en utskriftsjobb som lander på feil papir, leses som en feil selv når hver byte av PDF-en var riktig
Behold én motor på tvers av forhåndsvisning og utskrift
Én siste beslutning styrer stille troverdigheten. Valget av rendringsmotor gjelder både skjerm- og skrivermål, så fristelsen er å forhåndsvise med en rask motor og skrive ut med en presis. Motstå den. Å drive forhåndsvisningen og jobben gjennom ulike motorer gjeninnfører nøyaktig den troverdighetsdriften en skrivertro forhåndsvisning var bygd for å fjerne, og gjør det på en måte som bare viser seg på papir. Avveiningene mellom den innebygde motoren, Cairo og PDFium veies i rendring av PDF med flere motorer i Delphi; velg én og bruk den på begge sider
Dokumenter som er for store til å lastes komfortabelt før utskrift, kan åpnes gjennom veien for direkte tilgang beskrevet i sammenslåing, oppdeling og direkte tilgang for store PDF-er, som rendrer sider til en enhetskontekst fra et filhåndtak uten å bygge dokumenttreet. Den fullstendige API-referansen for utskrift finnes på produktsiden for losLab PDF Library for Delphi