【问题标题】:Can a PDF be converted to a vector image format that can be printed from .NET?可以将 PDF 转换为可以从 .NET 打印的矢量图像格式吗?
【发布时间】:2011-10-30 03:01:57
【问题描述】:

我们有一个 .NET 应用程序,可以打印到真实打印机和 PDF,目前使用的是 PDFsharp,但如果有更好的选择,可以更改该部分。大多数输出​​是生成的文本或图像,但可以有一个或多个页面附加到末尾。该页面由最终用户以 PDF 格式提供。

当打印到纸上时,我们的用户使用预先打印的纸,但在导出 PDF 的情况下,我们将这些页面连接到最后,因为它们已经是 PDF 格式。

我们希望能够将这些 PDF 直接嵌入到打印流中,这样它们就不需要预先打印的纸张。但是,将 PDF 渲染到 GDI 页面 (System.Drawing.Graphics) 并没有什么好的选择。

是否有某种外部程序可以将 PDF 转换为矢量格式,该格式可以呈现到 GDI+ 页面,而不会因先转换为位图而降级?

【问题讨论】:

  • 我们做了类似的事情,但首先将 PDF 渲染为位图 - 使用高质量库和高 dpi 值(最小 305,最佳 1200)进行渲染时可以解决质量问题消耗一些内存/性能...
  • 前几天我注意到 PDFCreator (sourceforge.net/projects/pdfcreator) 尽管主要针对 PDF 创建,但实际上可用于将 PDF 文档打印成多种格式,包括 SVG 和位图图像。可能值得一看。
  • 这些 PDF 往往是带有一些线条的文本。他们还将连续打印 1000 多次。将 30 KB PDF 转换为 5 MB 位图可能会使整个打印队列崩溃。

标签: c# pdf gdi vector-graphics


【解决方案1】:

在一篇题为“How To Convert PDF to EMF In .NET”的文章中,我展示了如何使用我们的 PDFOne .NET 产品来做到这一点。 EMF 是矢量图形,您可以在打印机画布上渲染它们。

另一种更简单的替代方法是 PDF 覆盖,在另一篇标题为“PDF Overlay - Stitching PDF Pages Together in .NET”的文章中进行了说明。 PDFOne 允许在叠加层中进行 x-y 偏移,从而可以在边缘缝合页面。在此处引用的文章中,我通过将偏移量设置为零来将页面重叠在一起。您将其设置为页面宽度和高度。

免责声明:我为 Gnostice 工作。

【讨论】:

  • 我们实际上已经使用 Delphi 中的 Gnostice PDF 工具包将两个 PDF 拼接在一起,但我们正在将打印转移到 .NET,因此有更多可用选项。我在 Delphi 组件上没有看到 GetPageMetafile(),这是新增功能还是特定于 .NET 版本?
  • 另一个问题,这真的会生成独立的 .EMF 文件吗?我们实际上尝试使用过去应该让您打印到 .EMF 的产品,但输出文件仅在打印程序打开时才有效,因为它具有对原始程序的回调,当您关闭该程序时会消失.如果 PDFOne.NET 可以保证工作的 .EMF 文件,这听起来像是我正在寻找的解决方案。
  • PDFtoolkit 有两种方法 - RenderToDC(您可以向其传递打印机画布)和 RenderToStream(您向其提供 EMF 流)。这些方法生成类似于 PDFOne .NET 的 GetPageMetafile 的 EMF。在 PDFOne .NET 中,GetPageMetaFile 仅在 ProPlus 版本中可用。
  • 在上面给出的第一篇文章中,我展示了一个在 Windows 图片和传真查看器中打开的 EMF 文件的屏幕截图。它是一个独立的 EMF 文件。 PDFOne 还有一个打印机组件,可以直接将 PDF 页面打印到打印机上。您不需要使用 EMF 文件。不过,EMF 文件可以直接在打印机画布上播放。两者都可以。
【解决方案2】:

Ghostscript可以输出PostScript(矢量文件),可以直接发送到某些类型的打印机。例如,如果您使用的是支持 LPR 的打印机,则可以使用类似以下项目的内容将 PS 文件直接设置为该打印机:http://www.codeproject.com/KB/printing/lpr.aspx

还有一些商业选项可以打印 PDF(虽然我不确定内部机制是基于矢量还是基于位图),例如 http://www.tallcomponents.com/pdfcontrols2-features.aspxhttp://www.tallcomponents.com/pdfrasterizer3.aspx

【讨论】:

  • 我对他们使用的打印机没有任何直接控制权,我猜大多数是设置为 PCL,而不是 Postscript。另一个问题是他们希望将 PDF 放在其中一页的背面(因此是预印纸),因此不能作为单独的打印作业发送。
【解决方案3】:

我终于发现有一个选项可以满足我将矢量格式嵌入打印作业的一般要求,但它不适用于基于 GDI 的打印。

可以使用 .NET 中包含的 ReachFramework.dll 从 WPF 打印由 Microsoft XPS Writer 打印驱动程序创建的 XPS 文件格式。通过使用 WPF 代替 GDI 进行打印,可以将 XPS 文档页面嵌入到更大的打印文档中。

缺点是,WPF 打印的工作方式有很大不同,因此所有直接使用 Sytem.Drawing 命名空间中内容的支持代码都必须重新编写。

下面是如何嵌入 XPS 文档的基本概述:

打开文档:

XpsDocument xpsDoc = new XpsDocument(filename, System.IO.FileAccess.Read);
var document = xpsDoc.GetFixedDocumentSequence().DocumentPaginator;

// pass the document into a custom DocumentPaginator that will decide
// what order to print the pages:
var mypaginator = new myDocumentPaginator(new DocumentPaginator[] { document });

// pass the paginator into PrintDialog.PrintDocument() to do the actual printing:
new PrintDialog().PrintDocument(mypaginator, "printjobname");

然后创建 DocumentPaginator 的后代,它将执行您的实际打印。重写抽象方法,特别是 GetPage 应该以正确的顺序返回 DocumentPages。这是我的概念验证代码,它演示了如何将自定义内容附加到 Xps 文档列表中:

public override DocumentPage GetPage(int pageNumber)
{
    for (int i = 0; i < children.Count; i++)
    {
        if (pageNumber >= pageCounts[i])
            pageNumber -= pageCounts[i];
        else
            return FixFixedPage(children[i].GetPage(pageNumber));
    }
    if (pageNumber < PageCount)
    {
        DrawingVisual dv = new DrawingVisual();
        var dc = dv.Drawing.Append();
        dc = dv.RenderOpen();
        DoRender(pageNumber, dc); // some method to render stuff to the DrawingContext
        dc.Close();
        return new DocumentPage(dv);
    }
    return null;
}

当尝试打印到另一个 XPS 文档时,它给出了一个异常“FixedPage 不能包含另一个 FixedPage”,H.Alipourian 的帖子演示了如何修复它:http://social.msdn.microsoft.com/Forums/da/wpf/thread/841e804b-9130-4476-8709-0d2854c11582

private DocumentPage FixFixedPage(DocumentPage page)
{
    if (!(page.Visual is FixedPage))
        return page;

    // Create a new ContainerVisual as a new parent for page children
    var cv = new ContainerVisual();
    foreach (var child in ((FixedPage)page.Visual).Children)
    {
        // Make a shallow clone of the child using reflection
        var childClone = (UIElement)child.GetType().GetMethod(
            "MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic
            ).Invoke(child, null);

        // Setting the parent of the cloned child to the created ContainerVisual
        // by using Reflection.
        // WARNING: If we use Add and Remove methods on the FixedPage.Children,
        // for some reason it will throw an exception concerning event handlers
        // after the printing job has finished.
        var parentField = childClone.GetType().GetField(
            "_parent", BindingFlags.Instance | BindingFlags.NonPublic);
        if (parentField != null)
        {
            parentField.SetValue(childClone, null);
            cv.Children.Add(childClone);
        }
    }

    return new DocumentPage(cv, page.Size, page.BleedBox, page.ContentBox);
}

很抱歉,这不是完全编译代码,我只是想概述一下使其工作所需的代码片段,以便其他人在所有需要组合在一起以使其工作的不同部分上抢占先机。尝试创建一个更通用的解决方案将比此答案的范围复杂得多。

【讨论】:

    【解决方案4】:

    虽然不是开源的,也不是 .NET 本机(我相信基于 Delphi,但提供了预编译的 .NET 库),Quick PDF 可以将 PDF 呈现为 EMF 文件,您可以将其加载到 Graphics 对象中。

    【讨论】:

      猜你喜欢
      • 2011-05-05
      • 2022-08-19
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 2017-07-25
      • 2015-09-16
      • 2012-07-31
      • 1970-01-01
      相关资源
      最近更新 更多