Displaying XPS Documents in Silverlight
Monday, April 20, 2009 8:10 PM
I’ve recently been involved on a project that has a requirement to create and view XPS documents in Silverlight. The application needs to display the XPS file in a full screen window together with zoom and navigation features.
After a little searching on how to do this, I was able to get a head start with this great post from David Anson, which includes sample code for viewing XPS files in Silverlight 2 beta 2. As his updated post attests to however, there were a few issues with Silverlight 2 due to a breaking change with the ways that font resources could be referenced from within a Silverlight assembly. Fortunately, this post from Li Chen pointed me in the right direction, separating out the ODTTF fonts into separate XAP files which can be referenced at runtime.
The sample code provided by Li Chen works well with the sample XPS files provided, but I couldn’t get it working with an XPS file generated using Microsoft Word. After a little debugging this weekend, I found a few subtleties with how the code dealt with XPS files geneated from Microsoft Word:
Firstly, the root document of the XPS file is called FixedDoc.fdoc instead of FixedDocument.fdoc (which is the name when using the XPS Printer Driver). This was fairly easy to correct using a simple check:
// Added to support "Save as XPS" from Microsoft Word
if (resourceInfo == null)
{
resourceInfo = Application.GetResourceStream(_streamResourceInfo, ConvertPartName("/Documents/1/FixedDoc.fdoc"));
}
Secondly, the .fdoc file refers to pages using relative links. Instead of the absolute link (e.g. /Documents/1/Pages/1.page), a relative link (e.g. /Page/1.page) was causing the sample to be unable to find the pages. A small piece of code to append the full path quickly fixes this also.
// Update the page names for "Save as XPS" from Microsoft Word
List<string> _newPageNames = new List<string>();
foreach (String pageName in _pageNames)
{
if (pageName.StartsWith("Page"))
{
_newPageNames.Add("/Documents/1/" + pageName);
}
}
Finally, it looks like Word includes a page attribute called BidiLevel, which isn’t recognized by the Canvas element. Adding an additional exclusion line into the sample code quickly fixed it.
_elementAttributesToRemove.Add("Glyphs", new List<string> { "BidiLevel" });
The result seems to work quite well, with an XPS file saved from Microsoft Word viewable within Silverlight.
If you are interested in trying this yourself, I’ve posted a version of the sample code here which contains the above modifications. To use the modified sample with your own XPS file, do the following:
1. Go into Microsoft Word and “Save as / XPS” to create a new XPS file.
2. Download and compile the sample (note: only works in Visual Studio 2008).
3. Copy the XPS into your Visual Studio project, in the SimpleSilverlightXpsViewer_Web project.
4. In Windows Explorer, rename the original XPS file to a ZIP file. Ignore the warning about changing the file extension.
5. Open the zip file and go into the /Resources directory. Look for font files ending in ODTTF - these are embedded font files for the XPS file and must be referenced separately in the Silverlight project. Copy all of these ODTTF files to the SampleWordGenerated project in the solution. Also note that the ODTTF files are dynamically named when the XPS file is generated. This means that although you may have already imported the correct ODTTF files for previous XPS file that use the same fonts, you’ll still need to re-import the fonts again to handle a new XPS file.
6. Edit default.html in SimpleSilverlightXpsViewer_Web and on line 70 change the xpsDocument=SampleWordGenerated.xps to the correct name of your XPS file.
7. F5 to run and you should see your XPS document displayed within a Silverlight control!
There is still a bit of work to do with the sample code, which I think would be worth taking into a CodePlex project. For example, the code should initially read the XPS root file (FixedDocSeq.fdseq) instead of looking or FixedDoc.fdoc or FixedDocument.fdoc directly. It would also be great to figure out a better way of extracting the fonts more dynamically at runtime. Other than that though I found this to be a good solution to display XPS files in Silverlight applications, especially useful as Silverlight doesn’t support the FlowDocument element (which is commonly used in WPF applications for creating documents and report generation).