locked
create multipage xps from flowdocuments RRS feed

  • Question

  • hi

    ive read 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

    but my need is a bit different

    im creating many separate flowdocuments via code

    now i want to create a single xpsdocument where each page is another flowdocument from my collection of flowdocuments

    is there a way to add pages to the xps document?

    heres the current code

     Dim strm = New MemoryStream
            Dim pkg As Packaging.Package = Package.Open(strm, FileMode.OpenOrCreate)
            Dim pack = "pack://" & Guid.NewGuid.ToString & ".xps"
            PackageStore.AddPackage(New Uri("pack://" & Guid.NewGuid.ToString & ".xps"), pkg)
            Dim xpsDoc As New Xps.Packaging.XpsDocument(pkg, CompressionOption.Maximum, pack)
            Dim xpsSM As New XpsSerializationManager(New XpsPackagingPolicy(xpsDoc), False)
            xpsSM.SaveAsXaml(MyFlowDoc.DocumentPaginator)
            Dim MyFixedDoc = xpsDoc.GetFixedDocumentSequence

    as you can see, it only works for a single flowdocument

    id appreciate any help or ideas

    thanks

    Monday, July 2, 2012 10:55 AM

Answers

  • Hi YismanOl,

    OK, below are some quick codes that fills the blanks. It demonstrate how to merge 3 flowducuments into 1 xps file.

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                MemoryStream strm = new MemoryStream();
                Package pkg = Package.Open(strm, FileMode.OpenOrCreate);
                string pack = "pack://" + Guid.NewGuid().ToString() + ".xps";
                PackageStore.AddPackage(new Uri(pack), pkg);
                XpsDocument xpsDoc = new XpsDocument(pkg, CompressionOption.Maximum, pack);
                XpsSerializationManager xpsSM = new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);
    
                FlowDocument doc1 = new FlowDocument(new Paragraph(new Run("doc1")));
                FlowDocument doc2 = new FlowDocument(new Paragraph(new Run("doc2")));
                FlowDocument doc3 = new FlowDocument(new Paragraph(new Run("doc3")));
                List<DocumentPaginator> documentPaginators = new List<DocumentPaginator>();
                documentPaginators.Add(((IDocumentPaginatorSource)doc1).DocumentPaginator);
                documentPaginators.Add(((IDocumentPaginatorSource)doc2).DocumentPaginator);
                documentPaginators.Add(((IDocumentPaginatorSource)doc3).DocumentPaginator);
    
                xpsSM.SaveAsXaml(new DocumentPaginatorMerger(documentPaginators, new Size(300, 300)));
                FixedDocumentSequence MyFixedDoc = xpsDoc.GetFixedDocumentSequence();
    
                DocumentViewer docViewer = new DocumentViewer();
                docViewer.Document = MyFixedDoc;
    
                this.Content = docViewer;
    
            }
    
        }
    
        public class DocumentPaginatorMerger : DocumentPaginator
        {
            IList<DocumentPaginator> _DocumentPaginators;
            int _PageCount;
            Size _PageSize;
    
            public DocumentPaginatorMerger(IList<DocumentPaginator> documentPaginators, Size pageSize)
            {
                _DocumentPaginators = documentPaginators;
                PageSize = pageSize;
            }
    
    
    
            public override DocumentPage GetPage(int pageNumber)
            {
                int remainingPageNumber = pageNumber;
    
                DocumentPage documentPage = null;
                foreach (var documentPaginator in _DocumentPaginators)
                {
                    if (remainingPageNumber < documentPaginator.PageCount)
                    {
                        documentPage = documentPaginator.GetPage(remainingPageNumber);
                        break;
                    }
                    else
                    {
                        remainingPageNumber -= documentPaginator.PageCount;
                    }
                }
                return documentPage;
            }
    
            public override bool IsPageCountValid
            {
                get
                {
                    foreach (var documentPaginator in _DocumentPaginators)
                    {
                        if (!documentPaginator.IsPageCountValid)
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }
    
            public override int PageCount
            {
                get
                {
                    return _PageCount;
                }
    
            }
    
            public override Size PageSize
            {
                get
                {
                    return _PageSize;
                }
    
                set
                {
                    _PageSize = value;
    
                    _PageCount = 0;
                    foreach (var documentPaginator in _DocumentPaginators)
                    {
                        documentPaginator.PageSize = value;
                        documentPaginator.ComputePageCount();
                        _PageCount += documentPaginator.PageCount;
                    }
                }
            }
    
            public override IDocumentPaginatorSource Source
            {
                get
                {
                    return _DocumentPaginators.First().Source;
                }
            }
        }

    Best regards,


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us



    • Edited by Min Zhu Thursday, July 12, 2012 1:22 AM
    • Marked as answer by YismanOl Friday, July 13, 2012 6:48 AM
    Thursday, July 12, 2012 1:21 AM
  • Hi YismanOl,

    Based on Feng Yuan's blog linked above, I think it's possible to convert several FlowDocuments into a multipage XPS. It's not exact what that blog demonstrates but it can be achieved in the same manner.

    1. XpsSerializationManager accepts a DocumentPaginator object for creating XPS file.
    2. You can create your own DocumentPaginator object and use that to create a XPS file.
    3. You need to provide DocumentPage for the custom DocumentPaginator. The DocumentPage can be easily retrieved from FlowDocument's default DocumentPaginator. ((IDocumentPaginatorSource)doc).DocumentPaginator. By doing this, you can successfully merge several FlowDocuments into one.

    If I misunderstand anything, please feel free to let me know.

    Best regards,


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us

    • Marked as answer by Min Zhu Monday, July 16, 2012 1:50 AM
    Monday, July 9, 2012 4:51 AM

All replies

  • Hi YismanOl,

    You could convert your flowdocuments to FixedPage firstly, and then you could loop those FixedPage, and export to Xpsdocument.

    http://msdn.microsoft.com/en-us/library/system.windows.documents.fixedpage.aspx

    best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, July 3, 2012 9:12 AM
  • hi

    thanks for posting (as usual :-) ...)

    excellent idea

    i actually tried it like this

    Dim fp = New FixedPage
    fp.Children.Add(MyFlowDoc.DocumentPaginator.GetPage(0).Visual)

    but this gives me an error

    Unable to cast object of type 'MS.Internal.PtsHost.PageVisual' to type 'System.Windows.UIElement'.

    and i couldnt find any info on this err on the web

    thank you very much

    p.s. .DocumentPaginator is just an extension method instead of "CType(MyFlowDoc, IDocumentPaginatorSource).DocumentPaginator"

     

    Tuesday, July 3, 2012 10:56 AM
  • Hi YismanOl,

    you could try to cast "MyFlowDoc.DocumentPaginator.GetPage(0).Visual" to "FixedPage".

    (FixedPage)(MyFlowDoc.DocumentPaginator.GetPage(0).Visual) or DirectCast(MyFlowDoc.DocumentPaginator.GetPage(0).Visual,Windows.Documents.FixedPage)

    and then UpdateLayout().

    Best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, July 4, 2012 2:49 AM
  • hi

    thanks again

    i tried now

            Dim fp As FixedPage = DirectCast(MyFlowDoc.DocumentPaginator.GetPage(0).Visual, Documents.FixedPage)
    

    but i get the same error as above: Unable to cast object of type 'MS.Internal.PtsHost.PageVisual' to type 'System.Windows.Documents.FixedPage'.

    thanks!

    Wednesday, July 4, 2012 9:55 AM
  • Hi YismanOl,

    I mean you could convert to FixedPage based on yuanfeng's blog to achieve your goal, refer to this blog:

    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

    and check his getPage method:

    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));
     
        }
    

    this method return a DocumentPage, you could change it to a UIElement, for example, a Grid, and then add it to Fixed Page.

    best regards,


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Thursday, July 5, 2012 2:36 AM
  • hi thanks

    for creating a DocumentPage, we don't need this code

    GetPage(0) already returns a DocumentPage

    the problem is how do we convert this here DocumentPage to a FixedPage? or how do we convert it to a UIElement which can then be added as a child of FixedPage

    thanks

    Thursday, July 5, 2012 12:57 PM
  • Hi YismanOl,

    I mean you could use that method to return a UIElement, and then add it to FixedPage.


    Sheldon _Xiao[MSFT]
    MSDN Community Support | Feedback to us
    Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, July 6, 2012 5:01 AM
  • hi

    im afraid were going around in circles...

    let me put it this way, can you please post or link to code that does exactly this: takes a flowdocument or DocumentPage and returns a FixedPage?

    the code above starts off with a DocumentPage, creates a new one, modifies it and return it. in other words it starts off as a DocumetnPage and returns a DocuemntPage. that is useless for our case.

    no where in that code is a FixedDocument or FixedPage being used or created

    heres some air-code of what im trying to achieve

    dim fdoc=new fixeddocument dim fp=new fixedpage fp.children.add("MyFlowDocToFixedPageConverterFunction")

    OR

    fp="MyFlowDocToFixedPageConverterFunction" fdoc.pages.add(fp) DocViewer.Document=fdoc


    Sunday, July 8, 2012 7:41 AM
  • Hi YismanOl,

    Based on Feng Yuan's blog linked above, I think it's possible to convert several FlowDocuments into a multipage XPS. It's not exact what that blog demonstrates but it can be achieved in the same manner.

    1. XpsSerializationManager accepts a DocumentPaginator object for creating XPS file.
    2. You can create your own DocumentPaginator object and use that to create a XPS file.
    3. You need to provide DocumentPage for the custom DocumentPaginator. The DocumentPage can be easily retrieved from FlowDocument's default DocumentPaginator. ((IDocumentPaginatorSource)doc).DocumentPaginator. By doing this, you can successfully merge several FlowDocuments into one.

    If I misunderstand anything, please feel free to let me know.

    Best regards,


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us

    • Marked as answer by Min Zhu Monday, July 16, 2012 1:50 AM
    Monday, July 9, 2012 4:51 AM
  • Hi,
    I believe you understand correctly, the problem is i don't understand you so well...
    I don't see XpsSerializationManager accepting a documentpaginator.
    even then, I dont see how it can accept "multiple" flowdocuments.
    Can you maybe please finish this code for me:
    dim Doc1=new flowdocument
    dim Doc2=new flowdocument
    dim Doc3=new flowdocument
    dim fDoc=new fixeddocument
    ....
    ....[fill fDoc with Doc1,2 & 3]
    ....
    MyDocumentViewer.Document=fDoc
    Monday, July 9, 2012 8:48 AM
  • Hi YismanOl,

    Something like this,

            Dim strm = New MemoryStream
            Dim pkg As Packaging.Package = Package.Open(strm, FileMode.OpenOrCreate)
            Dim pack = "pack://" & Guid.NewGuid.ToString & ".xps"
            PackageStore.AddPackage(New Uri("pack://" & Guid.NewGuid.ToString & ".xps"), pkg)
            Dim xpsDoc As New Xps.Packaging.XpsDocument(pkg, CompressionOption.Maximum, pack)
            Dim xpsSM As New XpsSerializationManager(New XpsPackagingPolicy(xpsDoc), False)
    
            xpsSM.SaveAsXaml(New MyCustomDocumentPaginatorMixer(myFlowDocs))
            Dim MyFixedDoc = xpsDoc.GetFixedDocumentSequence

    And it's MyCustomDocumentPaginatorMixer's job to merge the documents by overriding the GetPage method.

    Best regards,


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, July 10, 2012 8:16 AM
  • hi

    thanks

    well yes

    its the MyCustomDocumentPaginatorMixer that i need

    the rest i basically have in my original code in the first post

    Wednesday, July 11, 2012 6:53 AM
  • Hi YismanOl,

    OK, below are some quick codes that fills the blanks. It demonstrate how to merge 3 flowducuments into 1 xps file.

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                MemoryStream strm = new MemoryStream();
                Package pkg = Package.Open(strm, FileMode.OpenOrCreate);
                string pack = "pack://" + Guid.NewGuid().ToString() + ".xps";
                PackageStore.AddPackage(new Uri(pack), pkg);
                XpsDocument xpsDoc = new XpsDocument(pkg, CompressionOption.Maximum, pack);
                XpsSerializationManager xpsSM = new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);
    
                FlowDocument doc1 = new FlowDocument(new Paragraph(new Run("doc1")));
                FlowDocument doc2 = new FlowDocument(new Paragraph(new Run("doc2")));
                FlowDocument doc3 = new FlowDocument(new Paragraph(new Run("doc3")));
                List<DocumentPaginator> documentPaginators = new List<DocumentPaginator>();
                documentPaginators.Add(((IDocumentPaginatorSource)doc1).DocumentPaginator);
                documentPaginators.Add(((IDocumentPaginatorSource)doc2).DocumentPaginator);
                documentPaginators.Add(((IDocumentPaginatorSource)doc3).DocumentPaginator);
    
                xpsSM.SaveAsXaml(new DocumentPaginatorMerger(documentPaginators, new Size(300, 300)));
                FixedDocumentSequence MyFixedDoc = xpsDoc.GetFixedDocumentSequence();
    
                DocumentViewer docViewer = new DocumentViewer();
                docViewer.Document = MyFixedDoc;
    
                this.Content = docViewer;
    
            }
    
        }
    
        public class DocumentPaginatorMerger : DocumentPaginator
        {
            IList<DocumentPaginator> _DocumentPaginators;
            int _PageCount;
            Size _PageSize;
    
            public DocumentPaginatorMerger(IList<DocumentPaginator> documentPaginators, Size pageSize)
            {
                _DocumentPaginators = documentPaginators;
                PageSize = pageSize;
            }
    
    
    
            public override DocumentPage GetPage(int pageNumber)
            {
                int remainingPageNumber = pageNumber;
    
                DocumentPage documentPage = null;
                foreach (var documentPaginator in _DocumentPaginators)
                {
                    if (remainingPageNumber < documentPaginator.PageCount)
                    {
                        documentPage = documentPaginator.GetPage(remainingPageNumber);
                        break;
                    }
                    else
                    {
                        remainingPageNumber -= documentPaginator.PageCount;
                    }
                }
                return documentPage;
            }
    
            public override bool IsPageCountValid
            {
                get
                {
                    foreach (var documentPaginator in _DocumentPaginators)
                    {
                        if (!documentPaginator.IsPageCountValid)
                        {
                            return false;
                        }
                    }
                    return true;
                }
            }
    
            public override int PageCount
            {
                get
                {
                    return _PageCount;
                }
    
            }
    
            public override Size PageSize
            {
                get
                {
                    return _PageSize;
                }
    
                set
                {
                    _PageSize = value;
    
                    _PageCount = 0;
                    foreach (var documentPaginator in _DocumentPaginators)
                    {
                        documentPaginator.PageSize = value;
                        documentPaginator.ComputePageCount();
                        _PageCount += documentPaginator.PageCount;
                    }
                }
            }
    
            public override IDocumentPaginatorSource Source
            {
                get
                {
                    return _DocumentPaginators.First().Source;
                }
            }
        }

    Best regards,


    Min Zhu [MSFT]
    MSDN Community Support | Feedback to us



    • Edited by Min Zhu Thursday, July 12, 2012 1:22 AM
    • Marked as answer by YismanOl Friday, July 13, 2012 6:48 AM
    Thursday, July 12, 2012 1:21 AM
  • hurrah to Min Zhu!

    its working!!!

    thanks a million!

    i don't yet understand the code, and hope to work it through

    but for now, my client will be happy!(hopefully... :-))

    Friday, July 13, 2012 6:49 AM