PDFsharp & MigraDoc Foundation

PDFsharp - A .NET library for processing PDF & MigraDoc Foundation - Creating documents on the fly
It is currently Sat Sep 23, 2017 12:16 am

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: Thu Apr 13, 2017 10:39 am 
Offline

Joined: Mon Sep 05, 2016 7:54 am
Posts: 22
Hi there,

So I have a program which uses PDFSharp to programmatically fill the AcroForm on a wide range of different PDFs. One particular PDF seems to be throwing some errors up with PDFSharp however, where even though the AcroForm fields are Horizontal on the PDF, when it is loaded by PDFSharp it seems to think they are Portrait and therefore fills the text in with the wrong orientation.

Here is a screenshot of one of the 'problem' AcroFields before being filled
Image

And here is what it looks like once some data has been added to the Field and it has been flattened. (Note: I use the TextFormatter.DrawString function to draw the text onto the PDF and remove the AcroField itself. This of course uses the Rect flag of the AcroField which is where the problem seems to occur.
Image

Now, Debugging the PDFSharp program shows that the Rectangle PDFSharp thinks this box has, has the following X and Y values
Code:
X1=102.276, X2=325.7, Y1= 118.481, Y2=433.802


Which corresponds to a Height of 108.102 and a Width of 16.205, which is obviously 'Portrait' even though the Field is 'Landscape'.

Here is the PDF in question, is there a Fix for this? (Couldn't attach it because it is too big)

As a sidenote, when debugging this I also came across an interesting problem, if one of the AcroFields has a Font of "Helvetica 14" for example, when PDFSharp has loaded it the Font.FaceName and Font.Name are set as "Arial" and the Font.FontFamily is set to "Helvetica". This also happens for the font "Courier" which PDFSharp seems to turn into "Microsoft Sans Serif" and when this Font is used in the TextFormatter.DrawString function it is definitely not drawn as "Courier". Is there a fix for this too?

Thanks, Conor


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 13, 2017 11:53 am 
Offline
empira Employee
User avatar

Joined: Mon Oct 16, 2006 8:16 am
Posts: 2710
Location: Cologne, Germany
Hi, Conor!
RBrNx wrote:
Now, Debugging the PDFSharp program shows that the Rectangle PDFSharp thinks this box has, has the following X and Y values
Code:
X1=102.276, X2=325.7, Y1= 118.481, Y2=433.802


Which corresponds to a Height of 108.102 and a Width of 16.205, which is obviously 'Portrait' even though the Field is 'Landscape'.
Have you tried the patch from this post:
viewtopic.php?p=9591#p9591
Remove three lines from PdfPage c'tor and try again. Not sure if this relates to your problem.

RBrNx wrote:
Here is the PDF in question, is there a Fix for this? (Couldn't attach it because it is too big)
Thanks for providing the file. But AcroForms are not my area of expertise and without code I don't know what to look for.
How did you get the Rectangle for the text box?


Re fonts: PDFsharp does not support the built-in fonts of PDF, so Helvetica and Courier must be replaced by fonts installed on the computer. So find a place where you can map "Courier" to "Courier New" to prevent "Microsoft Sans Serif" from showing.

_________________
Regards
Thomas Hoevel
PDFsharp Team


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 13, 2017 12:59 pm 
Offline

Joined: Mon Sep 05, 2016 7:54 am
Posts: 22
Thomas Hoevel wrote:
Hi, Conor!
Have you tried the patch from this post:
viewtopic.php?p=9591#p9591
Remove three lines from PdfPage c'tor and try again. Not sure if this relates to your problem.


Thank you for your response! I definitely think its something to do with the same issue, I commented out those 3 lines of code, but my problem still seems to exist. Any other ideas?

Quote:
Thanks for providing the file. But AcroForms are not my area of expertise and without code I don't know what to look for.
How did you get the Rectangle for the text box?


To get the Rectangle for the text box I simply use
Code:
var rect = Rectangle;

inside the PdfTextField.cs file. This inherits the 'Rectangle' variable from the PdfAcroField class which get the rectangle from the '/Rect' element on the AcroField like so:

Code:
        public PdfRectangle Rectangle
        {
            get { return Elements.GetRectangle(PdfAnnotation.Keys.Rect); }
        }


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 19, 2017 9:47 am 
Offline

Joined: Mon Sep 05, 2016 7:54 am
Posts: 22
As a follow up to this post I have done some more digging and the issue only seems to appear when Drawing Strings to the PDF. For example, I currently have this function which I use to Draw the Text directly onto the PDF:

Code:
        internal void DrawToPDF(PdfRectangle rect, PdfPage elementPage, XFont font, XParagraphAlignment alignment)
        {
            if (!rect.IsEmpty)
            {
                using (var gfx = XGraphics.FromPdfPage(elementPage))
                {
                    // Note: Page origin [0,0] is bottom left !
                    var text = Text;
                    XTextFormatter tf = new XTextFormatter(gfx);
                    if (text.Length > 0)
                    {
                        var xRect = new XRect(rect.X1, elementPage.Height.Point - rect.Y2, rect.Width, rect.Height);
                        if ((Flags & PdfAcroFieldFlags.Comb) != 0 && MaxLength > 0)
                        {
                            gfx.Save();
                            gfx.IntersectClip(xRect);
                            tf.Alignment = alignment;
                            tf.DrawString(text, font, new XSolidBrush(ForeColor), xRect + new XPoint(0, 1.5), XStringFormats.TopLeft);
                            gfx.Restore();
                        }
                        else
                        {
                            gfx.Save();
                            gfx.IntersectClip(xRect);;
                            tf.Alignment = alignment;
                            tf.DrawString(text, font, new XSolidBrush(ForeColor), xRect + new XPoint(2, 2), XStringFormats.TopLeft);
                            gfx.Restore();
                        }
                    }
                }
            }
        }


If I run this code as it currently stands, the resulting PDF looks like this:
Image

Note: This also happens with gfx.DrawString() instead of tf.DrawString().

However if I replace the code
Code:
tf.Alignment = alignment;
                            tf.DrawString(text, font, new XSolidBrush(ForeColor), xRect + new XPoint(2, 2), XStringFormats.TopLeft);


With code to draw rectangles at the same rectangle location
Code:
gfx.DrawRectangle(new XSolidBrush(XColor.FromArgb(255, 255, 0, 0)), xRect);


Then it produces a PDF that looks like this
Image

I also attempted to write a function which would flip the Height and Width of the PdfRectangles that are used to draw the text on the PDF, which "worked" in the sense it fixed the height and width variables, but it did not affect how the Text was drawn onto the PDF.

Does anyone have any ideas?

Quick Edit:
It is as if the DrawString() function simply looks at the Rotation of the PDF (in this case 90 degrees) and Draws it as if it was a Portrait PDF that was rotated 90 degrees, instead of an actual Landscape PDF which should Draw objects as if they were at 0 degrees. Can anyone point me in the right direction?


Top
 Profile  
Reply with quote  
PostPosted: Wed Apr 26, 2017 6:10 pm 
Offline

Joined: Tue Sep 30, 2014 12:29 pm
Posts: 16
Hi !

I suspect you are not using the "vanilla" version of PdfSharp, but a modified one.
If so, on what version of PdfSharp your code is based on ?
If it's based on 1.32 (like my own modified version), welcome to the club ! :wink:
(as i struggled to solve the same problem a while ago)

What i did:

1.
In XGraphicsPdfRenderer.cs there is this code in the method "BeginPage" :
Code:
            if (this.page != null && this.page.Elements.GetInteger("/Rotate") == 90)  // HACK for InDesign flyer
            {
              defaultViewMatrix.RotatePrepend(90);
              defaultViewMatrix.ScalePrepend(1, -1);
            }
            else
            {
              // Recall that the value of Height depends on Orientation.
              defaultViewMatrix.TranslatePrepend(0, pageHeight);
              defaultViewMatrix.Scale(1, -1, XMatrixOrder.Prepend);
            }

Remove all but what's inside the "else" part.
(if your code is based on 1.5, you can skip this as the hack is already removed)

2.
Take the rotation of the widget itself into account.
In the document you linked, not only the page is rotated 90 degrees, but also the widgets ! (say: Appearances of AcroFields)
You find the rotation of the widget in the /MK dictionary under the /R key; these are also 90.

When flattening the Field:
    - identify and retrieve the Fields' Annotation(s) from the "Kids" array (let's call that "widget" as it's a WidgetAnnotation per Pdf-Spec)
    (note that the AcroField itself may be the widget if the dictionaries are merged)
    - retrieve the rotation of that widget
    - if rotation != 0
      - transform graphics context
      - flip width/height of the fields' rectangle if necessary

In my code it looks like this :
Code:
            var rect = widget.Rectangle;
                if (text.Length > 0)
                {
                    var xRect = new XRect(rect.X1, widget.Page.Height.Point - rect.Y2, rect.Width, rect.Height);
                    if (widget.Rotation != 0)
                    {
                        gfx.RotateAtTransform(-widget.Rotation, xRect.BottomLeft);
                        if (widget.Rotation == 90 || widget.Rotation == 270)
                           xRect = new XRect(rect.X1, widget.Page.Height.Point - rect.Y1, rect.Height, rect.Width);
                    }
                ....

Let me know if that works for you, i may have made other changes i don't recall right now.


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 27, 2017 8:43 am 
Offline

Joined: Mon Sep 05, 2016 7:54 am
Posts: 22
Thank you for your help!

I actually managed to fix my problem in a similar way to you which involved checking the rotation of the page and rotating the XGraphics object accordingly. My rotation code doesn't look as clean as yours though so I may implement your version.

I do run my own modified PdfSharp, you are correct! But I am not really sure which version it is, I don't have the code that you said should be in 1.3, so I am assuming I'm running 1.5.

I'm a little confused though, my flattening code takes place inside the PdfTextField.cs class, like so:

Code:
internal override void Flatten()
        {
            base.Flatten();

            if (HasKids)
            {
                for(int i = 0; i < Fields.Elements.Count; i++)
                {
                    var rect = Fields[i].Rectangle;
                    var page = Fields[i].Page;
                    var font = GetFontFromElement(Fields[i]);
                    XParagraphAlignment alignment = GetAlignment(Fields[i].Elements);
                    if(page.Rotate == 0) DrawToPDF(rect, page, font, alignment);
                    else LandscapeDrawToPDF(rect, page, font, alignment);

                }
            }
            else
            {
                var rect = Rectangle;
                var page = Page;
                XParagraphAlignment alignment = GetAlignment(Elements);
                if (page.Rotate == 0) DrawToPDF(rect, page, Font, alignment);
                else LandscapeDrawToPDF(rect, page, Font, alignment);
            }
        }


where would I get the rotation from inside here?

Thanks, RBrNx


Top
 Profile  
Reply with quote  
PostPosted: Thu Apr 27, 2017 11:08 am 
Offline

Joined: Tue Sep 30, 2014 12:29 pm
Posts: 16
Quote:
where would I get the rotation from inside here?

As mentioned, you have to get the rotation from the WidgetAnnotation.
In the code you posted, the widget may be eiter "Fields[i]" (in the 'if' part) or "this" (in the 'else' part).
(i would recommend checking "Fields[i].Elements" for the existence of a "/Subtype" entry with the value "/Widget" to make sure, it is in fact a widget and not an actual child-field.)
Then retrieve the "/MK" dictionary of that widget and get the rotation from its "/R" entry.


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: No registered users and 5 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:  
Powered by phpBB® Forum Software © phpBB Group