将 PDF 文档重新打印到 PDF 打印机 - 一般和 Delphi 特定

Reprint PDF document to PDF printer - generally and Delphi specific

我的系统生成的 PDF 文档由于某种未知原因非常大 - 超过 1 MB。如果我使用 PDF 打印机(Bullzip、PDF Architect 等)将此 PDF 文档打印到另一个 PDF 文件,则生成的 PDF 文件大小仅为 40kb,这很好。

所以 - 问题是 - 如何使用 PDF 打印机将 PDF 文件重新打印为另一个文件。我想要一个与 PDF 打印机无关的解决方案,让用户选择要安装和使用的 PDF 打印机。

我正在寻找 Embarcadero Delphi 解决方案,但我也很乐意收到一般性答案(我可以根据自己的情况调整它们以供 Delphi 实施)。

通常每个处理系统(MS office Word 或 Excel、ReportBuilder)都支持打开特定文件并静默打印它们。但显然 PDF 文件可以由不同的系统打开和打印。是公分母?或者我应该坚持使用某些特定的 PDF 系统并对用户添加额外的要求。

其他详细信息: 我正在使用数字隐喻 ReportBuilder 和 Pragnaan ReportBuilder 导出设备 http://www.rarefind.com/rbpro/index.html。 Pragnaan取ppReport-TReport组件和FileName-TmpFileName,代码一行:

ExportToPDF(ppReport, TmpFileName);

我们使用 Pragnaan 是因为它可以温和地处理非英语语言中的变音符号,但它的缺点是会产生巨大的文件。我们坚持使用 Pragnaan,因为没有更好的选择,但我们只需要重新打印已经完成的 PDF 文件。

PDF 文件未压缩。 "Saving as" 来自 Acrobat Reader 或 Foxit Reader 将压缩它。

您还可以在命令行中使用 QPDF 或类似的应用程序。 https://sourceforge.net/projects/qpdf/files/qpdf/7.0.0/

我设法找到了部分解决方案。我可以使用 Bullzip 打印机的 COM 自动化将 PDF 文档(大)打印为 PDF 文档(小):

var BZUtil, BZSettings: OleVariant;
    TmpPrinterName, TmpString: String;
begin
  BZUtil:=CreateOleObject('Bullzip.PdfUtil');
  BZSettings:=CreateOleObject('Bullzip.PdfSettings');

  TmpPrinterName:=BZUtil.DefaultPrinterName;
  BZSettings.SetValue('Output', 'C:\TMP\reprinted_file.pdf');

  BZSettings.SetValue('ShowSettings', 'never');
  BZSettings.SetValue('ShowSaveAS', 'never');
  BZSettings.SetValue('ShowPDF', 'no');
  BZSettings.SetValue('ShowProgress', 'no');
  BZSettings.SetValue('ShowProgressFinished', 'no');
  BZSettings.SetValue('ConfirmOverwrite', 'no');

  BZSettings.SetValue('AfterPrintProgramMode', 'hide');
  BZSettings.SetValue('RunOnSuccessMode', 'hide');
  BZSettings.SetValue('RunOnErrorMode', 'hide');

  //No programs are specified
  //TmpString:=BZSettings.GetValue('RunOnError');
  //MessageDlg('RunOnError: '+TmpString, mtInformation, [mbOK], 0);
  //TmpString:=BZSettings.GetValue('RunOnSuccess');
  //MessageDlg('RunOnSuccess: '+TmpString, mtInformation, [mbOK], 0);
  //TmpString:=BZSettings.GetValue('AfterPrintProgram');
  //MessageDlg('AfterPrintProgram: '+TmpString, mtInformation, [mbOK], 0);
  BZSettings.WriteSettings(True);
  try
    BZUtil.PrintFile('C:\TMP\original_file.pdf', TmpPrinterName);
  except
    on E: Exception do begin
      MessageDlg(E.Message, mtError, [mbOK], 0);
      Exit;
    end;
  end;
  MessageDlg('All is OK', mtInformation, [mbOK], 0);

但是此解决方案存在一个严重的问题 - PrintFile 程序会打开新的 PDF 文件片刻然后将其关闭,但 Adob​​e Acrobat Reader DC 主屏幕仍保持打开状态。自动打开 Acrobat 主屏幕(欢迎消息、最近文件列表等)是一个非常烦人的问题,一般没有解决方案,例如Windows 注册表更改 https://forums.adobe.com/thread/1812870 无法解决这种情况下的问题。我确信 Bullzip PrintFile 是问题的原因 - 它以某种方式忽略了需要静默打印的设置。

所以 - 现在只有一个未解决的问题 - 如何在 Bullzip 静默打印期间处理 Acrobat Reader 的 opening/closing。我向 Bullzip 公司提出了错误请求,但处理我的问题可能需要一些时间。也许存在一些解决方法?

我犹豫 post 引用第 3 方库作为你问题的答案,但除了 PDFtk 之外,如果你使用的是 ReportBuilder,我建议你看看 TExtraDevices http://www.waler.com/products.htm.

此库以 Delphi 组件的形式呈现,您将其放置在与 ReportBuilder 组件相同的表单上,并添加了将报告输出为多种其他格式(包括 PDF)的选项。我在 D5/7 时代使用过它,就 PDF 报告而言,它是 "just worked":我从来没有对生成的 PDF 报告的大小有任何问题,这些报告大多只有几 Kb,但 ymmv。

它不是免费的(50 美元)并且最近没有更新,我想主要是因为没有理由它是 - 至少它自 Delphi 2009 年以来已经更新,所以可用"on the right side" 的 Ascii/Unicode 鸿沟。

如果 TExtraDevices 做你想做的事,它将避免对你的报告文件进行任何 post 处理的需要。

PDFForge/PDFCreator COM 接口允许以几乎最佳的较小尺寸重新打印 PDF 文件,并保留所有格式和变音符号。

    Queue: IQueue;
    Job: IPrintJob;
    PDFCreator: IPDFCreator;
begin  
    PDFCreator:=CoPdfCreatorObj.Create;
    PDFCreator.AddFileToQueue(AFileName);

    Queue:=CoQueue.Create;
    Queue.Initialize;
    Job:=Queue.NextJob;
    Job.SetProfileByGuid('DefaultGuid');
    Job.SetProfileSetting('OpenViewer', 'False');
    Job.SetProfileSetting('OpenWithPdfArchitect', 'False');
    Job.SetProfileSetting('ShowProgress', 'False');
    Job.SetProfileSetting('ShowQuickActions', 'False');
    //SkipPrintDialog is mentioned in the documentation
    //http://docs.pdfforge.org/pdfcreator/latest/en/pdfcreator/com-interface/reference/settings/
    //but might no be available in free versions of PDFForge/Create, that is why we don't even attempt to use it.
    //Job.SetProfileSetting('SkipPrintDialog', 'True');
    Job.ConvertTo(ANewFileName);

    Queue.ReleaseCom;
end;