Output to PDF in WPF (for free!)

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 DocumentPaginator called 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.

[csharp]
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);
[/csharp]

If you already have an XPS file, you can simply convert it with a single line:
[csharp]
PdfSharp.Xps.XpsConverter.Convert(sourceXpsFile, destPdfFile, 0);
[/csharp]

 Downloads

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.

55 thoughts on “Output to PDF in WPF (for free!)

  1. For all the people that convert more than 50 pages… in debug mode (and because it only works in debug mode at the moment, you have to remove this in Xpsconverter.cs
    #if DEBUG
    // stop at page…
    if (pageIndex == 50)
    break;
    #endif

  2. Hi Nathan,

    Good demostration!

    But could you show us the inverse mode: convert PDF input to XPS output?.

    Have you written some article or code example?

    Thank you for you

  3. No, I haven’t. But if you’re in WPF, the only supported rendered document format is Xps. Just output to Xps in the first place.

  4. Hi,
    Solution is awsome!! but it also consumes memory. After generating same file for 100 times program used around 200MB of additional memory. I changed library to workaround this issue. I created static method in FontDescriptorStock which sets global field to null and call it at the end of convert method. I want to note that this was my first dive into code of this library so I cannot guarantee this will work for your. Last note, build targeting release because pdfs created with debug library are huge.

  5. Thanks Nathan for all the work you put into this. It worked great in my project.
    If anybody is interested, I added a method for saving the PDF to memory stream instead of to disk file (added to the PdfSharp.Xps.XpsConverter class):

    public static void ConvertToPdfInMemory(XpsDocument xpsDocument, MemoryStream pdfMemoryStream, int docIndex)
    {
    if (xpsDocument == null)
    throw new ArgumentNullException("xpsDocument");
    if (pdfMemoryStream == null)
    throw new ArgumentNullException("pdfMemoryStream");

    FixedDocument fixedDocument = xpsDocument.GetDocument();
    PdfDocument pdfDocument = new PdfDocument();
    PdfRenderer renderer = new PdfRenderer();

    int pageIndex = 0;
    foreach (FixedPage page in fixedDocument.Pages)
    {
    if (page == null)
    continue;
    PdfPage pdfPage = renderer.CreatePage(pdfDocument, page);
    renderer.RenderPage(pdfPage, page);
    pageIndex++;
    }
    pdfDocument.Save(pdfMemoryStream, false);
    }

  6. Hi,
    you’ve done a great job there.

    But i have one Problem while converting with your fixed PDFSharp Libary.

    When I try to convert an .XPS with an Canvas with RenderTransformation like e.g. “1.333333333,0,0,1.333333333,0,0”, wich includes an ImageBrush with its own Transformation, the Image in the .PDF has the wrong size

    ImageBrush:

    Canvas:

  7. <ImageBrush x:Key=”b1″ ViewportUnits=”Absolute” TileMode=”None” ViewboxUnits=”Absolute” Viewbox=”0,0,384,639.07″ Viewport=”0,0,1,1″ ImageSource=”/Resources/710cd81e-3ce0-4648-8dc1-13a2f49f7a2a.jpg” Transform=”288,0,0,479.05,306.25,351.35″ />

    <Canvas RenderTransform=”1.333333333,0,0,1.333333333,0,0″>
    <Path Fill=”{StaticResource b1}” Data=”M306.25,351.35L594.25,351.35 594.25,830.4 306.25,830.4Z” />
    </Canvas>

  8. Found the Error

    It was just a rounding error

    When writing out the transformation matrix only the first 3 digits after the comma were put into the .PDF File, so I changed that and now it works perfect 🙂

  9. Hi,

    your Changes are working well for me except for one thing.

    When i try to convert a paragraph with Text=”l” and FontFamily=”Wingdings” then i get a filled circle in the XPS but a square (missing character) but the right font (Wingdings) in the PDF.
    When i copy the circle (out of word) and put it in the Paragraph text, then it prints correct in both XPS and PDF.
    Could this be a unicode problem?

  10. After some debugging i can confirm this is a unicode problem.
    The bullet(TextMarkerStyle.Disc in WPF) for the listItem is unicode Ÿ. This results in a square in the PDF if i convert it to  then it works.

    This dirty fix works for me to show the bullet after the conversion from xps to pdf:
    XpsParser.Glyphs.cs(66)
    glyphs.UnicodeString = this.reader.Value == “Ÿ” ? “” : this.reader.Value;
    The value between the first quotes is Ÿ (not working in PDF)
    The value between the second quotes  (works)

    This could be extended to a small list but this fix was enough for me.

  11. For some reason, when I apply this solution to my project, I can only see first 4 lines of the content that I am trying to convert to PDF. Any ideas as to what may be causing this?

  12. If anynone got a ” Resource not found” exception thats because the dictionary isn’t filled with the resource of the *.dict files. If made some changes to load the resources now it works fine.

  13. Great Work! It seems to me, as the pdfsharp-wpf project settings included the ‘optimize code’ Tag in release mode. I have the experience, that ‘optimize code’ does dirty stuff like overjumping some if-else sequences.

  14. I have been looking at this XPS to PDF code, and everything is great. I took the time to test compatibility over all XPS conformance samples, and in most cases, it was working extremely good. There were of course a few content format discrepancies, but over all, this code is clean and very fast.
    There were however some XPS file types that resulted into “Resource not found”. I am going to try to remedy what Marcel has suggested about making sure that resource dict files are loaded.
    Marcel, can you post you changes, in case I fail.
    Thanks.
    –This code is a life saver–

  15. Not sure if you are still looking at this. Trying to figure out why a 2MB xps file is coming out as a 20MB pdf file? If I print the same document to a typical PDF printer, instead of doing it this way, it also is only about 2MB in size. Any ideas?

  16. Hi Haro,
    I’m coming across the same error, for my multi-page XPS documents, I get the error Static Reosurce not found:{StaticResource R0}
    Did u find any solution to that?
    Thanks in advance

  17. Thanks sir , really this solution helps me a lot but here one problem is that when i convert multiple pages xps file in which different fonts,contents etc are used but the generated PDF is not same as xps file bundle of contents are missing in generated pdf file so kindly anyone have a idea about such type of issue

  18. When we use PdfSharp.Xps.XpsConverter.Convert(xpsfile, pdfile, 0) to convert wpf control to PDF format, the images inside wpf control lost.
    Does anyone face the same problem lost images when converting from xps to pdf?
    Does anyone has solution for it? Thx!

  19. Hi,

    When i try to convert Text in FontFamily=”Wingdings” then i get correct symbols in XPS but a square (missing character) but the right font (Wingdings) in the PDF.
    What can i do?

  20. @Ruedi: It’s hard to say. Sounds like an encoding issue at some point in the conversion since you’re probably using Unicode chars.

  21. Hi,

    When converting a .xps with Hebrew characters, the text does not apears correct in the final .pdf. The orientation should be LTR and it turns LTR.

    Can you help with this issue?

    Best Regards

  22. Hey, thanks for sharing your efforts it’s been a great help.

    I noticed that the viewbox on the brush was not taken into account. this meant that if you get a document that contains a cropped image in word, (save/print to xps) and pass it through this you get an incorrect image. it will be the top left section of the full source image rather than the cropped section.

    I fixed that by changing the bbox but found i was getting results out by a pixel (maybe 2?)

    so I added a hacky fix and weirdly noticed almost the same code under buildform()

    var box = new PdfRectangle(brush.Viewbox.X, brush.Viewbox.Y + brush.Viewbox.Height – 1, brush.Viewbox.X + brush.Viewbox.Width – 1, brush.Viewbox.Y);

    is this to cope with the same thing I was seeing or is this perhaps the cause of my issue?

    either way the viewbox needs to be taken account of, and for me changing the code under tiling brush pattern for bbox to use the viewbox x/y/width/height and than adding a translate to match does the job.

  23. just to clarify I changed these two at the top of PdfTilingPattern BuildPattern(ImageBrush brush, XMatrix transform)

    XRect bbox = new XRect(brush.Viewbox.X, brush.Viewbox.Y, brush.Viewbox.Width, brush.Viewbox.Height);

    matrix.TranslatePrepend((brush.Viewport.X – brush.Viewbox.X)+2, (brush.Viewport.Y – brush.Viewbox.Y)+2);

  24. Hi,

    We have predefined templates in which the PDF has to be generated for invoice data.Any suggestion on can this solution:WPF to XPS,XPS to PDF be used to generate the PDF in predefined format. And can this be triggerred as background job in WPF?

    Thanks in Advance

  25. Hi,

    When converting a .xps with Hebrew characters, the text does not apears correct in the final .pdf. The orientation should be RTL and it turns LTR.

    Can you help with this issue?

    Best Regards

  26. @Miguel: I am facing the same problem. I have a Canvas having labels in Arabic language. While exporting in PDF, their orientation changes and PDF get distorted. Have you found any solution?

    @All: Can you help with this issue?

    Thanks

  27. Hi,
    I have found another issue with the converter that it does not support CMYP or GrayScale colors? Any idea or fix for that would be helpful.
    Thanks

  28. I’m still getting the error “StaticResource not found: {StaticResource R0}”. How do I resolve this? Thanks!

  29. Hi Nathan I am Using PdfSharp to Convert XPS to PDF in my Windows Application
    Its Working Fine at my Side But Giving “Static Resource Not Found {RO}” on another machine having Windows-7
    How Do I Resolve This
    Thanks!

  30. @Ravi: Do you have any other details? Sounds like it might be an issue with the local resources available to the xaml during rendering.

  31. Hi. Im new on this, i made an app using this dll but when i convert an XPS file it only converts the first 50 pages… Im using this line to make the job (winforms):

    PdfSharp.Xps.XpsConverter.Convert(sourceXpsFile, destPdfFile, 0);

    What’s wrong? what i have to do to convert the whole documment?

  32. I’m not sure, I’ve never tried to use it to generate more than a couple pages. I would recommend pulling in the source and walking through to see where it’s choking.

    What kind of error are you getting anyways?

  33. Nathan im not getting an exception or something. It works properly but, if the document has 3 pages, it converts the 3 pages like a charm. If the document has 53 pages, it only converts the first 50 pages, with no exceptions, no messages. I think its a limitation stated in the dll.

  34. Hi Nathan,
    I am using this library to convert xps to pdf document. The library is working great, except for few xps files(xps with watermark background) it is throwing exception ‘ArgumentException: StaticResource not found: {StaticResource R0}’.

    I started digging into this problem and ended up in below code. The below code is not reading ‘Resources_D1.dict’ file. It doesn’t go inside ‘while (this.reader.IsStartElement())’ loop. Can you please guide me in solving this problem??

    partial class XpsParser
    {
    ///
    /// Parses a ResourceDictionary element.
    ///
    void ParseResourceDictionary(ResourceDictionary dict)
    {
    //Debug.Assert(this.reader.Name == “ResourceDictionary”);
    Debug.Assert(this.reader.Name.Contains(“Resource”));
    try
    {
    bool isEmptyElement = this.reader.IsEmptyElement;
    //ResourceDictionary dict = new ResourceDictionary();
    while (MoveToNextAttribute())
    {
    switch (this.reader.Name)
    {
    case “Source”:
    dict.Source = this.reader.Value;
    break;

    default:
    UnexpectedAttribute(this.reader.Name);
    break;
    }
    }

    if (!isEmptyElement)
    {
    MoveToNextElement();
    while (this.reader.IsStartElement())
    {
    switch (this.reader.Name)
    {
    case "ImageBrush":
    ImageBrush ibrush = ParseImageBrush();
    dict.elements[ibrush.Key] = ibrush;
    ibrush.Parent = dict;
    break;

    case "LinearGradientBrush":
    LinearGradientBrush lbrush = ParseLinearGradientBrush();
    dict.elements[lbrush.Key] = lbrush;
    lbrush.Parent = dict;
    break;

    case "RadialGradientBrush":
    RadialGradientBrush rbrush = ParseRadialGradientBrush();
    dict.elements[rbrush.Key] = rbrush;
    rbrush.Parent = dict;
    break;

    case "VisualBrush":
    VisualBrush vbrush = ParseVisualBrush();
    dict.elements[vbrush.Key] = vbrush;
    vbrush.Parent = dict;
    break;

    case "SolidColorBrush":
    VisualBrush sbrush = ParseVisualBrush();
    dict.elements[sbrush.Key] = sbrush;
    sbrush.Parent = dict;
    break;

    case "MatrixTransform":
    MatrixTransform transform = ParseMatrixTransform();
    dict.elements[transform.Key] = transform;
    transform.Parent = dict;
    break;

    case "PathGeometry":
    PathGeometry geo = ParsePathGeometry();
    dict.elements[geo.Key] = geo;
    geo.Parent = dict;
    break;

    case "Path":
    Path path = ParsePath();
    dict.elements[path.Key] = path;
    path.Parent = dict;
    break;

    case "Glyphs":
    Glyphs glyphs = ParseGlyphs();
    dict.elements[glyphs.Key] = glyphs;
    glyphs.Parent = dict;
    break;

    case "Canvas":
    Canvas canvas = ParseCanvas();
    dict.elements[canvas.Key] = canvas;
    canvas.Parent = dict;
    break;

    default:
    Debugger.Break();
    break;
    }
    }
    }
    MoveToNextElement();
    //return dict;
    }
    finally
    {
    }
    }
    }

    }

  35. Hi Nathan,
    I am using your library for converting xps to pdf. The library is working great. But for some xps, it is throwing error saying ‘static rescource R0 not found.’.
    I started digging into source code. I came across below code. The source dictionary path is set but, it is not reading the elements. The while block is not executed. Could please help me solve this problem???

    void ParseResourceDictionary(ResourceDictionary dict)
    {
    //Debug.Assert(this.reader.Name == “ResourceDictionary”);
    Debug.Assert(this.reader.Name.Contains(“Resource”));
    try
    {
    bool isEmptyElement = this.reader.IsEmptyElement;
    //ResourceDictionary dict = new ResourceDictionary();
    while (MoveToNextAttribute())
    {
    switch (this.reader.Name)
    {
    case “Source”:
    dict.Source = this.reader.Value;
    break;

    default:
    UnexpectedAttribute(this.reader.Name);
    break;
    }
    }

    if (!isEmptyElement)
    {
    MoveToNextElement();
    while (this.reader.IsStartElement())
    {
    switch (this.reader.Name)
    {
    case "ImageBrush":
    ImageBrush ibrush = ParseImageBrush();
    dict.elements[ibrush.Key] = ibrush;
    ibrush.Parent = dict;
    break;

    case "LinearGradientBrush":
    LinearGradientBrush lbrush = ParseLinearGradientBrush();
    dict.elements[lbrush.Key] = lbrush;
    lbrush.Parent = dict;
    break;

    case "RadialGradientBrush":
    RadialGradientBrush rbrush = ParseRadialGradientBrush();
    dict.elements[rbrush.Key] = rbrush;
    rbrush.Parent = dict;
    break;

    case "VisualBrush":
    VisualBrush vbrush = ParseVisualBrush();
    dict.elements[vbrush.Key] = vbrush;
    vbrush.Parent = dict;
    break;

    case "SolidColorBrush":
    VisualBrush sbrush = ParseVisualBrush();
    dict.elements[sbrush.Key] = sbrush;
    sbrush.Parent = dict;
    break;

    case "MatrixTransform":
    MatrixTransform transform = ParseMatrixTransform();
    dict.elements[transform.Key] = transform;
    transform.Parent = dict;
    break;

    case "PathGeometry":
    PathGeometry geo = ParsePathGeometry();
    dict.elements[geo.Key] = geo;
    geo.Parent = dict;
    break;

    case "Path":
    Path path = ParsePath();
    dict.elements[path.Key] = path;
    path.Parent = dict;
    break;

    case "Glyphs":
    Glyphs glyphs = ParseGlyphs();
    dict.elements[glyphs.Key] = glyphs;
    glyphs.Parent = dict;
    break;

    case "Canvas":
    Canvas canvas = ParseCanvas();
    dict.elements[canvas.Key] = canvas;
    canvas.Parent = dict;
    break;

    default:
    Debugger.Break();
    break;
    }
    }
    }
    MoveToNextElement();
    //return dict;
    }
    finally
    {
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.