Introduction

Often we need to print the screen or some parts of the screen. This is pretty useful for printing graphic reports, like charts. In this article, we’re going to have a look at how to print a visual element with the WPF Visual Print. The essential part is serializing an XAML element to an XPS document, and converting the XPS document to a FlowDocument. Then we print and print preview the FlowDocument with the FlowDocument Viewer.

What is an XPS Document

The XML Paper Specification (XPS) format is basically an electronic representation of digital documents based on XML. It is a paginated fixed-layout format that retains the look and feel of your electronic documents. XPS documents can be easily created once you have the right software installed, like Microsoft Word.

The parts of an XPS document are organized in a logical hierarchy with the FixedDocumentSequence part at the top. An XPS document package may contain more than one document, and the sequence of these documents is described by the FixedDocumentSequence part. The FixedDocumentSequence part references the FixedDocument parts that, in turn, reference the pages of each document within the package.

Each FixedDocument part references the pages of that document as FixedPage parts. Each FixedPage part contains the text markup and layout of a page in the document as well as references to images, fonts, and other custom resources used in the page. Resources such as images and fonts are stored in the package but outside of the FixedPage part, allowing them to be shared by other pages. This is especially useful for font resources, but it could also be useful for any image resource that is used on more than one page, such as a watermark or letterhead logo.

I know it’s pretty boring for you to read these definitions. But I have to bring them out, because all these definitions will be used in the WPF Visual Print code.

Serialize a Visual Component to XPS Document

XPS documents are stored in a file, called a package, that conforms to the Open Packaging Conventions and are composed of a set of document components known as parts. A package has a physical and a logical organization. The physical organization consists of the document parts and folders inside the package, and the logical organization is a hierarchy described by the document parts. The XML Paper Specification applies a specific organization and naming convention to the logical layer for XPS documents.

WPF wraps the XPS API in the XPSSerializationManager class. Thus WPF can create an XPS document by serializing the XAML element page to an XPS document. Here is our serialization code:

Custom Document

When you serialize an XAML element to an XPS document, it’s always one page. Apparently, it’s not good enough for visual elements that need multiple pages. So you need to write your own paginator class, VisualDocumentPaginator.

The VisualDocumentPaginator constructor modifies the page size of the original paginator based on the required page size and margin. The new GetPage method calls the original GetPage method to get a page, then tries to measure the visual element size and split it to multiple pages per page size. Here is our VisualDocumentPaginator class:

Print and Print Preview with FlowDocument

Why We Use FlowDocument

A flow document is designed to "reflow content" depending on the window size, device resolution, and other environment variables. In addition, flow documents have a number of built-in features including search, viewing modes that optimize readability, and the ability to change the size and appearance of fonts. Flow documents are best utilized when ease of reading is the primary document consumption scenario.

Convert an XPS Document to a FlowDocument

There is an excellent article that talks about how to convert an XPS document to a flow document. I won’t repeat it here. As we talked before, an XPS document consists of multiple fixed pages. Every fixed page contains UI Element children. So in short words, this conversion extracts the UI Element children of a FixedPage and adds them to a FlowDocument UI block.

One thing I need to bring out is, the font resource in an XPS document is obfuscated. These fonts normally are saved as ODTTF files. Before converting to a flowdocument, we need to de-obfuscate first. In .NET Framework 3.5, we still use the same name for the de-obfuscated font file to generate a Glyph. But in .NET Framework 4.0, you’ll get a null reference exception when you try to generate a Glyph with an ODTTF file, even this file is de-obfuscated. That’s null reference exception from MS.Internal.FontCache.FontFaceLayoutInfo.IntMap.TryGetValue( Int32 key, UInt16& value)”. So you have to rename the de-obfuscated font file to a TTF file, and save font file to different folder. You can name the folder as font name.

Shown below is the code to de-obfuscate an ODTTF font. You can get the GUID from the ODTT font file name:

Print FlowDocument

Call the PrintDialog.PrintDocument method to call the print dialog that will allow you to select a printer and send a document to the printer to print it. The PrintDocument method of PrintDialog takes a DocumentPaginator object that you can get from the IDocumentPaginatorSource.DocumentPaginator property as listed in the following code:

Share

About the Author

Fred Song is a senior software developer who lives in Melbourne, Australia. In 1993, he started Programming using Visual C++, Visual Basic, and Oracle Developer Tools. From 2003, He started with .Net using C#.

He is often working with software projects in different business domains based on different Microsoft Technologies like SQL-Server, C#, VC++, ASP.NET, WCF,WPF and Silverlight, although he also did some development works on IBM AS400.