You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含裁剪区域的增强图元文件打印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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 17:37:47