PDFsharp & MigraDoc Foundation
https://forum.pdfsharp.net/

AcroForm Rect Size is loaded incorrectly
https://forum.pdfsharp.net/viewtopic.php?f=2&t=3572
Page 1 of 1

Author:  RBrNx [ Thu Apr 13, 2017 10:39 am ]
Post subject:  AcroForm Rect Size is loaded incorrectly

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

Author:  Thomas Hoevel [ Thu Apr 13, 2017 11:53 am ]
Post subject:  Re: AcroForm Rect Size is loaded incorrectly

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.

Author:  RBrNx [ Thu Apr 13, 2017 12:59 pm ]
Post subject:  Re: AcroForm Rect Size is loaded incorrectly

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

Author:  RBrNx [ Wed Apr 19, 2017 9:47 am ]
Post subject:  Re: AcroForm Rect Size is loaded incorrectly

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?

Author:  (void) [ Wed Apr 26, 2017 6:10 pm ]
Post subject:  Re: AcroForm Rect Size is loaded incorrectly

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.

Author:  RBrNx [ Thu Apr 27, 2017 8:43 am ]
Post subject:  Re: AcroForm Rect Size is loaded incorrectly

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

Author:  (void) [ Thu Apr 27, 2017 11:08 am ]
Post subject:  Re: AcroForm Rect Size is loaded incorrectly

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.

Page 1 of 1 All times are UTC
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/