Creating XPS Documents from Visual, plus an insider look at an XPS Document

In the last article blogs.msdn.com/fyuan/archive/2005/09/11/463708, we discussed how to generate XPS Documents from Win32 applications using the XPS Document Writer. The procedure is very simple, can you simply print whatever contents you have using GDI API. Now let’s try to generate XPS Documents from WPF (Windows Presentation Foundation) using its new API.

 

As lots of stuff is new, we will build a simple console application, but build step by step to illustrate what exact is involved. Starting by creating a blank C# console application using Visual Studio, here is what we will get:

 

using System;

using System.Collections.Generic;

using System.Text;

namespace BasicXps

{

    class Program

    {

        static void Main(string[] args)

        {

        }

    }

}

There are a few ways to create XPS Documents using XPF API, you can access the XPS Document container at stream level and build thing piece by piece, you can convert from a WPF Visual object, or your can convert from a WPF UIElement object. We will focus on the conversion from Visual approach. Let’s try to create a Visual which has a yellow rectangle and a pink ellipse as in the last example. The WPF code would be:

 

        static Visual CreateVisual()

        {

            const double Inch = 96;

            DrawingVisual visual = new DrawingVisual();

            DrawingContext dc = visual.RenderOpen();

            Pen bluePen = new Pen(Brushes.Blue, 1);

  dc.DrawRectangle(Brushes.Yellow, bluePen, new Rect(Inch / 2, Inch / 2, Inch * 1.5, Inch * 1.5));

            Brush pinkBrush = new SolidColorBrush(Color.FromArgb(128, 255, 0, 255));

            Pen blackPen = new Pen(Brushes.Black, 1);

        dc.DrawEllipse(pinkBrush, blackPen, new Point(Inch * 2.25, Inch * 2), Inch * 1.25, Inch);

            dc.Close();

            return visual;

        }

DrawingVisual is one main type of Visual supported by WPF. From a DrawingVisual, you can open a DrawingContext and draw graphics primitives into it. DrawingContext is much richer than XPS in terms of graphics primitives supported, but yet every Visual, either generated through DrawingContext or otherwise can be converted to XPS, in certain form. Here the code draws a rectangle and an ellipse. Note that the pink brush is created using Color.FromArgb(128, 255, 0, 255), 50% pink to be exact.

 

Classes like DrawingVisual, Pen, Brush, etc., are defined in PresentationCore assembly, which depends on WindowsBase assembly. So we need to add references to them, together with the necessary namespaces:

 

// Needed by CreateVisual, from WindowsBase, PresentationCore.

using System.Windows;

using System.Windows.Media;

The functionality to generate XPS Document resides in ReachFramework assembly, which depends on System.Printing assembly. So we need to add two more assembly references, together with a few more namespaces:

 

// Needed by VisualToXps

using System.IO.Packaging;

using System.IO;

// Need by VisualToXps, from ReachFramework, which needs System.Printing and PresentationFramework

using System.Windows.Xps.Packaging;

using System.Printing.PrintSubSystem;

The code to generate an XPS Document from a Visual is shown below:

        static void VisualToXps(string fileName, Visual visual)

        {

            Package package = Package.Open(fileName, FileMode.Create);

            {

                XpsDocument doc = new XpsDocument(package);

                {

                    XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(doc);

                    writer.Write(CreateVisual());

                }

                doc.Close();

            }

            package.Close();

        }

The code above first creates a container (in ZIP format). A container can contain multiple XpsDocument, only one is used here. An XpsDocument can have multiple pages. A single page is generated here because we only call XpsDocumentWriter once with a single Visual.

 

With two new helper functions, we can change the Main function to call both of them to finish our project:

 

        static void Main(string[] args)

        {

            VisualToXps("BasicXps.container", CreateVisual());

        }

Visually the container generated by this code is very similar to the one generated by the XPS Document Writer. The difference can be explained by the two different creation methods, one dragging using mouse, and the other specifying by inches in C# code.

 

The XPS Document generated by this code is much smaller than the one created by the XPS Document Writer, it’s only 2,496 bytes in size. The difference is mainly caused by font embedding, PrintTicket, splitting of stroking and filling, and clipping. The container is small enough that we can show everything here. Here is the list of components in the container:

 

  • [Content_Types].xml, 543 bytes
  • FixedDocumentSequence/FixedDocumentSequeuce_1.xaml, 168 bytes
  • FixedDocuments/FixedDocument_1.xaml, 138 bytes
  • FixedPages/FixedPage_1.xaml,
  • _rels/.rels, 308 bytes.

 

[Context_Types].xml specifies the syntax of the container:

 

<?xml version="1.0" encoding="utf-8"?>

<Types xmlns="https://schemas.microsoft.com/package/2005/06/content-types">

    <Default Extension="xaml" ContentType="application/vnd.ms-package.xps-fixeddocumentsequence+xml" />

    <Default Extension="rels" ContentType="application/vnd.ms-package.relationships+xml" />

    <Override PartName="/FIXEDDOCUMENTS/FIXEDDOCUMENT_1.XAML"

        ContentType="application/vnd.ms-package.xps-fixeddocument+xml" />

    <Override PartName="/FIXEDPAGES/FIXEDPAGE_1.XAML"

        ContentType="application/vnd.ms-package.xps-fixedpage+xml" />

</Types>

FixedDocumentdSequence_1.xaml says it only has FixedDocument_1.xaml as its only document:

<FixedDocumentSequence xmlns="https://schemas.microsoft.com/xps/2005/06">

    <DocumentReference Source="../FixedDocuments/FixedDocument_1.xaml" />

</FixedDocumentSequence>

FixedDocumentd_1.xaml says it only has FixedPage_1.xaml as its only page:

 

<FixedDocument xmlns="https://schemas.microsoft.com/xps/2005/06">

    <PageContent Source="../FixedPages/FixedPage_1.xaml" />

</FixedDocument>

 

FixedPage_1.xaml contains the XPS representation of the Visual we are writing:

<FixedPage xmlns="https://schemas.microsoft.com/xps/2005/06"

    xmlns:x="https://schemas.microsoft.com/xps/2005/06/resourcedictionary-key"

    xml:lang="en-us" Width="816" Height="1056">

    <FixedPage.Resources>

        <ResourceDictionary></ResourceDictionary>

    </FixedPage.Resources>

    <Path Stroke="#FF0000FF" StrokeThickness="1" StrokeMiterLimit="10" Fill="#FFFFFF00"

        Data="F0 M 48, 48 L 192, 48 192, 192 48, 192Z" />

    <Path Stroke="#FF000000" StrokeThickness="1" StrokeMiterLimit="10" Fill="#80FF00FF"

        Data="F0 M 336, 192 C 336, 245.02 282.27, 288 216, 288 C 149.73, 288 96, 245.02 96, 192

        C 96, 138.98 149.73, 96 216, 96 C 282.27, 96 336, 138.98 336, 192Z" />

</FixedPage>

 

The FixedPage element is quite similar, but much simpler than the version generated by the XPS Document Writer. The major new addition is the ResourceDictionary. Common elements can be put into the resource dictionary, given a name, and then reused. In this simple case, the resource dictionary happens to be empty.

 

Finally, the .rels binds everything together:

 

<?xml version="1.0" encoding="utf-8"?>

<Relationships xmlns="https://schemas.microsoft.com/package/2005/06/relationships">

    <Relationship Type="https://schemas.microsoft.com/xps/2005/06/fixedrepresentation"

        Target="/FixedDocumentSequences/FixedDocumentSequence_1.xaml" Id="RFBE031A033A8834A" />

</Relationships>