可以将PDF转换为可从.NET打印的矢量图像格式吗?

6
我们有一个.NET应用程序,可以打印到真实打印机和PDF文件,目前使用PDFsharp,但如果有更好的选项,这一部分可以更改。大部分输出是生成的文本或图像,但可能会有一个或多个页面附加在末尾。这些页面由最终用户以PDF格式提供。
当打印到纸张时,我们的用户使用预先印刷的纸张,但在导出PDF的情况下,我们将这些页面连接到末尾,因为它们已经是PDF格式。
我们希望能够直接嵌入这些PDF到打印流中,以便它们不需要预先印刷的纸张。然而,没有真正好的方法将PDF呈现为GDI页面(System.Drawing.Graphics)。
是否有一种矢量格式可以通过某种外部程序将PDF转换为,在被转换为位图之前可以呈现为GDI+页面而不会降低质量?

我们做类似的事情,但首先将PDF渲染为位图 - 渲染时使用高质量库和高dpi值(最小305,最佳1200)可以解决质量问题,但会消耗一些内存/性能... - Yahia
1
我最近注意到PDFCreator(http://sourceforge.net/projects/pdfcreator/)虽然主要用于创建PDF,但实际上也可以用来将PDF文档打印成各种格式,包括SVG和位图图像。值得一试。 - Dave Cluderay
这些PDF文件通常是带有一些线条艺术的文本。它们也会在一次运行中被打印1000多次。将30 KB的PDF转换为5 MB的位图可能会使整个打印队列崩溃。 - Bryce Wagner
4个回答

2
在一篇名为“.NET中如何将PDF转换为EMF”的文章中,我展示了如何使用我们的PDFOne .NET产品进行转换。 EMFs是矢量图形,您可以在打印机画布上呈现它们。
另一个更简单的选择是PDF叠加,在另一篇名为“.NET中的PDF叠加-将PDF页面拼接在一起”的文章中有详细解释。 PDFOne允许在叠加中设置x-y偏移量,从而允许您在边缘拼接页面。在这里引用的文章中,我通过将偏移量设置为零来将页面重叠在彼此上方。您需要将其设置为页面宽度和高度。
免责声明:我为Gnostice工作。

我们实际上已经在Delphi中使用了Gnostice PDF工具包来拼接这两个PDF文件,但是我们正在将打印功能迁移到.NET平台,因此有更多的选择。我没有在Delphi组件中看到GetPageMetafile()方法,这是一个新的添加还是只存在于.NET版本中? - Bryce Wagner
另外一个问题,这是否会生成独立的.EMF文件?我们曾经尝试过使用一个产品,据说可以让你打印到.EMF,但输出文件只有在打印程序打开时才能工作,因为它有回调到原始程序,当您关闭该程序时这些回调就消失了。如果PDFOne.NET可以保证有效的.EMF文件,那听起来就像是我正在寻找的解决方案。 - Bryce Wagner
PDFtoolkit有两种方法——RenderToDC(可以传递打印机画布)和RenderToStream(可以提供EMF流)。这些方法生成类似于PDFOne .NET的GetPageMetafile的EMF。在PDFOne .NET中,GetPageMetaFile仅在ProPlus版本中可用。 - BZ1
在上面的第一篇文章中,我展示了一个在Windows Picture and Fax Viewer中打开的EMF文件的截图。它是一个独立的EMF文件。PDFOne还有一个打印机组件,可以直接将PDF页面打印到打印机上。你不需要使用EMF文件。虽然EMF文件可以直接在打印机画布上播放,但两者都可以工作。 - BZ1

0

我终于找到了一个选项,可以满足我的一般需求,将矢量格式嵌入打印作业中,但它不能与基于GDI的打印兼容。

由Microsoft XPS Writer打印驱动程序创建的XPS文件格式可以使用.NET中包含的ReachFramework.dll从WPF中打印。通过使用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);
}

抱歉,这并不是完整的编译代码,我只是想提供一些关键代码片段的概述,让其他人更容易地开始整合这些需要结合起来才能使其正常运作的不同部分。尝试创建一个更通用的解决方案比本回答的范围要复杂得多。


0

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文件在其中一页的背面(因此需要预印的纸张),因此它不能作为单独的打印任务发送。 - Bryce Wagner

-1

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接