Contents
In the first article in this series, I referred to the problems I found in finding good sources to generate XPS with - but I only covered the XPS structure in that article. So finally, here's a small taste of one way to generate XPS documents, focusing on using the API in the .NET Framework.
It's been a little long coming this article, especially considering how short it is. Inspite of the fact that it's a "Hello World" application, it's not completely useless. I hope it will provide some good pointers.
At this point, I definitely should give credit to Bob Watson's article: "XPS Documents: A First Look at APIs For Creating XML Paper Specification Documents"; much of the code in this project is plagiarised from his article.
This is yet another simple console application. Once executed, it creates the document "HelloWorld.xps". It also makes a few assumptions, for instance, that it can find the arial.ttf font file at the location C:\Windows\Fonts. Please make the necessary changes to get it working.
First of all, what do you need to get at the XPS APIs? For that, add a reference to ReachFramework.dll. However, it effectively needs to reference WindowsBase.dll for the zip file work that it does. In other words, you'll need to reference to both DLLs. Within your code, you'll also need to add a using
statement for System.Windows.Xps.Packaging
.
So first off, there's cracking open a new XPS document. Here's an example:
#region The setup - Creating all the objects needed
XpsDocument xd = new XpsDocument("HelloWorld.xps", FileAccess.ReadWrite);
IXpsFixedDocumentSequenceWriter xdSW = xd.AddFixedDocumentSequence();
IXpsFixedDocumentWriter xdW = xdSW.AddFixedDocument();
IXpsFixedPageWriter xpW = xdW.AddFixedPage();
string fontURI = AddFontResourceToFixedPage(xpW, "C:\\Windows\\Fonts\\Arial.ttf");
StringBuilder pageContents = new StringBuilder();
#endregion
You can plainly see the hierarchy of an XPS document in the above commands. Document -> Fixed Document Sequence -> Fixed Document -> Fixed Page. Keeping that hierarchy in mind makes it easier when manipulating XPS documents.
Next is creating the actual XPS itself. For this part, I've just built the XPS as a string. However, you can also take XAML and flatten it out; see Bob's article reference above for an example of this. Yet another way, of course, is to generate the XPS (XML), and there's plenty of ways of doing this.
#region The actual XPS markup
pageContents.AppendLine("<FixedPage Width=\"793.76\" Height=\"1122.56\"
xmlns=\"http://schemas.microsoft.com/xps/2005/06\" xml:lang=\"und\">");
pageContents.AppendLine("<Glyphs Fill=\"#ff000000\" FontRenderingEmSize=\"16\"
StyleSimulations=\"None\" OriginX=\"75.68\" OriginY=\"90.56\"");
pageContents.AppendFormat(" FontUri=\"{0}\" ", fontURI);
pageContents.AppendLine(" UnicodeString=\"Hello XPS World!\"/>");
pageContents.AppendLine("</FixedPage>");
#endregion
It's about the briefest that I could get an XPS document. I've not bothered with the Indices
attribute to keep things even simpler. You'll note that the fontURI
comes from the return of the function call in the previous section.
The last part is finishing with all of the objects; by getting them to commit, we ensure that the various elements in the zip file get all completed correctly.
#region The shutdown - Commiting all of the objects
XmlWriter xmlWriter = xpW.XmlWriter;
xmlWriter.WriteRaw(pageContents.ToString());
xpW.Commit();
xmlWriter.Close();
xdW.Commit();
xdSW.Commit();
xd.Close();
#endregion
And that's it. As suggested in the comments, do some playing around with some of the actual XPS markup and see what it generates. You may find that you need to delete the XPS between each run though (especially if something screws up). Try adding more resources, pages, and documents; also, try obfuscating the font files and see what you get. But for that, you'll need to actually look at the code to see what to do.
History
- 2008-09-03: First version completed.