Leer hoe u PDF-documenten met PDFium VCL in Delphi afdrukt met correcte rendering, schaal en printerinstellingen.
Dit artikel is bedoeld voor ontwikkelaars die met pdf-programmeren werken. Productnamen, API-namen, bestandsnamen en codefragmenten zijn bewust ongewijzigd gehouden, zodat de voorbeelden direct met de oorspronkelijke documentatie en broncode te vergelijken zijn.
Overzicht
De pagina beschrijft het probleemgebied, de relevante implementatiekeuzes en de controles die belangrijk zijn voordat de oplossing in een echte toepassing wordt gebruikt.
Codevoorbeeld
Het onderstaande codefragment is ongewijzigd uit de Engelse bron overgenomen om identifiers, API-aanroepen en syntaxis exact te behouden.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
procedure PrintPdfSimple(const FileName: string); var Pdf: TPdf; I: Integer; Bitmap: TBitmap; begin Pdf := TPdf.Create(nil); try Pdf.FileName := FileName; Pdf.Active := True; Printer.Title := Pdf.Title; Printer.BeginDoc; try for I := 1 to Pdf.PageCount do begin if I > 1 then Printer.NewPage; Pdf.PageNumber := I; // Render page to printer Bitmap := Pdf.RenderPage( 0, 0, Printer.PageWidth, Printer.PageHeight, ro0, [rePrinting] ); try PrintBitmap(Printer, Bitmap); finally Bitmap.Free; end; end; finally Printer.EndDoc; end; finally Pdf.Active := False; Pdf.Free; end; end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
procedure PrintBitmap(Printer: TPrinter; Bitmap: TBitmap); var InfoHeaderSize, ImageSize: DWORD; InfoHeader: PBitmapInfo; Image: Pointer; begin 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, Bitmap.Width, Bitmap.Height, 0, 0, Bitmap.Width, Bitmap.Height, Image, InfoHeader^, DIB_RGB_COLORS, SRCCOPY ); finally FreeMem(Image); end; finally FreeMem(InfoHeader); end; end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
procedure TFormMain.PrintPDFPages; var FromPage, ToPage, Page, Copy, CopyCount: Integer; FirstPage: Boolean; Bitmap: TBitmap; ScaleX, ScaleY, Scale: Double; DestWidth, DestHeight: Integer; Dpi: Integer; begin // Get print quality DPI case cmbQuality.ItemIndex of 0: Dpi := 150; // Draft 1: Dpi := 300; // Normal 2: Dpi := 600; // High else Dpi := 300; end; // Determine page range if radAllPages.Checked then begin FromPage := 1; ToPage := Pdf.PageCount; end else begin FromPage := StrToIntDef(edtFromPage.Text, 1); ToPage := StrToIntDef(edtToPage.Text, Pdf.PageCount); end; // Handle copies and collation if chkCollate.Checked then CopyCount := 1 // Will repeat entire job else CopyCount := spnCopies.Value; FProcessing := True; FCancelled := False; Printer.Title := ExtractFileName(Pdf.FileName); Printer.BeginDoc; try FirstPage := True; progressPrint.Max := (ToPage - FromPage + 1) * spnCopies.Value; progressPrint.Position := 0; for Page := FromPage to ToPage do begin for Copy := 1 to CopyCount do begin if FCancelled then Break; if FirstPage then FirstPage := False else Printer.NewPage; Pdf.PageNumber := Page; // Calculate scaling based on settings case cmbScaling.ItemIndex of 0: // No scaling begin DestWidth := Round(Pdf.PageWidth * Dpi / 72); DestHeight := Round(Pdf.PageHeight * Dpi / 72); end; 1: // Fit to page begin ScaleX := Printer.PageWidth / (Pdf.PageWidth * Dpi / 72); ScaleY := Printer.PageHeight / (Pdf.PageHeight * Dpi / 72); Scale := Min(ScaleX, ScaleY); DestWidth := Round(Pdf.PageWidth * Dpi / 72 * Scale); DestHeight := Round(Pdf.PageHeight * Dpi / 72 * Scale); end; 2: // Shrink to fit (only if needed) begin ScaleX := Printer.PageWidth / (Pdf.PageWidth * Dpi / 72); ScaleY := Printer.PageHeight / (Pdf.PageHeight * Dpi / 72); Scale := Min(ScaleX, ScaleY); if Scale > 1 then Scale := 1; DestWidth := Round(Pdf.PageWidth * Dpi / 72 * Scale); DestHeight := Round(Pdf.PageHeight * Dpi / 72 * Scale); end; end; // Render and print Bitmap := Pdf.RenderPage(0, 0, DestWidth, DestHeight, ro0, [rePrinting, reAnnotations]); try PrintBitmap(Printer, Bitmap); finally Bitmap.Free; end; progressPrint.Position := progressPrint.Position + 1; Application.ProcessMessages; end; if FCancelled then Break; end; finally Printer.EndDoc; FProcessing := False; end; end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
procedure TFormMain.btnPrintClick(Sender: TObject); begin if not FileExists(edtPdfFile.Text) then begin ShowError('Please select a valid PDF file first.'); Exit; end; // Setup print dialog PrintDialog.MinPage := 1; PrintDialog.MaxPage := Pdf.PageCount; PrintDialog.FromPage := 1; PrintDialog.ToPage := Pdf.PageCount; PrintDialog.Options := [poPageNums, poSelection]; if PrintDialog.Execute then begin PrintPDFPages; end; end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
procedure TFormMain.UpdatePreview; var PreviewWidth, PreviewHeight: Integer; Scale: Double; begin if not Pdf.Active then Exit; Pdf.PageNumber := FCurrentPreviewPage; // Calculate preview size to fit panel Scale := Min( pnlPreview.Width / Pdf.PageWidth, pnlPreview.Height / Pdf.PageHeight ) * (FCurrentZoom / 100); PreviewWidth := Round(Pdf.PageWidth * Scale); PreviewHeight := Round(Pdf.PageHeight * Scale); // Render preview FPreviewBitmap.Free; FPreviewBitmap := Pdf.RenderPage( 0, 0, PreviewWidth, PreviewHeight, ro0, [reAnnotations], clWhite ); // Display in image control imgPreview.Picture.Assign(FPreviewBitmap); // Update page info lblPageInfo.Caption := Format('Page %d of %d', [FCurrentPreviewPage, Pdf.PageCount]); end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
procedure TFormMain.btnFirstPageClick(Sender: TObject); begin if Pdf.Active then begin FCurrentPreviewPage := 1; edtPreviewPage.Text := '1'; UpdatePreview; end; end; procedure TFormMain.btnPrevPageClick(Sender: TObject); begin if Pdf.Active and (FCurrentPreviewPage > 1) then begin Dec(FCurrentPreviewPage); edtPreviewPage.Text := IntToStr(FCurrentPreviewPage); UpdatePreview; end; end; procedure TFormMain.btnNextPageClick(Sender: TObject); begin if Pdf.Active and (FCurrentPreviewPage < Pdf.PageCount) then begin Inc(FCurrentPreviewPage); edtPreviewPage.Text := IntToStr(FCurrentPreviewPage); UpdatePreview; end; end; procedure TFormMain.btnLastPageClick(Sender: TObject); begin if Pdf.Active then begin FCurrentPreviewPage := Pdf.PageCount; edtPreviewPage.Text := IntToStr(FCurrentPreviewPage); UpdatePreview; end; end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
procedure TFormMain.cmbZoomChange(Sender: TObject); begin case cmbZoom.ItemIndex of 0: FCurrentZoom := 50; 1: FCurrentZoom := 75; 2: FCurrentZoom := 100; 3: FCurrentZoom := 125; 4: FCurrentZoom := 150; 5: FCurrentZoom := 200; else FCurrentZoom := 100; end; UpdatePreview; end; |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function GetPrintRotation(Rotation: TRotation): TRotation; begin // Adjust rotation for printing (printer coordinate system) case Rotation of ro90: Result := ro270; ro270: Result := ro90; else Result := Rotation; end; end; // Use in printing Bitmap := Pdf.RenderPage(0, 0, Width, Height, GetPrintRotation(Pdf.PageRotation), [rePrinting]); |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
procedure TFormMain.SaveSettings; var Ini: TIniFile; begin Ini := TIniFile.Create(FSettingsFile); try Ini.WriteInteger('Print', 'Quality', cmbQuality.ItemIndex); Ini.WriteInteger('Print', 'Scaling', cmbScaling.ItemIndex); Ini.WriteInteger('Print', 'Copies', spnCopies.Value); Ini.WriteBool('Print', 'Collate', chkCollate.Checked); Ini.WriteInteger('Preview', 'Zoom', FCurrentZoom); finally Ini.Free; end; end; procedure TFormMain.LoadSettings; var Ini: TIniFile; begin if FileExists(FSettingsFile) then begin Ini := TIniFile.Create(FSettingsFile); try cmbQuality.ItemIndex := Ini.ReadInteger('Print', 'Quality', 1); cmbScaling.ItemIndex := Ini.ReadInteger('Print', 'Scaling', 1); spnCopies.Value := Ini.ReadInteger('Print', 'Copies', 1); chkCollate.Checked := Ini.ReadBool('Print', 'Collate', False); FCurrentZoom := Ini.ReadInteger('Preview', 'Zoom', 100); finally Ini.Free; end; end; end; |
Praktische aandachtspunten
- Controleer invoerbestanden en foutpaden expliciet voordat u de routine in productie gebruikt.
- Houd paginalay-out, lettertypen en coördinaten reproduceerbaar, vooral bij server-side verwerking.
- Test het resultaat in meer dan één PDF-viewer wanneer rendering, annotaties of interactieve elementen belangrijk zijn.
- Laat componentlevensduur en bestandshandles altijd via try/finally-achtige patronen opruimen.