At gengive en PDF-side på en Windows-enhedskontekst til forhåndsvisning ved udskrivning sætter tre koordinatsystemer i samme kodelinje, og de er sjældent enige. PDF-siden måles i punkter med origo nederst til venstre. Skærmens DC måles i pixels med origo øverst til venstre og en zoomfaktor, du vælger. Printerens DC, den som forhåndsvisningen skal forudsige, måler pixels ved enhedens opløsning, men placerer sit origo i hjørnet af det udskrivbare område, ikke i hjørnet af arket. Ram bare én af dem forkert, og forhåndsvisningen ser fin ud, mens den udskrevne side kommer ud forskudt, skaleret eller beskåret langs en kant. Det sædvanlige symptom er en formular med ramme, der vises centreret og udskrives med de øverste og venstre streger skåret af, fordi laserprinteren ikke kan lægge blæk i de yderste få millimeter, og ingen fortalte forhåndsvisningen det. losLab PDF Library (PDF Library for Delphi) dækker hele stien med gengivelseskald til enhedskontekster, et konfigurationslag med virtuelle printere og forhåndsvisningsbitmaps genereret ud fra printerens egne mål, og det sidste er det, der gør forhåndsvisningen ærlig omkring den margen
Papirets geometri er ikke det udskrivbare områdes geometri
To rektangler beskriver ethvert udskrivningsmål, og forskydningen mellem dem er dér, de fleste fejl i forhåndsvisninger bor. Papirrektanglet er det fysiske ark. Det udskrivbare rektangel er den mindre region, printmotoren rent faktisk kan nå, indrykket af en hardwaremargen, der er forskellig fra printermodel til printermodel og undertiden fra bakke til bakke. Bibliotekets udskrivningslag måler begge. Den underliggende klasse TPLPrinter blotlægger PageWidth og PageHeight for det udskrivbare område, FullPageWidth og FullPageHeight for hele arket og PrintOffsetX sammen med PrintOffsetY for afstanden mellem deres origoer, alt sammen i enhedspixels ved den opløsning, GetDPI melder. En ærlig forhåndsvisning skalerer netop de tal ned til skærmopløsning i stedet for at male siden ind i det rektangel, kontrollen nu tilfældigvis har. Springer du det trin over, antager forhåndsvisningen stiltiende en margen på nul, og det er den ene værdi, ingen rigtig printer bruger
Forhåndsvisning på skærmen gennem RenderPageToDC
Til en forhåndsvisningskontrol på skærmen tegner RenderPageToDC(DPI, Page, DC) en side af det indlæste dokument direkte på enhver GDI-enhedskontekst, hvad enten det er et TPaintBox-lærred, en bitmap uden for skærmen eller en metafil-DC. DPI-argumentet sætter zoomen. 96 svarer nogenlunde til en visning på 100 % på en klassisk skærm, og en fordobling fordobler den gengivne størrelse
procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
// disse tre er klæbrig bibliotekstilstand, ikke parametre per kald:
FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
FPdf.SetRenderDCErasePage(1);
FPdf.SetRenderCropType(0);
FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;
Fælden er, at DC-gengivelsesstien styres af klæbrig bibliotekstilstand, ikke af parametre per kald. SetRenderDCOffset, SetRenderDCErasePage og SetRenderCropType bliver hver især hængende, indtil noget ændrer dem, så en miniature-løkke, der kører, efter at brugeren har justeret den zoomede visning, arver hvilken forskydning eller beskæring den forrige kodesti nu efterlod. Symptomet er en forhåndsvisning, der kun driver i bestemte navigationsforløb, hvilket er omtrent så elendigt at reproducere, som en fejl kan blive. At sætte al den relevante tilstand i toppen af paint-handleren, som ovenfor, koster ingenting og fjerner hele klassen. En anden multiplikator gemmer sig lige i nærheden. Den effektive outputopløsning er gengivelsesskalaen gange DPI-argumentet, og selv om SetRenderScale som standard står på 1,0, bliver den også hængende, når den først er ændret, så en eksportfunktion, der har skruet på den, omskalerer stille og roligt enhver senere forhåndsvisning, indtil noget sætter den tilbage
Fremvisere med rulning og delvise gentegninger har en dedikeret variant. RenderPageToDCClip tager en clip-specifikation sammen med enhedskonteksten, så en invalidering af ét bånd i vinduet kun gentegner det bånd i stedet for at rasterisere hele siden igen. Ved høj zoom på storformatsider er det forskellen på en fremviser, der følger rullebjælken, og en, der smører sig ud bag den
Et printjob, der matcher forhåndsvisningen
Udskrivningssiden arbejder gennem en virtuel printer. NewCustomPrinter kloner en systemprinter ind i en biblioteksprivat konfiguration, og SetupPrinter justerer den klon uden at røre maskinens fælles DevMode: papir går ind som indstilling 1 (en DMPAPER_*-konstant) og orientering som indstilling 11. Gevinsten er isolering. En tjeneste kan udskrive A4-etiketter, mens værtens standardprinter bliver stående på Letter, og intet skal gendannes bagefter
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); // indstilling 1 = papir, DMPAPER_A4
Pdf.SetupPrinter(Virt, 11, 1); // indstilling 11 = orientering, 1 = stående
Opt := Pdf.PrintOptions(1, 1, 'Monthly Report'); // tilpas til papir, autorotér + centrér
Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
finally
Pdf.Free;
end;
end;
PrintOptions fortjener en omhyggelig læsning. Den returnerer et options-handle, som du skal sende videre til PrintDocument eller PrintPages; det er ikke omgivende tilstand. At bygge indstillingerne og så glemme at sende handlet videre fejler tavst. Jobbet udskrives med standardværdier, og ingen bemærker det, før en politik om tilpasning til papiret var forventet, og en overdimensioneret side i stedet kom ud beskåret. Argumentet om sideskalering er dér, den politik bor. Ingen skalering bevarer den dimensionelle nøjagtighed, hvilket betyder noget for formularer, der bliver målt op mod en lineal. Tilpasning til papiret omskalerer alt til arket. Formindsk store sider lader normale sider være i fred og træder kun til, når en side overskrider det udskrivbare område, hvilket som regel er det rigtige standardvalg til et blandet sæt dokumenter. Flaget for autorotering og centrering håndterer liggende sider uden en ekstra kodesti
Programmer, der allerede styrer en TPrinter gennem VCL'ens dialogforløb, kan overdrage den direkte. PrintDocumentToPrinterObject og PrintPagesToPrinterObject tager imod den konfigurerede TPrinter-instans, hvilket bevarer standardudskrivningsdialogen som den brugervendte konfigurationsflade, mens biblioteket står for sidegengivelsen. At blande de to tilgange i én kodesti plejer at genindføre netop den geometridrift, resten af dette arbejde skulle slå ihjel, så vælg én. Vejen med den virtuelle printer passer til tjenester uden opsyn; TPrinter-vejen passer til interaktive programmer
Selektivt output fungerer på samme måde. PrintPages tager en intervalstreng, så hvis du sender den virtuelle printers navn, '2-5,12' og options-handlet, udskrives side 2 til 5 og 12 med geometrikontrakten intakt, og den samme syntaks driver print-til-fil-varianterne. De filvarianter er det praktiske svar til et miljø uden opsyn, hvor ingen fysisk enhed er tilsluttet: regressionstest af printgeometri på en byggeserver, der slet ikke har nogen driverkø. Gengiv det samme dokument gennem de samme indstillinger til et filartefakt ved hver build, og en geometriregression bliver til en diff i stedet for en kundehenvendelse tre uger senere
Forhåndsvisningsbitmaps med printerens egne mål
En forhåndsvisning gengivet ved 96 DPI mod en antaget sidestørrelse besvarer det forkerte spørgsmål. Den viser, hvordan siden ser ud, ikke hvad denne printer vil lægge på dette papir. GetPrintPreviewBitmapToString lukker det hul ved at bygge forhåndsvisningen ud fra samme brugerdefinerede printer og samme options-handle som det endelige job, så papirstørrelse, orientering, skaleringspolitik, rotation og hardwareforskydningen alle går ind i bitmappen. Det, der kommer tilbage, 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 sætter loft over bitmappens lange kant. 1200 pixels forbliver skarpt i en forhåndsvisningsdialog og holder hukommelsesforbruget beskedent selv for ingeniørtegninger i E-format, hvor en gengivelse i fuld opløsning ved printerens 600 DPI ville løbe op i gigabyte
At huske brugerens printervalg
Udskrivningsdialoger, der glemmer deres indstillinger mellem sessioner, genererer supportsager helt af sig selv. DevMode-parret, GetPrinterDevModeToString og SetPrinterDevModeFromString, serialiserer en printers fulde driverkonfiguration til en uigennemsigtig streng, du kan gemme i brugerindstillingerne og gendanne næste session, inklusive de driverspecifikke valgmuligheder, intet generisk API gider modellere. Gem printeren efter navn fra GetPrinterNames, aldrig efter listeindeks. Rækkefølgen i indekset ændrer sig, hver gang en printer tilføjes eller fjernes, så et gemt indeks peger stille og roligt på den forkerte enhed, næste gang listen forskyder sig. GetDefaultPrinterName dækker reserveløsningen, når den huskede enhed er forsvundet helt
Valg af bakke runder historien om vedvarende indstillinger af. GetPrinterBins melder de papirkilder, en driver blotlægger, hvilket betyder noget for arbejdsgange med brevpapir, hvor side ét trækker fra brevpapirbakken og resten fra almindeligt papir. Det er en politik, brugere forventer, at programmet husker sammen med alt det andet, og et printjob, der lander på det forkerte papir, læses som en fejl, selv når hver eneste byte i PDF'en var korrekt
Hold én motor på tværs af forhåndsvisning og udskrivning
En sidste beslutning styrer stille og roligt troværdigheden. Valget af gengivelsesmotor gælder for både skærm- og printerdestinationer, så fristelsen er at vise forhånd med en hurtig motor og udskrive med en præcis. Modstå den. At drive forhåndsvisningen og jobbet gennem forskellige motorer genindfører præcis den drift i troværdighed, som en printertro forhåndsvisning blev bygget for at fjerne, og den gør det på en måde, der kun viser sig på papir. Afvejningerne mellem den indbyggede motor, Cairo og PDFium bliver vejet i PDF-gengivelse med flere motorer i Delphi; vælg én, og brug den på begge sider
Dokumenter, der er for store til at indlæse bekvemt før udskrivning, kan åbnes gennem den direct access-sti, der beskrives i fletning, opdeling og direct access for store PDF'er, som gengiver sider til en enhedskontekst ud fra et fil-handle uden at bygge dokumenttræet. Den komplette API-reference for udskrivning findes på produktsiden for losLab PDF Library til Delphi