Search code examples
c#.netpdfflowdocumentxps

How to convert a FlowDocument or XPS to PDF using C# .NET in 2023


I'm working on application that builds FlowDocuments and exports them to various formats. We can export to XPS easily enough and PDF is possible using the Microsoft Print to PDF driver, but the latter has limitations that provide a poor user experience. Namely, it provides its own 'Save As' dialog and doesn't provide any notification upon completion when printing large documents. Therefore, I'm looking at alternatives to either convert FlowDocuments or XPS documents to PDF.

Most sources (a selection linked below) suggest using PDFSharp library using the PdfSharp.Xps.XpsConverter.Convert method to convert an XPS to PDF, with code samples similar to the following:

public void SaveAsPdf(FlowDocument flowDoc, string path)
{
    using var stream = new MemoryStream();
    using var package = Package.Open(stream, FileMode.Create);
    using var xpsDoc = new XpsDocument(package);
    var xpsDocWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
    xpsDocWriter.Write(((IDocumentPaginatorSource)flowDoc).DocumentPaginator);

    using var pdfXpsDoc = PdfSharp.Xps.XpsModel.XpsDocument.Open(stream);
    PdfSharp.Xps.XpsConverter.Convert(pdfXpsDoc, path, 0);
}

However, whatever NuGet package I try to install I am unable to resolve the PdfSharp.Xps namespace. I've tried searching various code repositories on Github for the XpsConverter class but I can't seem to find any. From what I can gather the XPS features were a beta in version 1.31 of PDF Sharp but were included in version 1.32 and haven't been today. I'd rather not use beta code in our solution if we can avoid it. Many of these articles 10 years old and I suspect that the information may be out of date.

Therefore, I'm asking, as of 2023, how can I convert a FlowDocument or an XPS document to PDF without the usability limitations of Microsoft Print to PDF driver mentioned above. Acceptable answers including the use of third party libraries, but ideally they are open source, stable and easily accessible via NuGet. Example code would also be welcome.

Sources


Solution

  • I think I've figured out. There's a couple of things that confused me:

    1. The standard PDFSharp NuGet package doesn't provide any of the XPS conversion features.
    2. There's a mistake in my code sample.

    I've found the following NuGet package that provides the XpsConverter class: XpsToPdf. This packages wasn't mentioned in any of the sources above, probably because the package was first published in 2020. I'm grateful that it exists now.

    Also there was a mistake in my sample code in the question. I was getting a System.IO.FileFormatException: 'Archive file cannot be size 0.' exception from the PdfSharp.Xps.XpsModel.XpsDocument.Open. Turns out I need to close the Package and XpsDocument objects before data is written to the stream. Therefore the correct code sample is:

    public void SaveAsPdf(FlowDocument flowDoc, string path)
    {
        using var stream = new MemoryStream();
        using (var package = Package.Open(stream, FileMode.Create))
        using (var xpsDoc = new XpsDocument(package))
        {
            var xpsDocWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
            xpsDocWriter.Write(((IDocumentPaginatorSource)flowDoc).DocumentPaginator);
        }
    
        using var pdfXpsDoc = PdfSharp.Xps.XpsModel.XpsDocument.Open(stream);
        PdfSharp.Xps.XpsConverter.Convert(pdfXpsDoc, path, 0);
    }
    

    So in summary, to answer the original question:

    1. Install the NuGet package XpsToPdf by Thomas Hoevel and Nathan Jones.
    2. Use the above code sample to convert a FlowDocument to XPS and then to PDF.