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

http://blogs.msdn.com/b/fyuan/archive/2007/03/10/convert-xaml-flow-document-to-xps-with-style-multiple-page-page-size-header-margin.aspx

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;

        }

    }

}


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;

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章