Een PDF-pagina renderen op een Windows device context voor afdrukvoorbeeld plaatst drie coördinaatsystemen in dezelfde coderegel, en ze komen zelden overeen. De PDF-pagina wordt gemeten in points met de oorsprong linksonder. De scherm-DC wordt gemeten in pixels met de oorsprong linksboven en een zoomfactor die u kiest. De printer-DC, degene die het voorbeeld geacht wordt te voorspellen, meet pixels op de apparaatresolutie maar plaatst zijn oorsprong in de hoek van het afdrukbare gebied, niet in de hoek van het vel. Krijg er daarvan één fout en het voorbeeld ziet er prima uit terwijl de afgedrukte pagina verschoven, geschaald of langs een rand afgesneden uitkomt. Het gebruikelijke symptoom is een formulier met randen dat in het voorbeeld gecentreerd staat maar afdrukt met de bovenste en linker lijnen afgesneden, omdat de laserprinter geen inkt in de buitenste paar millimeter kan zetten en niemand het voorbeeld dat heeft verteld. losLab PDF Library (PDF Library for Delphi) dekt het hele pad met device-context-renderingaanroepen, een configuratielaag voor virtuele printers en voorbeeld-bitmaps gegenereerd uit de printer's eigen metriek, wat het onderdeel is dat het voorbeeld eerlijk maakt over die marge
Papiergeometrie is geen afdrukgeometrie
Twee rechthoeken beschrijven elk afdrukdoel, en de verschuiving ertussen is waar de meeste voorbeeldbugs leven. De papierrechthoek is het fysieke vel. De afdrukbare rechthoek is het kleinere gebied dat de printkop daadwerkelijk kan bereiken, ingesprongen door een hardwaremarge die per printermodel verschilt en soms per lade. De printlaag van de bibliotheek meet beide. De onderliggende TPLPrinter-klasse toont PageWidth en PageHeight voor het afdrukbare gebied, FullPageWidth en FullPageHeight voor het hele vel, en PrintOffsetX met PrintOffsetY voor de afstand tussen hun oorsprongen, allemaal in apparaatpixels op de resolutie die GetDPI rapporteert. Een eerlijk voorbeeld schaalt diezelfde getallen omlaag naar schermresolutie in plaats van de pagina te schilderen in welke rechthoek het besturingselement dan ook toevallig heeft. Sla die stap over en het voorbeeld gaat er stilzwijgend vanuit dat de marge nul is, wat de ene waarde is die geen echte printer gebruikt
Schermvoorbeeld via RenderPageToDC
Voor een voorbeeldbesturingselement op het scherm tekent RenderPageToDC(DPI, Page, DC) een pagina van het geladen document direct op elke GDI device context, of dat nu het canvas van een TPaintBox is, een off-screen bitmap of een metafile-DC. Het DPI-argument bepaalt de zoom. 96 benadert een 100%-weergave op een klassiek beeldscherm, en het verdubbelen ervan verdubbelt de gerenderde grootte
procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
// deze drie zijn plakkerige library-state, geen parameters per call:
FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
FPdf.SetRenderDCErasePage(1);
FPdf.SetRenderCropType(0);
FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;
De val is dat het DC-render-pad wordt gestuurd door sticky bibliotheekstatus, niet door parameters per aanroep. SetRenderDCOffset, SetRenderDCErasePage en SetRenderCropType blijven elk bestaan tot iets ze verandert, dus een miniatuur-lus die draait nadat de gebruiker de ingezoomde weergave had aangepast, erft de verschuiving of bijsnede die het vorige codepad had achtergelaten. Het symptoom is een voorbeeld dat alleen in specifieke navigatiesequenties afdrijft, wat ongeveer even ellendig te reproduceren is als een bug kan zijn. Het instellen van alle relevante status bovenaan de paint-handler, zoals hierboven, kost niets en verwijdert de hele klasse. Een tweede vermenigvuldiger verbergt zich vlakbij. De effectieve uitvoerresolutie is de renderschaal maal het DPI-argument, en hoewel SetRenderScale standaard 1.0 is, blijft het ook bestaan na wijziging, dus een exportfunctie die het had opgehoogd herschaalt stilletjes elk later voorbeeld tot iets het weer terugzet
Scrollende viewers en gedeeltelijke herweergave hebben een speciale variant. RenderPageToDCClip neemt een clip-specificatie samen met de device context, zodat het ongeldig verklaren van één band van het venster alleen die band opnieuw tekent in plaats van de hele pagina opnieuw te rasteren. Bij hoge zoom op pagina's in groot formaat is dat het verschil tussen een viewer die de schuifbalk volgt en een die erachteraan smeuïgt
Een printjob die overeenkomt met het voorbeeld
De printkant werkt via een virtuele printer. NewCustomPrinter kloont een systeemprinter naar een bibliotheek-private configuratie, en SetupPrinter past die kloon aan zonder de machinebrede DevMode aan te raken: papier gaat erin als instelling 1 (een DMPAPER_*-constante) en oriëntatie als instelling 11. Het resultaat is isolatie. Een service kan A4-etiketten printen terwijl de standaardprinter van de host op Letter blijft staan, en er hoeft achteraf niets te worden hersteld
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'); // passend op papier, automatisch roteren + centreren
Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
finally
Pdf.Free;
end;
end;
PrintOptions verdient een zorgvuldige lezing. Het retourneert een options-handle die u moet doorgeven aan PrintDocument of PrintPages; het is geen omgevingsstatus. De options bouwen en de handle dan vergeten door te geven faalt stil. De job print met standaardwaarden, en niemand merkt het tot een fit-to-paper-beleid werd verwacht en een te grote pagina in plaats daarvan afgesneden uitkwam. Het pagina-schalingsargument is waar dat beleid leeft. Geen schaling behoudt dimensionale nauwkeurigheid, wat uitmaakt voor formulieren die tegen een liniaal worden gemeten. Fit-to-paper schaalt alles naar het vel. Shrink-large-pages laat normale pagina's met rust en grijpt pas in wanneer een pagina het afdrukbare gebied overschrijdt, wat meestal de juiste standaard is voor een gemengde documentenset. De auto-rotate-and-center-vlag behandelt liggende pagina's zonder een tweede codepad
Toepassingen die al een TPrinter beheren via de VCL-dialogflow kunnen deze direct overhandigen. PrintDocumentToPrinterObject en PrintPagesToPrinterObject accepteren de geconfigureerde TPrinter-instantie, wat de standaard printdialoog als het gebruikersgerichte configuratieoppervlak behoud terwijl de bibliotheek de paginarendering afhandelt. Het mengen van de twee benaderingen in één codepad neigt de geometrische drift te herintroduceren die de rest van dit werk juist moest elimineren, dus kies er één. De virtuele-printer-route past bij onbemande services; de TPrinter-route past bij interactieve toepassingen
Selectieve uitvoer werkt op dezelfde manier. PrintPages neemt een bereikstring, dus het doorgeven van de virtuele printernaam, '2-5,12' en de options-handle print pagina's 2 tot en met 5 en 12 met het geometriecontract intact, en dezelfde syntaxis stuurt de print-naar-bestand-varianten. Die bestandsvarianten zijn het praktische antwoord voor een onbemande omgeving zonder aangesloten fysiek apparaat: regressietesten van printgeometrie op een build-server die helemaal geen driver-wachtrij heeft. Render hetzelfde document met dezelfde options naar een bestandsartefact bij elke build, en een geometrieregressie wordt een diff in plaats van een klantenrapport drie weken later
Voorbeeld-bitmaps met de printer's eigen metriek
Een voorbeeld dat op 96 DPI wordt gerenderd tegen een aangenomen paginagrootte beantwoordt de verkeerde vraag. Het toont hoe de pagina eruitziet, niet wat deze printer op dit papier zal zetten. GetPrintPreviewBitmapToString sluit die kloof door het voorbeeld op te bouwen uit dezelfde custom printer en dezelfde options-handle als de uiteindelijke job, zodat papierformaat, oriëntatie, schalingsbeleid, rotatie en de hardwareverschuiving allemaal in de bitmap vloeien. Wat terugkomt is wat het vel zal tonen
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;
Het MaxDimension-argument begrenst de lange zijde van de bitmap. 1200 pixels blijft scherp voor een voorbeelddialoog en houdt het geheugen bescheiden, zelfs voor engineering-tekeningen in E-formaat, waar een render op volledige resolutie op de 600 DPI van de printer zou oplopen tot gigabytes
De printerkeuzes van de gebruiker onthouden
Printdialogen die hun instellingen tussen sessies vergeten genereren zelf supporttickets. Het DevMode-paar, GetPrinterDevModeToString en SetPrinterDevModeFromString, serialiseert de volledige driverconfiguratie van een printer naar een ondoorzichtige string die u in gebruikersvoorkeuren kunt opbergen en de volgende sessie kunt herstellen, inclusief de driver-specifieke opties die geen generieke API de moeite neemt te modelleren. Persisteer de printer op naam uit GetPrinterNames, nooit op lijstindex. De indexvolgorde verandert telkens wanneer een printer wordt toegevoegd of verwijderd, dus een opgeslagen index wijst stilletjes naar het verkeerde apparaat de volgende keer dat de lijst verschuift. GetDefaultPrinterName dekt de fallback wanneer het onthouden apparaat volledig is verdwenen
Ladeselectie rondt het persistentieverhaal af. GetPrinterBins rapporteert de papierbronnen die een driver toont, wat uitmaakt voor briefhoofd-workflows waar pagina één uit de briefhoofdlade trekt en de rest uit gewoon papier. Dat is een beleid waarvan gebruikers verwachten dat de toepassing het onthoudt net als al het andere, en een printjob die op het verkeerde papier belandt leest als een bug, zelfs wanneer elke byte van de PDF juist was
Eén motor houden over voorbeeld en print
Eén laatste beslissing bepaalt op de achtergrond de getrouwheid. De selectie van de render-motor is van toepassing op zowel scherm- als printerbestemmingen, dus de verleiding is om voor te vertonen met een snelle motor en te printen met een precieze. Weersta die. Het aansturen van het voorbeeld en de job via verschillende motoren herintroduceert precies de getrouwheidsdrift die een printer-echt voorbeeld moest verwijderen, en het doet dat op een manier die alleen op papier zichtbaar wordt. De afwegingen tussen de ingebouwde, Cairo- en PDFium-motoren worden afgewogen in multi-engine PDF-rendering in Delphi; kies er één en gebruik die aan beide kanten
Documenten die te groot zijn om voor het printen comfortabel te laden, kunnen worden geopend via het direct-access-pad dat wordt beschreven in grote PDF samenvoegen, splitsen en direct access, dat pagina's naar een device context rendert vanuit een bestands-handle zonder de documentboom op te bouwen. De volledige print-API-referentie staat op de productpagina van losLab PDF Library for Delphi