There are two general strategies to outputting to a PDF in WPF. One is to output directly to a PDF which requires you traverse a visual or flow document and translate to a PDF. But the other and more common method is to output to XPS as an intermediary and then convert the XPS file to a PDF. The latter is preferred (to me) since .NET will do all the work converting to XPS which, as a document format, is closer to the structure of a PDF and therefore easier to convert.
It’s difficult to find a free, open source library to output to a PDF from a WPF program. I’ve been working on a project that needs direct output to a PDF, but everything I found was either very expensive or command line only (Ghostscript 9.06). But with a little help from Alex Hope O’Connor, I was pointed in the right direction.
Version 1.31 of the PDFSharp project included a beta project that converts an XPS to PDF. (For whatever reason it wasn’t included in 1.32 which has confused a lot of people directed to the project for this purpose.) Because it was in beta, many people had issues with it so I first applied fixes from here.
But I was still having issues with invalid bounding of a tiled brush, improperly scaled path geometry, and tiling of a vector image (leaving artifacts around the edges). I managed to work through these by opening the XPS with 7-zip and examining the raw page data. In this process, the PDF Reference for version 1.4 was indispensable in playing with the output PDF to make it work right.
Here’s some sample code that writes a
dp to an in-memory XPS and then passes that to the PDFSharp converter. (I got this snippet from here.) Note that you could just as easily pass in a
Visual or any of the other supported arguments to the Write method. In addition to including the PDFSharp.Xps library, you’ll also need to reference System.Printing and ReachFramework.
using System.IO; using System.IO.Packaging; using System.Windows.Xps.Packaging; using System.Windows.Xps; MemoryStream lMemoryStream = new MemoryStream(); Package package = Package.Open(lMemoryStream, FileMode.Create); XpsDocument doc = new XpsDocument(package); XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc); writer.Write(dp); doc.Close(); package.Close(); var pdfXpsDoc = PdfSharp.Xps.XpsModel.XpsDocument.Open(lMemoryStream); PdfSharp.Xps.XpsConverter.Convert(pdfXpsDoc, d.FileName, 0);
If you already have an XPS file, you can simply convert it with a single line:
PdfSharp.Xps.XpsConverter.Convert(sourceXpsFile, destPdfFile, 0);
- PDFSharpXpsFixes.diff (9KB) – Diff file for changes I made to PdfSharp.Xps project.
- PdfSharpXps.zip (984KB) – PdfSharp 1.32 and updated PdfSharp.Xps 1.31 project in VS2010 format.
- PdfSharpXpsBin.zip (406KB) – Compiled Dll / Xml doc.
Update (3/31/2013) – When I went to push out a release with my project using these updates, I noticed that there was a difference between Debug and Release mode, namely that Release didn’t work (partial pages). I pulled in PdfSharp 1.32, walked through every conditional compile, but couldn’t track down the issue. I ended up just making the PdfSharp.Xps project define the DEBUG constant and it worked. I’ve republished the downloads with these changes and also added a compiled version for convenience.