含裁剪区域的增强图元文件打印PDF时内容空白问题
增强图元文件打印PDF时裁剪区域内容空白的问题解决建议
问题描述
开发的增强图元文件(Enhanced Metafile)应用,将图元文件输出到PaintBox画布、物理打印机或自定义预览窗口时均正常,但通过NitroPDF、Adobe Acrobat及Microsoft Print to PDF打印为PDF时,裁剪区域内的绘制内容显示为空白。
裁剪按列实现(所有表头和列继承自TBaseColumn),表头和边框在裁剪区域释放后绘制,核心代码如下:
procedure TBaseColumn.Draw(ACanvas: TMetafileCanvas); var P: array [0 .. 1] of TPoint; LClipRegion: HRGN; begin // Set temp reference to MetafileCanvas FCanvas := ACanvas; // Set clipping area P[0] := FDrawingRect.TopLeft; P[1] := FDrawingRect.BottomRight; LPtoDP(FCanvas.Handle, P, 2); LClipRegion := CreateRectRgn(P[0].X, P[0].Y, P[1].X, P[1].Y); SelectClipRgn(FCanvas.Handle, LClipRegion); try DrawContent; // Protected method so we can override in derived columns finally // Reset clipping SelectClipRgn(FCanvas.Handle, HRGN(nil)); DeleteObject(LClipRegion); end; DrawHeader; DrawColumnBorder; FCanvas := nil; end;
打印逻辑代码
自定义预览提供三种打印方法,核心实现如下:
procedure TMyPrintPreview.Print(const APrintTitle: string; const APrintMethod: TSD_PrintMethod); var LMetaFileRect: TRect; LBmp: Vcl.Graphics.TBitmap; begin if not assigned(FMetaFile) then exit; if APrintTitle = '' then Printer.Title := 'Document_' + FormatDateTime('yymmdd_hhnn', Now) else Printer.Title := APrintTitle; Printer.BeginDoc; try LMetaFileRect := FPreviewInfo.GetPrinterMetaFileRect(FMetaFileScaling); case APrintMethod of pmPlayMetaFile: begin PlayEnhMetaFile(Printer.Canvas.Handle, FMetaFile.Handle, LMetaFileRect); end; pmDirect: begin Printer.Canvas.StretchDraw(LMetaFileRect, FMetaFile); end; pmBitmap: begin LBmp := Vcl.Graphics.TBitmap.Create; try LBmp.Width := LMetaFileRect.Width; LBmp.Height := LMetaFileRect.Height; LBmp.Canvas.StretchDraw(Rect(0, 0, LBmp.Width, LBmp.Height), FMetaFile); Printer.Canvas.StretchDraw(LMetaFileRect, LBmp); finally LBmp.Free; end; end; end; Printer.EndDoc; except on E: Exception do begin Printer.Abort; raise; end; end; end;
调用逻辑:
procedure TfrmMain.btnPrintClick(Sender: TObject); var dlgMethod: TdlgPrintMethod; LMethod: TSD_PrintMethod; begin if ParamStr(1) = '/p' then begin // DEBUG PRINTING. Show modal dialog to select "print method".... dlgMethod := TdlgPrintMethod.Create(self); try if dlgMethod.ShowModal = mrOK then begin LMethod := TSD_PrintMethod(dlgMethod.PrintMethod); MyPrintPreview.Print(FFileName, LMethod); end; finally dlgMethod.Free; end; end else // NORMAL PRINTING. No dialog, use default method (pmPlayMetaFile) MyPrintPreview.Print(FFileName); end;
异常现象
- 直接调用任意打印方法打印PDF时,裁剪区域内容均空白;
- 通过模态对话框选择打印方法后,PDF输出完全正常。
解决建议
1. 替换区域裁剪API,规避虚拟打印机兼容性问题
部分PDF虚拟打印机对HRGN对象的支持存在缺陷,改用矩形裁剪的GDI API替代SelectClipRgn:
// 替换原裁剪区域设置代码 IntersectClipRect(FCanvas.Handle, P[0].X, P[0].Y, P[1].X, P[1].Y); // 恢复裁剪时使用 SelectClipRgn(FCanvas.Handle, HRGN(nil));
2. 确保打印机画布初始化完成
直接调用打印时,虚拟打印机可能未完成画布初始化,在Printer.BeginDoc后添加延迟或刷新操作:
Printer.BeginDoc; try // 添加画布刷新,确保虚拟打印机完成初始化 Printer.Canvas.HandleNeeded; Sleep(10); // 短延迟,适配部分慢响应的虚拟打印机 LMetaFileRect := FPreviewInfo.GetPrinterMetaFileRect(FMetaFileScaling); // 后续打印逻辑...
3. 显式指定默认打印方法为pmBitmap
直接调用时默认使用pmPlayMetaFile,尝试改为pmBitmap,绕过增强图元文件的区域解析问题:
// NORMAL PRINTING. No dialog, use pmBitmap method instead MyPrintPreview.Print(FFileName, pmBitmap);
4. 调整坐标转换的时机
将LPtoDP转换放在获取画布句柄之后,确保使用当前画布的正确映射模式:
// 先获取句柄,再转换坐标 FCanvas.HandleNeeded; LPtoDP(FCanvas.Handle, P, 2);
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

