Welcome to MSDN Blogs Sign in | Join | Help

Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

XPS is a fixed document format in which pages are pre-formated to a fixed page sixe. On the opposite of the spectrum, WPF provides flow document which can be paginated dynamically in XAML viewer. To bridge the two, WPF provides features to convert a flow document to fixed document in XPS format. Actually, you can convert any WPF Visual content to XPS.

While the default conversion is very easy to use, if you want more features, you need to write some code. This acticle shows how to convert WPF flow document to multiple page XPS with page size, margin, and header.

When a flow document is loaded in WPF, it returns an IDocumentPaginatorSource object. From it, you can get its DocumentPaginator object. When you're using XpsSerializationManager to convert a loaded flow document to XPS, it accepts a DocumentPaginator object. But the little know fact is that WPF allows you to wrap a new DocumentPaginator around a DocumentPaginator to control the conversion process.

Here is our DocumentPaginator wrapper class:

public class DocumentPaginatorWrapper : DocumentPaginator

{

    Size              m_PageSize;

    Size              m_Margin;

    DocumentPaginator m_Paginator;

    Typeface          m_Typeface;

   

    public DocumentPaginatorWrapper(DocumentPaginator paginator, Size pageSize, Size margin)

    {

        m_PageSize  = pageSize;

        m_Margin    = margin;   

        m_Paginator = paginator;

 

        m_Paginator.PageSize = new Size(m_PageSize.Width  - margin.Width  * 2,

                                        m_PageSize.Height - margin.Height * 2);

    }

  

    Rect Move(Rect rect)

    {

        if (rect.IsEmpty)

        {

            return rect;

        }

        else

        {

            return new Rect(rect.Left + m_Margin.Width, rect.Top + m_Margin.Height,

                            rect.Width, rect.Height);               

        }

    }

   

    public override DocumentPage GetPage(int pageNumber)

    {

        DocumentPage page = m_Paginator.GetPage(pageNumber);

 

        // Create a wrapper visual for transformation and add extras

        ContainerVisual newpage = new ContainerVisual();

 

        DrawingVisual title = new DrawingVisual();

 

        using (DrawingContext ctx = title.RenderOpen())

        {

            if (m_Typeface == null)

            {

                m_Typeface = new Typeface("Times New Roman");

            }

 

            FormattedText text = new FormattedText("Page " + (pageNumber + 1),

                System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight,

                m_Typeface, 14, Brushes.Black);

 

            ctx.DrawText(text, new Point(0, -96 / 4)); // 1/4 inch above page content

        }

 

        DrawingVisual background = new DrawingVisual();

 

        using (DrawingContext ctx = background.RenderOpen())

        {

            ctx.DrawRectangle(new SolidColorBrush(Color.FromRgb(240, 240, 240)), null, page.ContentBox);

        }

 

        newpage.Children.Add(background); // Scale down page and center

       

            ContainerVisual smallerPage = new ContainerVisual();

            smallerPage.Children.Add(page.Visual);

            smallerPage.Transform = new MatrixTransform(0.95, 0, 0, 0.95,

                0.025 * page.ContentBox.Width, 0.025 * page.ContentBox.Height);

 

        newpage.Children.Add(smallerPage);

        newpage.Children.Add(title);

 

        newpage.Transform = new TranslateTransform(m_Margin.Width, m_Margin.Height);

 

        return new DocumentPage(newpage, m_PageSize, Move(page.BleedBox), Move(page.ContentBox));

    }

 

    public override bool IsPageCountValid

    {

        get

        {

            return m_Paginator.IsPageCountValid;

        }

    }

 

    public override int PageCount

    {

        get

        {

            return m_Paginator.PageCount;

        }

    }

 

    public override Size PageSize

    {

        get

        {

            return m_Paginator.PageSize;

        }

       

        set

        {

            m_Paginator.PageSize = value;

        }

    }

 

    public override IDocumentPaginatorSource Source

    {

        get

        {

            return m_Paginator.Source;

        }

    }

}

The DocumentPaginatorWrapper constructor modifies the page size of the original paginator based on required page size and margin. The new GetPage method calls the original GetPage method to get a page, then adds a header Visual, a background Visual (just to show the original page), and then move the combined Visual by top left margin. To demonstrate that you can easily transform a Visual, the original page Visual is scaled down and centered within its original box.

Here is the code calling the wrapper class to load a flow XAML document and convert to an XPS document:

public static int SaveAsXps(string fileName)

{

    object doc;

 

    FileInfo fileInfo = new FileInfo(fileName);

 

    using (FileStream file = fileInfo.OpenRead())

    {

        System.Windows.Markup.ParserContext context = new System.Windows.Markup.ParserContext();

        context.BaseUri = new Uri(fileInfo.FullName, UriKind.Absolute);

        doc = System.Windows.Markup.XamlReader.Load(file, context);

    }

 

    if (! (doc is IDocumentPaginatorSource))

    {

        Console.WriteLine("DocumentPaginatorSource expected");

        return -1;

    }

 

    using (Package container = Package.Open(fileName + ".xps", FileMode.Create))

    {

        using (XpsDocument xpsDoc = new XpsDocument(container, CompressionOption.Maximum))

        {

            XpsSerializationManager rsm = new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);

           

            DocumentPaginator paginator = ((IDocumentPaginatorSource)doc).DocumentPaginator;

 

            // 8 inch x 6 inch, with half inch margin

            paginator = new DocumentPaginatorWrapper(paginator, new Size(768, 676), new Size(48, 48));

 

            rsm.SaveAsXaml(paginator);

        }

    }

 

    Console.WriteLine("{0} generated.", fileName + ".xps");

 

    return 0;

}

The code above loads a flow XAML document and paginates it to 8 inch by 6 inch, with half inch margin.

Here is a sample output:

The gray background is added to illustrate margin, header, and original page. The original page is scaled down by 5% and centered in the gray box.

The sample XAML file is extracted from WPF Feature Montage sample (http://wpf.netfx3.com/files/folders/code_snippets/entry3752.aspx).

Published Saturday, March 10, 2007 11:39 AM by fyuan
Filed under: ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

Tuesday, March 13, 2007 12:15 PM by Kepar

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Nice article! it really helped me create multipaged XPS files from XAML.

one small detail: I have to call m_Paginator.ComputePageCount(); before using the PageCount property

Wednesday, March 28, 2007 10:37 AM by Senthil

# Convert XAML Page Document to XPS with Style (multiple page, page size, header, margin)

how to convert the xaml page into xps.

Friday, March 30, 2007 2:21 PM by Duncan (Merrion) Rantz

# More document creation samples for XPS

Feng Yuan, one of the main sources for printing information has posted a blog with a code sample to convert...

Monday, April 16, 2007 10:30 PM by Darrel

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hi Feng,

I am trying to access the DocumentPaginator of the WPF Window that I have dynamically created in code (C#) (i.e. it is not based on XAML).  Any suggestions on how/if this can be done?

I have been able to create an XPS document of the Window but would like to split it over multiple pages as you have done here.

Thanks in advance,

Darrel

Tuesday, April 17, 2007 7:45 PM by Ragha

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Feng,

I am new to XPS and I am looking for any tool to create the XPS template that I can later replace with the values. Could you please let me know if there is something that already exists to create XPS templates?

Thanks

Tuesday, July 31, 2007 11:56 PM by standgale

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

hi, thanks for this article, it is very good and very useful. I was wondering whether you could help me with a question though.

Whether I use this method you have given or simply get a paginator by means of

DocumentPaginator paginator = ((IDocumentPaginatorSource)doc).DocumentPaginator; and use that, the pages all have a ContentBox that is smaller than the page size, so the apparent margin of the page is greater when printing and the content area is a bit smaller. I read that the default size of the ContentBox is the same as PageSize, and that the default location is (0,0). Neither of these are the case. I was wondering if you knew how to change the ContentBox properties (the ContentBox is a read-only property) or where/how they were being set. the location, for instance seems to always be offset by 18.1796875 and the width and height correspondingly smaller by twice that.

Wednesday, August 01, 2007 7:13 PM by standgale

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

hi, me again, I still don't know how the ContentBox property was being sneakily set, but I think I have managed to fix it.

Thanks again for this article, it really is very useful.

Monday, September 10, 2007 9:29 AM by Carlos

# Hi, I need to genarate a Xps using several FlowDocumnets

Hi, I need to genarate a Xps using several FlowDocumnets.  Any ideas ?.  This blog show how to make a xps from 1 FlowDocument, i need to make it from various FlowDocuments.

Thursday, October 18, 2007 11:58 AM by roblcecil

# Specified Visual is already a child of another Visual or the root of a CompositionTarget.

Hi, I have created code around the Flow Document Paginator concept above, and feed the paginator into a DocumentPageView along with paging buttons (forward, back, beginning, end) to view the modified paginator. In certain cases I am getting

"Specified Visual is already a child of another Visual or the root of a CompositionTarget." on the line where the inner DocumentPage is being added to the children of the smaller page:

smallerPage.Children.Add(page.Visual);

This happens when I page through document, all the way to end and go backwards. It seems to be  sensitive to how fast I page.

Any ideas?

Tuesday, May 13, 2008 9:34 AM by Cow-Killer

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

This code wont work with DataBinding... I mean XPS file wont contating dynamic items:-(

Any solution?

Thx

Wednesday, May 14, 2008 12:58 AM by Linda Rawson

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

The only method I found for databinding was this article ->  http://roecode.wordpress.com/2008/04/30/using-flowdocuments-xaml-to-print-xps-documents-part-5/

I am having the same problem.  What a pain to build the report in a big string.

Linda

Tuesday, June 24, 2008 12:38 PM by Mahesh

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

I want to print the name, DOB and Address of my company employees. How can I can Print?

Wednesday, July 09, 2008 6:07 AM by radhika

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hi Can u tell me wht is the name of the .xaml file you used to convert it to Xps? when i took the sample xaml files from the WPF Feature Montage sample ..its giving me error.

So could u please specify the name of the .xaml file you used.

Thanking you.

Saturday, July 12, 2008 5:09 PM by Mark Nijhof

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hi RoeCode,

THanks for your great post, implementing this is next on my list :)

I found a way to get the databinding to work when saving a FlowDocument to XPS. I created a post on my blog about it, you may find it interesting: http://blog.fohjin.com/2008/07/saving-wpf-xaml-flowdocument-to-xps.html If there is another/better way of doing this than I am all ears :)

-Mark

Saturday, July 12, 2008 5:11 PM by Mark Nijhof

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Sorry about the wrong name, Feng Yuan. Is late and copied the wrong stuff.

Saturday, August 09, 2008 5:32 AM by Hammad

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hi,

Can i do it for Silverlight 2.0 Beta 2?

Saturday, October 18, 2008 6:47 AM by Fohjin.com

# Saving a WPF Xaml FlowDocument to XPS with working DataBinding

I needed to create an invoice system, where the look and feel of the invoice could differ and also of

Saturday, November 15, 2008 1:34 AM by James

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hello,

I want to convert a given flow document to XPS, but I want to have different page size for the first few pages. For example: If I have 10 pages in the flow document, for the first 5 i want to have a big top margin and for the rest, I want to start from the top itself.

Can this be achieved?

Hemant

Tuesday, November 25, 2008 10:54 AM by Carlos de Luna Saenz

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

I use the document wrapper but i can't convert the document to XPS since my application runs in a partially trusted environment. When i put the Document inside a FlowDocumentReader the pages numbers are not shown...

Greetings

Monday, December 01, 2008 12:49 PM by Carlos de Luna Saenz

# re: Specified Visual is already a child of another Visual or the root of a CompositionTarget.

I have the same toruble. could you get a solution???

Greetings

Wednesday, December 03, 2008 5:54 PM by Carlos de Luna Saenz

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Yep, look at:

http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/ef9a71b8-1365-4d02-b06e-0b0a2cf3372f

Greetings, i still have tehe trouble for lookeing the document in the flowDocumentReader scrollview

Wednesday, December 17, 2008 10:18 AM by Bram Fokke

# Solution for repeating table headers

I created a DocumentPaginator that automatically repeats table headers (and handles headers and footers as well). Check it out at the CodeProject:

http://www.codeproject.com/KB/WPF/PimpedDocumentPaginator.aspx

Tuesday, December 23, 2008 5:04 AM by Jim

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hi, Feng Yuan, wonderful blogs, could you please as me as your MSN contacts. thanks.

I love WPF very much, and i'd like to communicate with more friends.

my MSN: zhoujiguo1985@live.cn

Thanks a lot in advance.

Tuesday, January 13, 2009 12:41 PM by Carlos de Luna Saenz

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

It's working, i can generate a FlowDocument with the paginator wrapper and the FlowDocumentPageViewer displays it OK, but zoom is not working, also on the print dialog the user selection range is not working, the print is made always from the entire doc.

Monday, March 09, 2009 4:37 AM by Gerry

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hi Fyuan,

where i can search a code example of your example?

Thanks .

Have a fun

Thursday, April 16, 2009 2:18 AM by Avinash Patil

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

Hello Fyuan,

actually i want to know that, from where i will get the class DocumentPaginator which is used in the code.

plz help me for the same

Thanks & Regards

Avinash Patil

Tuesday, June 02, 2009 8:02 PM by Kenny

# re: Convert XAML Flow Document to XPS with Style (multiple page, page size, header, margin)

This really helped me a lot.  I am generating XPS knowledge tests (that come from XML used as source for web-based training) and then using CutePDF to 'print' the XPS docs and save them as PDF files.  I added page numbers and some other footer information using your code above.  Thanks a lot for the post!

Leave a Comment

(required) 
required 
(required) 
 
Page view tracker