当打印到纸张时,我们的用户使用预先印刷的纸张,但在导出PDF的情况下,我们将这些页面连接到末尾,因为它们已经是PDF格式。
我们希望能够直接嵌入这些PDF到打印流中,以便它们不需要预先印刷的纸张。然而,没有真正好的方法将PDF呈现为GDI页面(System.Drawing.Graphics)。
是否有一种矢量格式可以通过某种外部程序将PDF转换为,在被转换为位图之前可以呈现为GDI+页面而不会降低质量?
我终于找到了一个选项,可以满足我的一般需求,将矢量格式嵌入打印作业中,但它不能与基于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);
}
抱歉,这并不是完整的编译代码,我只是想提供一些关键代码片段的概述,让其他人更容易地开始整合这些需要结合起来才能使其正常运作的不同部分。尝试创建一个更通用的解决方案比本回答的范围要复杂得多。
Ghostscript 可以输出 PostScript(矢量文件),可以直接发送到某些类型的打印机上。例如,如果您使用支持 LPR 的打印机,则可以使用类似于此项目的方式将 PS 文件直接设置为该打印机:http://www.codeproject.com/KB/printing/lpr.aspx
还有一些商业选项可以打印 PDF(虽然我不确定内部机制是矢量还是位图基础),例如 http://www.tallcomponents.com/pdfcontrols2-features.aspx 或 http://www.tallcomponents.com/pdfrasterizer3.aspx
虽然不是开源的,也不是 .NET 原生的(我相信它是基于 Delphi 的,但提供了一个预编译的 .NET 库),Quick PDF 可以将 PDF 渲染为 EMF 文件,您可以将其加载到 Graphics 对象中。