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

如何将Microsoft Print to PDF输出改为流而非文件?

实现Microsoft Print to PDF输出到流的解决方案

Microsoft Print to PDF本身没有直接输出到流的API,它的设计仅支持生成文件。要实现输出到流,只能通过临时文件中转的方式:先打印到临时文件,再将文件内容读入目标流,最后清理临时文件。

修改后的核心代码

首先给你的TMSPDFPrinter类添加输出流属性和临时文件管理逻辑:

type
  TMSPDFPrinter = class(TPrinter)
  private
    FOutputStream: TStream;
    FTempFileName: string;
  public
    // 用于接收PDF内容的输出流,调用BeginDoc前必须赋值
    property OutputStream: TStream read FOutputStream write FOutputStream;
    procedure BeginDoc; override;
    procedure EndDoc; override;
  end;

重写BeginDoc方法(打印到临时文件)

procedure TMSPDFPrinter.BeginDoc;
var
  DeviceMode: THandle;
  Device, Driver, Port: array[0..80] of Char;
  TempPath: array[0..MAX_PATH] of Char;
begin
  if not Assigned(FOutputStream) then
    raise Exception.Create('必须先指定OutputStream才能开始打印');

  // 获取系统临时目录并生成唯一临时文件名
  if GetTempPath(MAX_PATH, TempPath) = 0 then
    raise Exception.Create('获取临时目录失败');
  FTempFileName := TPath.GetTempFileName(TempPath, 'PDF', 0, '');

  // 配置Microsoft Print to PDF打印机指向临时文件
  Printer.PrinterIndex := Printer.Printers.IndexOf('Microsoft Print to PDF');
  Printer.GetPrinter(Device, Driver, Port, DeviceMode);
  Printer.SetPrinter(Device, Driver, PChar(FTempFileName), 0);
  
  inherited;
end;

重写EndDoc方法(将临时文件内容写入流并清理)

procedure TMSPDFPrinter.EndDoc;
var
  TempFileStream: TFileStream;
begin
  try
    inherited; // 完成打印,确保PDF文件已生成

    // 将临时文件内容复制到输出流
    TempFileStream := TFileStream.Create(FTempFileName, fmOpenRead or fmShareDenyNone);
    try
      FOutputStream.CopyFrom(TempFileStream, TempFileStream.Size);
    finally
      TempFileStream.Free;
    end;
  finally
    // 无论打印成功与否,都清理临时文件
    if FileExists(FTempFileName) then
      DeleteFile(FTempFileName);
    FTempFileName := '';
  end;
end;

使用示例

var
  PDFPrinter: TMSPDFPrinter;
  OutputStream: TMemoryStream;
begin
  OutputStream := TMemoryStream.Create;
  try
    PDFPrinter := TMSPDFPrinter.Create;
    try
      PDFPrinter.OutputStream := OutputStream;
      PDFPrinter.BeginDoc;
      // 执行打印内容绘制逻辑(比如Canvas.TextOut等)
      PDFPrinter.Canvas.TextOut(100, 100, '测试PDF内容');
      PDFPrinter.EndDoc;

      // 此时OutputStream中已包含PDF内容,可按需使用(比如保存到文件、发送网络等)
      OutputStream.Position := 0;
      OutputStream.SaveToFile('output.pdf');
    finally
      PDFPrinter.Free;
    end;
  finally
    OutputStream.Free;
  end;
end;

注意事项

  • 必须在调用BeginDoc前给OutputStream赋值,且流必须处于可写状态(比如TMemoryStream默认支持)
  • 异常处理逻辑确保临时文件不会残留,避免占用磁盘空间
  • 如果需要多线程打印,要注意临时文件名的唯一性,TPath.GetTempFileName可保证这一点
  • 如果追求更高效率,可考虑直接使用支持输出到流的第三方PDF库(如SynPDF、QuickPDF等),避免临时文件的IO开销

内容的提问来源于stack exchange,提问作者complete_stranger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 10:08:16