I finally figured out that there is an option that addresses my general requirement of embedding a vector format into a print job, but it doesn't work with GDI based printing.
The XPS file format created by Microsoft XPS Writer print driver can be printed from WPF, using the ReachFramework.dll included in .NET. By using WPF for printing instead of GDI, it's possible to embed an XPS document page into a larger print document.
The downside is, WPF printing works quite a bit different, so all the support code that directly uses stuff in the Sytem.Drawing namespace has to be re-written.
Here's the basic outline of how to embed the XPS document:
Open the document:
XpsDocument xpsDoc = new XpsDocument(filename, System.IO.FileAccess.Read);
var document = xpsDoc.GetFixedDocumentSequence().DocumentPaginator;
// pass the document into a custom DocumentPaginator that will decide
// what order to print the pages:
var mypaginator = new myDocumentPaginator(new DocumentPaginator[] { document });
// pass the paginator into PrintDialog.PrintDocument() to do the actual printing:
new PrintDialog().PrintDocument(mypaginator, "printjobname");
Then create a descendant of DocumentPaginator, that will do your actual printing. Override the abstract methods, in particular the GetPage should return DocumentPages in the correct order. Here's my proof of concept code that demonstrates how to append custom content to a list of Xps documents:
public override DocumentPage GetPage(int pageNumber)
{
for (int i = 0; i < children.Count; i++)
{
if (pageNumber >= pageCounts[i])
pageNumber -= pageCounts[i];
else
return FixFixedPage(children[i].GetPage(pageNumber));
}
if (pageNumber < PageCount)
{
DrawingVisual dv = new DrawingVisual();
var dc = dv.Drawing.Append();
dc = dv.RenderOpen();
DoRender(pageNumber, dc); // some method to render stuff to the DrawingContext
dc.Close();
return new DocumentPage(dv);
}
return null;
}
When trying to print to another XPS document, it gives an exception "FixedPage cannot contain another FixedPage", and a post by H.Alipourian demonstrates how to fix it: http://social.msdn.microsoft.com/Forums/da/wpf/thread/841e804b-9130-4476-8709-0d2854c11582
private DocumentPage FixFixedPage(DocumentPage page)
{
if (!(page.Visual is FixedPage))
return page;
// Create a new ContainerVisual as a new parent for page children
var cv = new ContainerVisual();
foreach (var child in ((FixedPage)page.Visual).Children)
{
// Make a shallow clone of the child using reflection
var childClone = (UIElement)child.GetType().GetMethod(
"MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic
).Invoke(child, null);
// Setting the parent of the cloned child to the created ContainerVisual
// by using Reflection.
// WARNING: If we use Add and Remove methods on the FixedPage.Children,
// for some reason it will throw an exception concerning event handlers
// after the printing job has finished.
var parentField = childClone.GetType().GetField(
"_parent", BindingFlags.Instance | BindingFlags.NonPublic);
if (parentField != null)
{
parentField.SetValue(childClone, null);
cv.Children.Add(childClone);
}
}
return new DocumentPage(cv, page.Size, page.BleedBox, page.ContentBox);
}
Sorry that it's not exactly compiling code, I just wanted to provide an overview of the pieces of code necessary to make it work to give other people a head start on all the disparate pieces that need to come together to make it work. Trying to create a more generalized solution would be much more complex than the scope of this answer.