PDF-koordinater er i punkter, printerkoordinater er i enhedsenheder, og de to har intet med hinanden at gøre, før du konverterer dem bevidst. Det misforhold er roden til det meste dårlige print-output i Delphi-applikationer: koden sender den rigtige fil, men siden kommer beskåret, strakt eller blank ud. PDFium Component håndterer gengivelsessiden rent; printer-rørarbejdet (plumbing) er standard VCL. De to passer sammen med en beskeden mængde kode, når du først forstår, hvad hver side forventer
Sådan fungerer render-then-print-pipelinen
PDFium Component taler ikke direkte med printere. Mønsteret er: gengiv en side til en TBitmap med den opløsning, du ønsker, og overfør derefter den bitmap til printerens lærred med StretchDIBits. TPdf.RenderPage returnerer en caller-ejet bitmap, så du styrer pixeldimensionerne. Bestå [rePrinting] i indstillingssættet, og PDFium skifter sin gengivelsessti til en, der udelader effekter, der kun er til skærm, såsom LCD-subpixel-hinting, og håndterer sidens MediaBox korrekt til udskrivning. Udelad rePrinting, og det, du sender til printeren, er en skærmgengivelse, som ser fint ud på en skærm, men har en tendens til at producere et blødere output på høj-DPI-printere, fordi hinting-beslutningerne truffet til 96 DPI-skærme ikke passer til 300 eller 600 DPI-udskrivning
TPdf.Active er den eneste port, der skal kontrolleres, før du rører ved nogen sideegenskab. Komponenten sluger indlæsningsfejl stille og roligt: At indstille Active := True på en beskadiget eller adgangskodebeskyttet fil rejser ikke en undtagelse; det efterlader simpelthen Active som False. Tjek det altid efter tildelingen. Aflæsning af PageCount eller PageWidth på et inaktivt dokument returnerer nul, hvilket producerer tavse no-ops, der er meget svære at diagnosticere, når de først når spooleren
En minimal udskrivningsløkke
Det enkleste fungerende tilfælde indlæser en fil, åbner et udskriftsjob, itererer sider og lukker. Den eneste vanskelige detalje er, at Printer.NewPage ikke må kaldes før den første side, deraf FirstPage-flaget. Overførslen af StretchDIBits går gennem GetDIBSizes og GetDIB for at trække enhedsuafhængige bits fra bitmap-håndtaget og maler dem derefter på printerlærredet i den fulde sidestørrelse:
procedure PrintPdfFile(const FileName: string);
var
Pdf: TPdf;
I: Integer;
Bitmap: TBitmap;
InfoHeaderSize, ImageSize: DWORD;
InfoHeader: PBitmapInfo;
Image: Pointer;
FirstPage: Boolean;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
if not Pdf.Active then
Exit; // load failed silently; bail out
Printer.Title := Pdf.Title;
Printer.BeginDoc;
try
FirstPage := True;
for I := 1 to Pdf.PageCount do
begin
if FirstPage then
FirstPage := False
else
Printer.NewPage;
Pdf.PageNumber := I;
// Render at printer resolution; rePrinting adjusts the render path
Bitmap := Pdf.RenderPage(
0, 0,
Printer.PageWidth,
Printer.PageHeight,
ro0,
[rePrinting]
);
try
GetDIBSizes(Bitmap.Handle, InfoHeaderSize, ImageSize);
InfoHeader := AllocMem(InfoHeaderSize);
try
Image := AllocMem(ImageSize);
try
GetDIB(Bitmap.Handle, 0, InfoHeader^, Image^);
StretchDIBits(
Printer.Canvas.Handle,
0, 0, Printer.PageWidth, Printer.PageHeight,
0, 0, Bitmap.Width, Bitmap.Height,
Image, InfoHeader^, DIB_RGB_COLORS, SRCCOPY
);
finally
FreeMem(Image);
end;
finally
FreeMem(InfoHeader);
end;
finally
Bitmap.Free;
end;
end;
finally
Printer.EndDoc;
end;
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
At overføre Printer.PageWidth og Printer.PageHeight som bitmap-dimensionerne betyder, at du gengiver ved printerens oprindelige pixelstørrelse, hvilket allerede tager højde for enhedens DPI. StretchDIBits-kaldet knytter derefter disse pixels 1:1 til siden. Dette giver dig den bedst opnåelige nøjagtighed uden nogen udtrykkelig DPI-aritmetik, men det virker kun, når PDF-siden og det fysiske papir tilfældigvis er samme størrelse. Når de adskiller sig, har du brug for eksplicit skalering
Skalering, når side- og papirstørrelser afviger
En PDF-side i A4-portræt passer ikke automatisk til en US Letter-printer, og en landskabsside indsat i en portrætorienteret printer vil blive beskåret. Standardtilgangen er at beregne en ensartet skaleringsfaktor fra forholdet mellem printerpixels og PDF-punkter og derefter anvende den på begge dimensioner, så billedformatet bevares. Pdf.PageWidth og Pdf.PageHeight eksponerer de aktuelle sidedimensioner i punkter, hvor ét punkt er 1/72 tomme. At gange med en mål-DPI og dividere med 72 konverteres til pixels ved den opløsning. Tag Min af X- og Y-forholdene for at få den største skala, der stadig passer inden for det printbare område:
// Fit PDF page to printable area, preserving aspect ratio
var
ScaleX, ScaleY, Scale: Double;
DestWidth, DestHeight: Integer;
Dpi: Integer;
begin
Dpi := 300; // target render resolution
Pdf.PageNumber := PageIndex;
ScaleX := Printer.PageWidth / (Pdf.PageWidth * Dpi / 72);
ScaleY := Printer.PageHeight / (Pdf.PageHeight * Dpi / 72);
Scale := Min(ScaleX, ScaleY);
// Clamp to 1.0 for shrink-to-fit only (no enlargement)
if Scale > 1.0 then Scale := 1.0;
DestWidth := Round(Pdf.PageWidth * Dpi / 72 * Scale);
DestHeight := Round(Pdf.PageHeight * Dpi / 72 * Scale);
Bitmap := Pdf.RenderPage(0, 0, DestWidth, DestHeight, ro0,
[rePrinting, reAnnotations]);
// ... transfer with StretchDIBits as above
end;
Gengivelse ved Dpi = 300 passer til de fleste kontorprintere. Ved 600 DPI løber bitmappet for en enkelt A4-side op på omkring 34 megapixel, hvilket er omkring 100 MB som et 32-bit bitmap; kvalitetsgevinsten for almindelige tekstdokumenter er minimal, og hukommelsesomkostningerne pr. side er betydelige. Gem 600 DPI til trykkerier eller vektortunge tekniske tegninger, hvor det virkelig betyder noget
Flaget reAnnotations i den anden kodeblok er uafhængigt af rePrinting. Inkluder det, når brugeren forventer, at stempler, fremhævelser og kommentarbokse vises på papir. Udelad det for output, der kun har indhold. Begge flag kan frit kombineres
Siderotation
PDFium gemmer siderotation i PDF'en som en /Rotate post, der er tilgængelig via Pdf.PageRotation, som returnerer en TRotation værdi (ro0, ro90, ro180, ro270). Printerkoordinatsystemet inverterer 90 og 270 graders rotationer i forhold til skærmen. Hvis du overfører den rå PageRotation værdi direkte til RenderPage uden nogen justering, vil landskabssider indlejret i et portrætdokument udskrives på hovedet på de fleste Windows-printerdrivere. Rettelsen er en simpel ombytning før renderings-kaldet: map ro90 til ro270 og ro270 tilbage til ro90, mens ro0 og ro180 forbliver uændrede
Bekræft denne adfærd på din specifikke målprinter inden forsendelse. Driveradfærd omkring rotation er ikke ensartet på tværs af leverandører, og nogle drivere anvender deres egen rotationskorrektion på GDI-niveauet. Hvis du ser dobbelt rotation, skal du fjerne ombytningen; hvis du slet ingen korrektion ser, skal du tilføje den. Et dokument med blandet orientering med skiftende portræt- og landskabssider er den hurtigste måde at fange begge fejltilstande under test
Hukommelsesstyring på tværs af et langt udskriftsjob
Hvert kald til RenderPage allokerer et nyt TBitmap, som opkalderen (caller) ejer og skal frigøre. I løkken ovenfor håndterer try/finally Bitmap.Free blokken dette korrekt for én side ad gangen. Akkumuler ikke bitmaps på tværs af sider: en 300-DPI gengivelse af et 200-siders dokument ville forbruge gigabytes, før den første side når spooleren. Frigør hvert bitmap, før du går videre til den næste side
AllocMem / FreeMem parret inde i overførselsblokken følger samme regel. GetDIBSizes fortæller dig, hvor meget hukommelse DIB-headeren og pixeldataene har brug for; du allokerer, udfylder, maler og frigør alt inden for rammerne af én side. At lade enten blokken lække vil medføre, at udskriftsjobbet opbruger procesheapen (process heap) på dokumenter, der er længere end et par dusin sider
Hvis du har brug for at køre udskriftsjob på en baggrundstråd, skal du beholde TPdf og alle VCL-printerkald på samme tråd. TPdf i sig selv er ikke trådsikker på tværs af instanser, der deler PDFium DLL'ens globale tilstand; den sikreste model er én TPdf pr. tråd, som hver indlæser sin egen kopi af filen
Det viste gengivelses- og dokument-API er en del af PDFium Component til Delphi og C++Builder