PDFsharp & MigraDoc Foundation

PDFsharp - A .NET library for processing PDF & MigraDoc Foundation - Creating documents on the fly
It is currently Thu Jul 18, 2024 5:23 pm

All times are UTC


Forum rules


Please read this before posting on this forum: Forum Rules



Post new topic Reply to topic  [ 7 posts ] 
Author Message
PostPosted: Wed Mar 03, 2010 6:31 pm 
Offline

Joined: Mon Nov 23, 2009 5:23 pm
Posts: 16
I have created a report which displays 1:n relation in 2 different tables. To add some user comfort I have added link between the parent record and the related records and the other way. If both tables have only a few records and the table is on one page it is no problem you can jump between the tables like you want and see always the records.
If the tables have more data so they are displayed over more pages, you always get to the first page of the table. (Sometimes I have about 20 pages...)

So I think all the bookmarks are rendered to the page where the table begins.

Can you confirm this? Is there any solution? :roll:

Thank you in advice.

BR


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 05, 2010 5:54 pm 
Offline

Joined: Mon Nov 23, 2009 5:23 pm
Posts: 16
Have you any suggestions for me? do you need an example?
I really want to solve this problem, because it is one important point of my created document...


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 09, 2010 5:57 pm 
Offline

Joined: Mon Nov 23, 2009 5:23 pm
Posts: 16
After over 2 hours debugging I have found the origin of my problem:

The bookmarks are generated before the data is rendered. So the table is theoretically always on the same page.

Callstack
Code:
    MigraDoc.Rendering.dll!MigraDoc.Rendering.FieldInfos.AddBookmark(string name = "ac_ANF-29146-Y33R7F") Zeile 69   C#
>   MigraDoc.Rendering.dll!MigraDoc.Rendering.ParagraphRenderer.FormatBookmarkField(MigraDoc.DocumentObjectModel.Fields.BookmarkField bookmarkField = {MigraDoc.DocumentObjectModel.Fields.BookmarkField}) Zeile 1616 + 0x23 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.ParagraphRenderer.FormatElement(MigraDoc.DocumentObjectModel.DocumentObject docObj = {MigraDoc.DocumentObjectModel.Fields.BookmarkField}) Zeile 1539 + 0x32 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.ParagraphRenderer.Format(MigraDoc.Rendering.Area area = {MigraDoc.Rendering.Rectangle}, MigraDoc.Rendering.FormatInfo previousFormatInfo = null) Zeile 1392 + 0x21 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.TopDownFormatter.FormatOnAreas(PdfSharp.Drawing.XGraphics gfx = {PdfSharp.Drawing.XGraphics}, bool topLevel = false) Zeile 123 + 0x11 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.FormattedCell.Format(PdfSharp.Drawing.XGraphics gfx = {PdfSharp.Drawing.XGraphics}) Zeile 76 + 0x18 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.TableRenderer.FormatCells() Zeile 285 + 0x10 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.TableRenderer.InitFormat(MigraDoc.Rendering.Area area = {MigraDoc.Rendering.Rectangle}, MigraDoc.Rendering.FormatInfo previousFormatInfo = null) Zeile 261 + 0x8 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.TableRenderer.Format(MigraDoc.Rendering.Area area = {MigraDoc.Rendering.Rectangle}, MigraDoc.Rendering.FormatInfo previousFormatInfo = null) Zeile 306 + 0xe Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.TopDownFormatter.FormatOnAreas(PdfSharp.Drawing.XGraphics gfx = {PdfSharp.Drawing.XGraphics}, bool topLevel = true) Zeile 123 + 0x11 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.FormattedDocument.Format(PdfSharp.Drawing.XGraphics gfx = {PdfSharp.Drawing.XGraphics}) Zeile 118 + 0xf Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.DocumentRenderer.PrepareDocument() Zeile 92 + 0x10 Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.PdfDocumentRenderer.PrepareDocumentRenderer(bool prepareCompletely = true) Zeile 153 + 0xd Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.PdfDocumentRenderer.PrepareRenderPages() Zeile 185 + 0xd Bytes   C#
    MigraDoc.Rendering.dll!MigraDoc.Rendering.PdfDocumentRenderer.RenderDocument() Zeile 163 + 0x8 Bytes   C#
    OMReportingTool.exe!OMReportingTool.Window1.btnSave_Click(object sender = {System.Windows.Controls.Button: Speichern}, System.Windows.RoutedEventArgs e = {System.Windows.RoutedEventArgs}) Zeile 65 + 0xa Bytes   C#


In the method

Code:
internal void AddBookmark(string name)
    {
      if (this.pyhsicalPageNr <= 0)
        return;

      if (this.bookmarks.ContainsKey(name))
        this.bookmarks.Remove(name);

      if (this.pyhsicalPageNr > 0)
        this.bookmarks.Add(name, new BookmarkInfo(this.pyhsicalPageNr, this.displayPageNr));
    }


The PageNr-Fields are always 1 because the bookmarks are collected before the document is rendering. As you see in the callstack it is the DocumentRenderer.PrepareDocument() method.

The solution would be to change this, so that the bookmarks are collected while the table is rendering. It seems to me it is a big change and I haven't an idea where to begin to change this. Can you give me any support to solve this? I have also a few problems to get the relation between the different objects and the workflow of the document rendering.

I would be really happy about any feedback!

Thanks + BR


Top
 Profile  
Reply with quote  
PostPosted: Tue Mar 23, 2010 6:14 pm 
Offline

Joined: Mon Nov 23, 2009 5:23 pm
Posts: 16
I have solved this problem by myself, if anyone is interested in the solution, just ask!


Top
 Profile  
Reply with quote  
PostPosted: Wed Mar 24, 2010 12:30 pm 
Offline
PDFsharp Guru
User avatar

Joined: Mon Oct 16, 2006 8:16 am
Posts: 3101
Location: Cologne, Germany
Hi!

I'm sorry I couldn't help with your problem.

I'd like to know how you solved it.
TIA

_________________
Regards
Thomas Hoevel
PDFsharp Team


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 26, 2010 4:27 pm 
Offline

Joined: Mon Nov 23, 2009 5:23 pm
Posts: 16
The reason was the bookmarks are collected in the fieldinfos before the table is really rendered, so the logic doesn't know the exact page if there is an automatic page break. So i have changed the preparation of the fieldinfos and add the bookmarks at rendering. After the content rendering i add links which can't be rendered.

I don't know if my solution is universal insertable, but it works fine:

1st change in MigraDoc.Rendering.ParagraphRenderer.FormatBookmarkField:

Code:
FormatResult FormatBookmarkField(BookmarkField bookmarkField)
    {
      if (!bookmarkField.Name.Contains("ac_"))     
            this.fieldInfos.AddBookmark(bookmarkField.Name);
      return FormatResult.Ignore;
    }


I have all bookmarks in the 2nd table with the prefix 'ac_' so i can do it this way. Better solution would be if parent is table, but didn't find a field how i can validate this. So I did it like this.

Next change is also in the ParagraphRenderer I have change following Method and the call of it:

Code:
void RenderBookmarkField(BookmarkField bookmarkField)
    {
        this.FieldInfos.AddBookmark(bookmarkField.Name);
        RenderUnderline(0, false);
    }


Change is the method needs the bookmark field and I have added the bookmark at this position. Also I have added to the property the get part.
If the bookmark is already in the fieldinfos it would get updated.

So now the links from the bottom to the top are working. But the Links from the top to the bottom are not there, because the bookmark couldn't be found.
The solution:

Change the EndHyperlink:

Code:
 void EndHyperlink(Hyperlink hyperlink, XUnit right, XUnit bottom)
    {
      this.hyperlinkRect.Width = right - this.hyperlinkRect.X;
      this.hyperlinkRect.Height = bottom - this.hyperlinkRect.Y;
      PdfPage page = this.gfx.PdfPage;
      if (page != null)
      {
        XRect rect = this.gfx.Transformer.WorldToDefaultPage(this.hyperlinkRect);

        switch (hyperlink.Type)
        {
          case HyperlinkType.Local:
            int pageRef = this.fieldInfos.GetPhysicalPageNumber(hyperlink.Name);
            if (pageRef > 0)
                page.AddDocumentLink(new PdfRectangle(rect), pageRef);
            else
            {
                fieldInfos.AddBookmarkToRenderLater(hyperlink.Name, new PdfRectangle(rect));
            }
            break;

          case HyperlinkType.Web:
            page.AddWebLink(new PdfRectangle(rect), hyperlink.Name);
            break;

          case HyperlinkType.File:
            page.AddFileLink(new PdfRectangle(rect), hyperlink.Name);
            break;
        }
        this.hyperlinkRect = new XRect();
      }
    }


For this I have added an Dictionary in the fieldinfos:

Code:
Dictionary<string, PdfRectangle> bookmarksToRenderLater
internal void AddBookmarkToRenderLater(string name, PdfRectangle rect)
    {
        if (this.bookmarksToRenderLater == null)
            this.bookmarksToRenderLater = new Dictionary<string, PdfRectangle>();
        if (this.bookmarksToRenderLater.ContainsKey(name))
            this.bookmarksToRenderLater.Remove(name);

        if (rect != null)
            this.bookmarksToRenderLater.Add(name, rect);
    }


The name bookmark is a bit disappointing, because it is more the links then the bookmarks...

Now I have in the fieldinfos the links incl. bookmarks with the links which couldn't be rendered.
So I have add a few lines to the PdfDocumentRenderer.RenderPages

Code:
public void RenderPages(int startPage, int endPage)
    {
        if (startPage < 1)
            throw new ArgumentOutOfRangeException("startPage");

        if (endPage > this.documentRenderer.FormattedDocument.PageCount)
            throw new ArgumentOutOfRangeException("endPage");

        if (this.documentRenderer == null)
            PrepareDocumentRenderer();

        if (this.pdfDocument == null)
            this.pdfDocument = CreatePdfDocument();

        this.documentRenderer.printDate = DateTime.Now;
        for (int pageNr = startPage; pageNr <= endPage; ++pageNr)
        {
            PdfPage pdfPage = this.pdfDocument.AddPage();
            PageInfo pageInfo = this.documentRenderer.FormattedDocument.GetPageInfo(pageNr);
            pdfPage.Width = pageInfo.Width;
            pdfPage.Height = pageInfo.Height;
            pdfPage.Orientation = pageInfo.Orientation;

            using (XGraphics gfx = XGraphics.FromPdfPage(pdfPage))
            {
                gfx.MUH = this.unicode ? PdfFontEncoding.Unicode : PdfFontEncoding.WinAnsi;
                gfx.MFEH = this.fontEmbedding;
                this.documentRenderer.RenderPage(gfx, pageNr);
            }
        }
        //MIH: Add links of the bookmarks after the completion of the document because http://forum.pdfsharp.net/viewtopic.php?f=2&t=1080
        FieldInfos fieldInfo = null;
        PdfPage page = null;
        for (int pageNr = startPage; pageNr <= endPage; ++pageNr)
        {
            fieldInfo = this.documentRenderer.FormattedDocument.GetFieldInfos(pageNr);
            if (fieldInfo.bookmarksToRenderLater != null)
            {
                using (XGraphics gfx = XGraphics.FromPdfPage(this.pdfDocument.Pages[pageNr - 1]))
                {
                    page = gfx.PdfPage;
                    foreach (KeyValuePair<string, PdfRectangle> entry in fieldInfo.bookmarksToRenderLater)
                        if (fieldInfo.GetPhysicalPageNumber(entry.Key) > 0)
                            page.AddDocumentLink(entry.Value, fieldInfo.GetPhysicalPageNumber(entry.Key));
                }
            }
        }
    }


I hope I got all changes, but if I miss anything it won't be a big thing.

Just ask if there are any things which need more infomation....


Top
 Profile  
Reply with quote  
PostPosted: Thu Feb 16, 2012 10:57 am 
Offline

Joined: Mon Feb 13, 2012 7:03 pm
Posts: 2
Awesome! The actual link, ie. clicking the TOC HyperLink works, but if I add a AddPageRefField to the TOC Hyperlink, in the TOC, I'll get: "Bookmark is not defined within the document.".

This is probably due to this...... Any suggestions on how I can extend the nice work of mr MIH and get the PageRefField working?
Code:
 string GetFieldValue(DocumentObject field)
        {
            if (field is NumericFieldBase)
            {
                int number = -1;
                if (field is PageRefField)
                {
                    PageRefField pageRefField = (PageRefField)field;
                    number = this.fieldInfos.GetShownPageNumber(pageRefField.Name);
                    if (number <= 0)
                    {
                        if (this.phase == Phase.Formatting)
                            return "XX";
                        else
                            return Messages.BookmarkNotDefined(pageRefField.Name);
                    }
                }


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC


Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 27 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Privacy Policy, Data Protection Declaration, Impressum
Powered by phpBB® Forum Software © phpBB Group