XPS Documents

A First Look at APIs For Creating XML Paper Specification Documents

Bob Watson

This article is based on a prerelease version of WinFX. All information contained herein is subject to change.

This article discusses:

  • Physical and logical organization of XPS Documents
  • Creating and saving XPS Documents
  • Navigating XPS Documents
  • Using XPS Document technology in your application
This article uses the following technologies:

Code download available at:XPS.exe(121 KB)


What is XPS?
Inside the XPS Document
XPS Document Programming
Building an XPS Document
Storing Text and Resources
Signing an XPS Document
Navigating Through an XPS Document
Closing Thoughts

Electronic documents have changed the way people publish and exchange information. But while convenient, the process isn't flawless. With current software, documents often appear one way when viewed on the screen and another when printed. To make matters worse, many documents depend on locally installed software, fonts, and other resources. When a document is viewed on different computers, aspects of the document, such as page layout, image color, and font style, can change, altering the document's appearance. Consequently, a document's appearance depends greatly on where and how the document is viewed.

The appearance of the printed version of a document can also vary, depending on the printer used. Unfortunately, there is currently no guarantee that users with different printers attached to different machines will see the exact same document printed on paper as when the document is viewed on the monitor screen. In fact, it is almost guaranteed that each version of the document will have a different appearance.

Microsoft is addressing these issues with an improved document technology that will be included in Windows Vista™. An integral part of this new technology is the XML Paper Specification (XPS). This specification is designed to provide users with a consistent document appearance regardless of where and how the document is viewed.

This article provides an overview of the XPS Document format and how you can create and access XPS Documents programmatically. Using sample code, I'll show you a couple of different ways to create an XPS Document from an application based on Windows® Presentation Foundation (WPF). I'll also discuss how you can navigate through the components of an XPS Document. This article should give you the necessary information to get started using XPS Documents in your own applications in anticipation of this technology becoming available in Windows Vista.

What is XPS?

XPS Documents maintain a consistent appearance for documents—despite environmental variables—through the use of a fixed page layout and new technologies such as Windows Presentation Foundation (WPF), the Windows Color System, the Open Packaging Conventions, the XPS print path, and XPS Viewer. Essentially, XPS-based technologies allow an author to be much more certain that the next person to view or print their document will see it exactly as the author intended.

XPS technologies offer benefits to users throughout the document workflow, starting with authoring and viewing the document and continuing through to storing and archiving. For starters, XPS Documents are fixed-format documents described by an XML-based language. This means the document layout is fixed, just as it would be if it were printed on a piece of paper. As a result, XPS Viewer and the XPS print path can present the document in the same way to the user whether it is viewed in a window or on a piece of paper. For more on this, take a look at the sidebar "Three Paths to High Fidelity."

XPS Documents also support security features, such as digital signatures, to provide greater document security. When applied to a document, these digital signatures can help ensure the identity of the author and the validity of the document content.

Custom resources and other application-specific metadata can also be included in an XPS Document, allowing applications to create and use XPS Document packages. These packages offer numerous benefits over other file formats, including the ability to store and archive files with all the content and design details in tact. XPS Document packages describe their contents using a plain-text, XML-based data format (rather than a proprietary binary format). And they contain all the information related to the document and its particular application. The XML Paper Specification describes this format in an open and published specification provided to users and developers by Microsoft under a royalty-free license.

The XPS Document package is a compressed ZIP archive that allows the resulting file to regain some of the space efficiency that is lost by using an XML-based language. The XPS Document package complies with the Open Packaging Conventions. And the ZIP archive format described in the Open Packaging Conventions is a published, open specification that is included under the XPS license. Documents created by the next version of Microsoft® Office System (codenamed "Office 12") will also comply with the Open Packaging Conventions. Using open specifications for both the content and the storage format helps ensure that the document's contents will be accessible long into the future. Further details and downloads regarding XPS and the Open Packaging Conventions can be found on the Microsoft Web site at XML Paper Specification.

Inside the XPS Document

The XPS Document API closely follows the logical organization of an XPS Document, so I'll present a brief overview of how an XPS Document is organized. Understanding the internal configuration of the document will make it easier to understand how the XPS Document API works.

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.

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 resources that are used on more than one page, such as a watermark or letterhead logo.

The logical hierarchy of an XPS Document is illustrated in Figure 1. This example shows the contents of a package that contains two separate documents, each containing two pages. The package in this example could be a presentation where FixedDocument_1 contains the slides while FixedDocument_2 contains the background information.

Figure 1 Logical Hierarchy of an XPS Document

Figure 1** Logical Hierarchy of an XPS Document **

The XPS Document API manages the physical storage of the document parts and presents them to the application in the logical hierarchy, While it is not necessary to manipulate the package contents directly, it can be useful to know what is stored inside the package, if only to confirm your program is creating the document parts correctly.

The easiest way to view the physical contents of an XPS Document package is with Windows Explorer. The XPS Document API creates XPS Document packages in the ZIP archive file format. The package contents may be stored in a compressed or uncompressed format. In either case, they retain the ZIP organization. Simply changing the file extension of an XPS Document package to ZIP allows the XPS Document to be opened in the file explorer as if it were a standard ZIP archive. Figure 2 shows what a sample XPS Document looks like when viewed in Windows Explorer.

Figure 2 SampleDocXPS Physical Hierarchy in Windows Explorer

Figure 2** SampleDocXPS Physical Hierarchy in Windows Explorer **

While the different physical parts of the document are displayed as files and folders in the Windows Explorer window, the logical hierarchy of the document parts is not apparent. The logical organization of the document part hierarchy is illustrated in Figure 3.

Figure 3 SampleDocXPS Logical Hierarchy

Figure 3** SampleDocXPS Logical Hierarchy **

The logical hierarchy is described in the document parts of the package. In every XPS Document, the first part in the hierarchy is described as a target in the only part that has a predefined name: /_rels/.rels. Aside from the /_rels/.rels part, the names of the other parts and folders in an XPS Document are listed in the contents of the document parts. In the case of this sample XPS Document, the first document part listed in the /_rels/.rels file is the FixedDocumentSequence.fdseq part, which is located in the document root.

The FixedDocumentSequence.fdseq part describes the next level in the logical hierarchy of the XPS Document, the FixedDocument parts. The contents of the FixedDocumentSequence.fdseq part looks like the following:

<FixedDocumentSequence xmlns="https://schemas.microsoft.com/xps/2005/06"> <DocumentReference Source="Documents/FixedDocument_1.fdoc" /> </FixedDocumentSequence>

Essentially, it describes where the FixedDocuments of this package are located. In this particular document, there is only one FixedDocument so there is only one DocumentReference element. XPS Document packages containing more documents have more DocumentReference elements listing the sequence in which they should appear.

The FixedPage parts for the FixedDocument of this XPS Document package are listed in the FixedDocument_1.fdoc part in the Documents folder. The Fixed Document part looks like this:

<FixedDocument xmlns="https://schemas.microsoft.com/xps/2005/06"> <PageContent Source="../Pages/FixedPage_1.fpage" /> </FixedDocument>

Again, this is a very simple case with only one page contained in the XPS Document, so the listing is very short. The FixedPage_1.fpage part is in the Pages folder (along with the _rels folder) and contains the layout of that page in the document. The part describing the resources used by this page is found in the Pages/_rels folder. Furthermore, logical document parts can be interleaved and stored in more than one physical part. Interleaving is described further in the XML Paper Specification.

Also visible in the Windows Explorer display of the sample file are the folders containing the resources stored in the document. In this sample document, the page contains a photo and uses a font, both of which were stored as resources for this document. This image can be found in the \Resources\Images folder of the XPS Document package, while the font is located in the \Resources\Fonts folder.

XPS Document Programming

In an application that uses Windows Presentation Foundation, writing the application's data as an XPS Document is straightforward. Windows Presentation Foundation applications describe their graphical content using Extensible Application Markup Language (XAML) code. XPS Documents use a subset of this code as their markup. A WPF application can create an XPS document by calling a method that flattens the application XAML elements of the page into to the XPS page markup.

A managed code application can also create the individual parts of an XPS Document by using the XPS Document API in the System.Windows.Xps.Serialization namespace. This may be a better option for retaining precise control over how the XPS Document is constructed or for adding additional metadata to the document. An application can also create an XPS Document directly, even if the application was not built using Windows Presentation Foundation. This can be done by creating the markup for the individual XPS Document parts, but the classes and methods described here that actually create the XPS Document only run in a managed code environment.

The XamlStreamToXps method from the sample code shown in Figure 4 can enable your WPF application to create an XPS Document. XamlStreamToXps accepts as its arguments the file name of the XPS Document to be created and a Stream containing XAML, which describes the document. XamlStreamToXps will read the input stream and create an XPS Document.

Figure 4 XamlStreamToXps

void XamlStreamToXps( Stream srcXamlStream, ParserContext context, string destXpsFile) { XpsDocument document = new XpsDocument(destXpsFile, FileAccess.ReadWrite); XpsPackagingPolicy packagePolicy = new XpsPackagingPolicy(document); XpsSerializationManager serializationMgr = new XpsSerializationManager(packagePolicy, false); object parsedDocObject = (null == context) ? XamlReader.Load (srcXamlStream) : XamlReader.Load (srcXamlStream, context); serializationMgr.SaveAsXaml(parsedDocObject); document.Close(); }

The first thing the XamlStreamToXps method does is create an empty XpsDocument object with the desired file name and file access. Because the method is creating a new document for the purpose of writing new content into the resulting XPS Document package, it needs write access to the file. Read access is also required to perform this action.

The XpsPackagingPolicy object is used to describe how to package the data in the XPS Document. The packaging policy describes to the serialization manager various aspects of the document package and whether to share resources when it writes the XPS Document. This information is used by the serialization manager that is created in the following line. In this example, the default packaging policy of no resource sharing will be used.

The XpsSerializationManager object is the object that actually does the conversion and writes the XPS Document once the XAML file has been parsed. This object can be configured to write the data to the XPS Document package as it is processed or as a batch. When adding multiple pages to the document, they can be added as a batch by setting the second argument of the constructor to true. Using batch mode requires the commit method to be called before the parsed data is written to disk. The following code snippet illustrates adding an array of streams to a document:

object [] parsedDocObject = ...; // get the objects to be saved XpsSerializationManager serializationMgr = new XpsSerializationManager(packagePolicy, true); for (int i = 0; i < numObjects; i++ ) { serializationMgr.SaveAsXaml(parsedDocObject[i]); } serializationMgr.Commit();

The XpsSerializationManager object has other configuration parameters that define the font subsetting policy. In this case, however, the default policy values and non-batch mode will work fine.

The next few lines in the method create the parsed object containing the XAML code from the input stream. The context argument is used only if the stream is a FileStream, as would be the case if the XAML for these pages came from an external file. If the stream is an internal stream from the application, the context argument is null. This option allows the method to work with both file streams and internal streams from an application.

Here is a method used to read the XAML from a file and have it parsed by the XamlStreamToXps method:

void XamlFileToXps(string srcXamlFile, string destXpsFile) { using(Stream fileStream = File.OpenRead(srcXamlFile)) { ParserContext context = new ParserContext(); context.BaseUri = new Uri(Directory.GetCurrentDirectory() + "//"); XamlStreamToXps(fileStream, context, destXpsFile); } }

With the XAML code parsed into the parsedDocObject object, the XpsSerializationManager now has all it needs to create the XPS Document. Rest assured that even though the SaveAsXaml method was called, the end result will be an XPS Document package because the method is part of the XpsSerializationManager object (apparently this method was named before the XPS documentation had been published).

Any resources referenced by the XAML document must be available to this method so they can be opened and read into the resulting XPS Document. This means they must be present and not locked by another application or user. If there is a problem, the serialization method will throw an exception. The method finishes by calling the Close method on the document before exiting.

Figure 5 Sample Document

Figure 5** Sample Document **

To follow this process as it acts on a document, let's start with the document shown in Figure 5. The XAML code that describes this document in my application is shown in Figure 6. Note this XAML code has been simplified greatly and uses default values for many of the attributes. The XAML code used in a real application would be much more explicit.Three Paths to High Fidelity

The XPS Document format is more than just a new way to save documents. It allows your app to take advantage of a variety of XPS-based features found in Windows Vista, ensuring documents can be viewed and printed with perfect fidelity.

XPS Print Path

The XPS print path in Windows Vista—that is, the software path a document takes to get from the application to the printer—has been optimized for XPS Documents. Prior to Windows Vista, a document being sent to a printer had to undergo several translations and transformations. For documents containing only text, the worst-case scenario is that some of the fonts used in the document may not be supported by the printer. These days, however, very few documents consist of only text. Because most documents today contain a mix of images, drawings, text in multiple fonts, and other graphical elements, the graphical processing ability of the print and display paths is very important.

Print drivers used in versions of Windows prior to Windows Vista required the documents be spooled in Enhanced Metafile format (EMF). Since the EMF format is based on 8 bit-per-pixel Graphics Device Interface (GDI) functions, the graphical fidelity supported by this conversion is limited. Again, for text documents and those with basic graphics, this is sufficient. But those types of documents are becoming increasingly rare.

One current workaround is to have the application do all the graphics processing and send a raw file (a file in the printer's native Page Description Language, or PDL) to the spooler. This option obviously requires a lot more development in the application since it must handle the on-screen image rendering in the application, as well as perform all the print rendering functions.

This is where the XPS print path makes dramatic improvements. It uses the XPS Spool File format defined in the XML Paper Specification as the print spool file format and, if supported by the printer, the printer's PDL. Keeping the format of the document unchanged throughout the entire printing process retains the original quality and fidelity of the document all the way to the printer.

Further, the XPS print path supports a modular, filter-based architecture that allows application developers to move their print rendering functions out of the application and into the print path. Here, they can be more efficiently leveraged by other applications or as a standalone product.

Graphics processing in the XPS print path is not limited to using only GDI functions. As a result, the XPS print path can support more color spaces and greater pixel bit-depths than is possible with GDI. The XPS print path is discussed in greater detail on the Windows Hardware Developer Centralsite at XML Paper Specification.

Let's look at an example of how an application can benefit from the XPS print path. Many consumer and professional graphics programs that produce documents for printed output use the cyan-magenta-yellow-black (CMYK) color space because that color space most closely represents the four colors commonly used to make color prints. An XPS Document created by an application working in the CYMK color space can print to a printer with an XPSDrv print driver that also supports the CYMK color space. This means the application does not need to convert the image to another color space in order to print the document. If the printer uses an XPS-based PDL, the same language used to spool the document in the print server can also be used to render the document in the printer, thus providing the most accurate rendering of the document on the printed page.

XPS Document Writer

Another feature of the XPS technology planned to be provided by Windows Vista is the Microsoft XPS Document Writer. To an application, the XPS Document Writer looks like a printer and allows the application to write a document as an XPS Document in the same way it would print the document to the printer. When a user prints their document to XPS Document Writer, rather than sending the file to a printer, the software creates an XPS Document.

This means an application that is not written for the Windows Presentation Foundation can still create an XPS Document by using the XPS Document Writer, enabling it to take advantage of the features provided by the XPS print path and XPS Viewer. For some documents and applications, this functionality may be all that's needed. If so, it doesn't get much easier to create an XPS Document.

XPS Viewer

XPS Viewer is a lightweight, configurable, browser-hosted application that displays XPS Documents. XPS Viewer is an application built around the Document Viewer control included in the WinFX Runtime Components. Developers can also use this control to build and configure their own viewer applications.

The XPS Viewer currently runs on Windows 2000, Windows XP, and Windows Server™ 2003 with the WinFX® Runtime Components 3.0, which allows XPS Documents to be viewed on different client machines running many different versions of Windows. XPS Viewer also allows users to view documents natively on their own computers without having to install the authoring software or some other application.

Figure 6 XAML Code of Sample Document

<FixedDocument> <PageContent> <FixedPage Background="#EEEEEE" Width="800" Height="500" xmlns="https://schemas.microsoft.com/xps/2005/06" xml:lang="en-US"> <Canvas FixedPage.Top="10" FixedPage.Left="20"> <TextBlock Height="20" Canvas.Top="20" FontSize="18"> This is a photo from my recent vacation. </TextBlock> <TextBlock Height="20" Canvas.Top="40" FontSize="18"> I had a great time visiting the different places </TextBlock> <TextBlock Height="20" Canvas.Top="60" FontSize="18"> and seeing all the sights, but my favorite </TextBlock> <TextBlock Height="20" Canvas.Top="80" FontSize="18"> place to visit was Cape Canaveral. </TextBlock> <Rectangle Canvas.Top="120" Canvas.Left="60" Width="150" Height="150"> <Rectangle.Fill> <SolidColorBrush Opacity="1" Color="Orange"/> </Rectangle.Fill> </Rectangle> <Rectangle Canvas.Top="140" Canvas.Left="40" Width="150" Height="150"> <Rectangle.Fill> <ImageBrush Opacity="1" ImageSource="images/Rockets.jpg" /> </Rectangle.Fill> </Rectangle> </Canvas> </FixedPage> </PageContent> </FixedDocument>

Passing the parsed XAML code to the XpsSerializationManager creates the XPS Document. The serialization routines read in the XAML code from the Windows Presentation Foundation application and create the corresponding parts in the XPS Document. As an example, an excerpt of the FixedPage part that corresponds to the sample page is shown in Figure 7.The XPS Document markup code is similar, but not identical, to the XAML used by Windows Presentation Foundation applications.

Figure 7 FixedPage Representation of the Sample Page

<FixedPage Width="800" Height="500"> <FixedPage.Resources> <ResourceDictionary> <ImageBrush x:Key="b0" ViewportUnits="Absolute" TileMode="None" ViewboxUnits="Absolute" Viewbox="0,0,300,300" Viewport="0,0,150,150" ImageSource="/Resources/Images/Image_1.jpeg" /> </ResourceDictionary> </FixedPage.Resources> <Path Fill="#FFEEEEEE" Data="F0 M 0, 0 L 800, 0 800, 500 0, 500Z" /> <Canvas RenderTransform=" 1, 0, 0, 1, 20, 10"> <Glyphs OriginX="0" OriginY="36.59" FontRenderingEmSize="18" FontUri="/Resources/Fonts/d79d2c70-3b70-4e00-a376- bee81a5fc96e.ODTTF" UnicodeString="This is a photo from my recent vacation." Fill="#FF000000" /> <Glyphs OriginX="0" OriginY="56.59" FontRenderingEmSize="18" FontUri="/Resources/Fonts/d79d2c70-3b70-4e00-a376- bee81a5fc96e.ODTTF" UnicodeString="I had a great time visiting the different places" Fill="#FF000000" /> <Glyphs OriginX="0" OriginY="76.59" FontRenderingEmSize="18" FontUri="/Resources/Fonts/d79d2c70-3b70-4e00-a376- bee81a5fc96e.ODTTF" UnicodeString="and seeing all the sights, but my favorite" Fill="#FF000000" /> <Glyphs OriginX="0" OriginY="96.59" FontRenderingEmSize="18" FontUri="/Resources/Fonts/d79d2c70-3b70-4e00-a376- bee81a5fc96e.ODTTF" UnicodeString="place to visit was Cape Canaveral." Fill="#FF000000" /> <Path Fill="#FFFFA500" RenderTransform="1, 0, 0, 1, 60, 120" Data="F0 M 0, 0 L 150, 0 150, 150 0, 150Z" /> <Path Fill="{StaticResource b0}" RenderTransform="1, 0, 0, 1, 40, 140" Data="F0 M 0, 0 L 150, 0 150, 150 0, 150Z" /> </Canvas> </FixedPage>

Building an XPS Document

Your application can create the elements of the XPS Document directly and thereby maintain more control over the final output. With this approach, your application can also embed metadata resources in the document.

An application written to use Windows Presentation Foundation will have access to the XAML used to format and lay out the document pages. However, an existing application not written for WPF can still create XPS Documents by adding in the routines to create the markup that represents the text and graphics layout. Whether it is better to add these functions to your application or rewrite the application for Windows Presentation Foundation depends on your specific requirements.

Note that the examples here use only a subset of the commands provided by XPS. For a complete listing of XPS commands, see the specifications found at XML Paper Specification.

To create a document from scratch, the program needs to create the document parts and their interfaces using the following methods from the System.Windows.Xps.Packaging namespace. It can then start filling the document parts with the contents derived from the application:

// create a new document object XpsDocument document = new XpsDocument(destFileName,FileAccess.ReadWrite); // create a new FixedDocumentSequence object in the document IXpsFixedDocumentSequenceWriter docSeqWriter = document.AddFixedDocumentSequence(); // create a new FixedDocument object in in the document sequence IXpsFixedDocumentWriter docWriter = docSeqWriter.AddFixedDocument(); // add a new FixedPage to the FixedDocument IXpsFixedPageWriter pageWriter = docWriter.AddFixedPage();

Once the document package is opened for writing and the necessary interfaces are instantiated, the application can begin writing the application content to the XPS Document. Notice how the interfaces "nest," with the IXpsFixedDocumentSequenceWriter interface created from the document object, the IXpsFixedDocumentWriter interface created from the IXpsFixedDocumentSequenceWriter, and so on. This structure reflects the logical hierarchy of the XPS Document and is important to keep in mind when planning how to adapt your app's document content to the XPS Document.

In a more practical application, a program would most likely call these methods in a manner similar to the pseudo code shown in Figure 8. When the document object is closed, the resulting XPS Document is stored in a self-contained package that includes the resources necessary to render and display the document exactly as it was stored. So, in addition to the text and layout, the non-text resources are also written to the document.

Figure 8 Pseudocode for Creating an XPS Document

// create a new document object XpsDocument document = new XpsDocument(destFileName,FileAccess.ReadWrite); // create a new FixedDocumentSequence object in the document IXpsFixedDocumentSequenceWriter docSeqWriter = document.AddFixedDocumentSequence(); // then process the documents and pages for this package for each document in the application to save in the current package: { // create a new FixedDocument in the document sequence IXpsFixedDocumentWriter docWriter = docSeqWriter.AddFixedDocument(); for each page in the document: { // create a new FixedPage in the FixedDocument IXpsFixedPageWriter pageWriter = docWriter.AddFixedPage(); ... // add page content here ... // add resources used in this page pageWriter.Commit(); // save the page content to the package } docWriter.Commit(); } docSeqWriter.Commit(); document.Close();

Storing Text and Resources

The text for each page in an XPS Document is stored in FixedPage objects that contain the markup describing the layout of each page. Your application needs to generate this markup for your application's documents. The precise method of doing this depends on the type of layout and formatting your application needs to produce.

Take my sample document, for example. The IXpsFixedPageWriter interface writes the markup for this page to the document as a raw, unformatted string. For this method, the application needs this text in a string variable that can be passed to the XmlWriter object. The code shown in this example uses a string named pageContents, which contains the FixedPage description. The following code will add markup to create a page in the XPS Document:

string pageContents = ...; // XPS formatted description of page content XmlWriter xmlWriter = pageWriter.XmlWriter; xmlWriter.WriteRaw(pageContents); pageWriter.Commit(); xmlWriter.Close();

In this sample code, the document is described by an XML file that contains the page markup, as well as the image and font resources. The raw string for this page markup is provided as CDATA in an element of the XML file. It describes the document and is assigned to the pageContents variable.

The first line obtains the XML writer object for this page. The markup string is then written to the FixedPage object, the content is committed to the document, and the writer is closed.

The last path elements in the document, shown in the following code sample, describe a colored rectangle and one that is created and filled with an image. The image referenced in the second path element is located in the Images folder and needs to be saved as a resource in the document to completely render this page. Here is what the code looks like:

<Path Fill="#FFFFA500" RenderTransform="1, 0, 0, 1, 60, 120" Data="F0 M 0, 0 L 150, 0 150, 150 0, 150Z" /> <Path Fill="{StaticResource b0}" RenderTransform="1, 0, 0, 1, 40, 140" Data="F0 M 0, 0 L 150, 0 150, 150 0, 150Z" />

All the fonts used in the document must be embedded in the document in a similar fashion. Additionally, custom resources can be created by an app and written to the XPS Document package.

In this example, the file names of the images used on a page are stored with the page content in the sample application document. The application adds these images to the XPS Document as it creates each page using the method shown in Figure 9. This method receives the page writer interface from the document, which is passed as an argument along with the file name of the image. Inside the method, a new image resource is added to the FixedPage part, and an image file stream is obtained from the object. The file stream from the image is then opened and copied using the local stream copy method shown in Figure 10. The data is then committed to the document package, the local stream objects are closed, and the method returns.

Figure 10 CopyStream Method

Int32 CopyStream(Stream srcStream, Stream dstStream) { const int size = 64*1024; // copy using 64K buffers byte[] localBuffer = new byte[size]; int bytesRead; Int32 bytesMoved = 0; // reset stream pointers srcStream.Seek(0, SeekOrigin.Begin); dstStream.Seek(0, SeekOrigin.Begin); // stream position is advanced automatically by stream object while((bytesRead = srcStream.Read(localBuffer, 0, size)) > 0) { dstStream.Write(localBuffer, 0, bytesRead); bytesMoved += bytesRead; } return bytesMoved; }

Figure 9 Storing an Image Resource

void AddJpegImageResourceToFixedPage ( IXpsFixedPageWriter pageWriter, String imgFileName) { XpsImage image = pageWriter.AddImage("image/jpeg"); using(Stream dstImageStream = image.GetStream()) using(Stream srcImageStream = File.OpenRead(imgFileName)) { CopyStream(srcImageStream, dstImageStream); // commit image resource to the package file image.Commit(); } image.Close(); }

Fonts used in this document are saved into the XPS Document package in a similar fashion. The method in Figure 11 stores a font to a FixedPage part of an XPS Document.

Figure 11 Storing a Font Resource

void AddFontResourceToFixedPage ( IXpsFixedPageWriter pageWriter, String fontFileName) { XpsFont font = pageWriter.AddFont(false); using(Stream dstFontStream = font.GetStream()) using(Stream srcFontStream = File.OpenRead(fontFileName)) { CopyStream(srcFontStream, dstFontStream); // commit font resource to the package file font.Commit(); } font.Close(); }

The AddFont method of the page writer interface allows you to obfuscate the font when it is stored in the document for security and intellectual property protection. This method is intended to prevent the casual user from extracting the font and installing it on their system. While it is not considered a form of strong encryption, it is an obstacle that prevents a user from using the ZIP archive functionality of the operating system to extract a font file and install it on their system. To obfuscate the font, set the argument in the AddFont method to true. In my example, this argument is set to false and the fonts will be stored without obfuscation.

When all document parts have been added to the document, the following code flushes all the buffers and closes the objects:

pageWriter.Commit(); docWriter.Commit(); docSeqWriter.Commit(); document.Close();

Signing an XPS Document

Digital signatures can be applied to XPS Documents, which is similar to physically signing a document. In some ways, though, this is even better than a physical, pen-and-ink signature. In addition to having a verifiable identity, digital signatures in XPS Documents also indicate the time and date the signature was applied and whether the document has changed since the signature was applied. An XPS Document can also have more than one digital signature.

Attaching a digital signature to an XPS Document is very straightforward, as shown in the following code sample:

void SignDocument(string srcXpsDocument, string certFilename) { XpsDocument document = new XpsDocument(srcXpsDocument, FileAccess.ReadWrite); X509Certificate certificate = X509Certificate.CreateFromCertFile(certFilename); document.SignDigitally(certificate, true, XpsDigSigPartAlteringRestrictions.None); document.Close(); }

The file name of the XPS Document and the file name of the digital certificate are passed as arguments, and the XPS Document is opened for read and write access. Then a certificate object is created from the certificate file. Calling the SignDigitally method on the XpsDocument object applies the digital signature to the document by adding the signature to the document's collection of digital signatures. The arguments of the SignDigitally method include the certificate object, a flag indicating that the certificate should be embedded in the document, and a flag indicating that altering the core components of the digital signature should not be restricted. Using the flags of the XpsDigSigPartAlteringRestrictions can be used to restrict changes to the core document components such as the Core Metadata, the Annotations, or the Signature Origin properties of the certificate embedded in the document.

At this point, the document has been signed, but it may be useful to see what other digital signatures have also been applied to this document. You can view the digital signatures in an XPS Document by viewing the document in the XPS Viewer. Or you can use the following sample method to list all the digital signatures that have been applied to the document:

void ListSignatures(string srcXpsDocument) { XpsDocument document = new XpsDocument(srcXpsDocument, FileAccess.Read); foreach (XpsDigitalSignature digitalSignature in document.Signatures) { Console.WriteLine(digitalSignature.SigningTime.ToString() + " " + digitalSignature.SignerCertificate.Issuer + " " + digitalSignature.SignerCertificate.Subject); } document.Close(); }

In this method, the file name of an XPS Document is passed in the argument list. The XPS Document is opened and the method iterates through the collection of digital signatures, writing the date and time the signature was applied as well as some information on the source of the certificate used for the signature. After iterating through the collection of signatures, the document object is closed and the function returns.

Navigating Through an XPS Document

An application already written for the Windows Presentation Foundation can save its document as an XPS Document using the serialization methods I've described. However, if the application has additional, custom resources, they can also call explicit XPS Document methods to add those resources to the document.

Up to this point, I have focused on how to create an XPS Document. But at some point, it will probably be necessary to access XPS Documents in order to read their contents. It should be noted here that an XPS Document is a fixed document and is not intended to be modified directly. For that reason the XPS Document API is divided into a set of writing interfaces and methods, and a separate set of reading interfaces and methods. To modify an XPS Document, an application must read in the current document, modify the contents, and write the modified version as a new document from the application.

As shown previously, Windows Explorer can be used to view and extract the contents of an XPS Document, and XPS Viewer can be used to view the document in its intended form. However, other programs can be used to navigate through XPS Documents by using the XPS Document API.

The method shown in Figure 12 shows how you can navigate through an XPS document. In this example, the code iterates through all the different document parts in order to list the image resource references, returning the count as an integer. The ListImagesInXpsDoc method accepts the file name of an XPS Document as the argument, lists the image references to the console, and returns a count of the image resource references found in that document. This example is admittedly of little practical use by itself, but it demonstrates the nested and hierarchical organization of the XPS Document API, as well as the XPS Document, itself. Using this skeleton as a starting point, more interesting methods can be created.

Figure 12 Navigating Through an XPS Document

int ListImagesInXpsDoc(string srcXpsDocument) { int numImages = 0; XpsDocument document = new XpsDocument(srcXpsDocument, FileAccess.Read); IXpsFixedDocumentSequenceReader docSequence = document.FixedDocumentSequenceReader; foreach (IXpsFixedDocumentReader docSeqDocument in docSequence.FixedDocuments) { foreach (IXpsFixedPageReader docPage in docSeqDocument.FixedPages) { foreach (XpsImage image in docPage.Images) { numImages++; Console.WriteLine("Page: " + docPage.PageNumber + ", Image Index: " + numImages + ", Image Part Name: " + image.Uri); } } } document.Close(); return numImages; }

So let's take a closer look at what occurs here. The document is opened with read access and an IXpsFixedDocumentSequenceReader interface is obtained from the document object to begin navigating through the parts of the XPS Document. The code then iterates through all the fixed pages of all fixed document parts found in this document, and counts the image resource references found in the document.

An IXpsFixedDocumentReader interface is obtained for each fixed document in the fixed document sequence. Using the fixed document reader, the code then iterates through the fixed document, obtaining an IXpsFixedPageReader interface for each page. Image and font resources of a fixed document are referenced by the page in which they are used. Using the page reader, the code iterates through the image resource references found in the page, incrementing the count and writing the Uniform Resource Identifier (URI) of each image reference that is found. The method ends by closing the document and returning the count of image resource references found in the document. The nesting of the reading methods used in this example is the same as the writing functions used previously.

While this sample illustrates how a program can navigate through the hierarchy of document parts in an XPS Document, remember the physical storage hierarchy of these parts does not necessarily reflect the logical hierarchy used to access them. Consequently, the approach used to find an individual part in a document will vary depending on how they need to be accessed.

Closing Thoughts

In this article, I have demonstrated some of the most basic ways to read and write XPS Documents. While this is enough knowledge to get started using the XPS Document format in an application, there is much more to this new technology. For starters, look at what else can be done within the FixedPage. As shown in this article, font and image resources can be added to the document. However, XPS Documents can also include custom resources.

Document printing intentions and preferences can also be added to the individual parts of an XPS Document, as well as to the document as a whole, by including XML PrintTicket objects. The information in an XML PrintTicket includes many printer settings, such as page orientation and page size, and can be applied to an entire fixed document sequence or individually to a fixed page in the document.

The list goes on, but these topics will have to wait for another article. In the meantime, you can get started creating and using XPS Documents in your application right away and make use of the new XPS print path and XPS Viewer while "future-proofing" your application's documents at the same time.

Bob Watson, who still has his build 317 CD of Windows NT 3.1, is a technical writer in the Windows Digital Documents Platform and Solutions Group at Microsoft.