2

I've been generating PDF forms using iTextSharp for quite a while now, and it's been working fine. However, recently my organization upgraded to Adobe Reader XI; since then, the generated PDFs have started displaying a "Do you want to save changes" dialog when you open the PDF in Reader and then close it again, even if you haven't made any changes to the form.

I did some research, and apparently this is caused by Adobe Reader making some kind of change to the PDF file behind the scenes, either because the PDF itself is corrupted (and Reader fixes it for you) or for some other reason. Further investigation revealed the culprit: I was setting the AcroForm's NeedAppearances flag to true, which causes Reader to generate the form fields' appearances when you open the file, and then delete the NeedAppearances flag from the file -- thus modifying the PDF, and prompting you to save your "changes" when you close it.

Removing the NeedAppearances flag fixed this issue, but spawned a new one to take its place. Without that flag, iTextSharp generates the form fields' appearances itself -- but they only apply to the fields' default, unfocused state. When you click a (multiline text) field to edit it, the Reader-defined appearance takes over, causing a visible change to the field's text contents.

The most obvious change is that the contents shift downwards by a fair amount, but I can fix that by adding a top margin, using the TextField.SetExtraMargin() method. However, the field's character spacing also changes when it gains focus, becoming slightly narrower. It's only narrower by a small amount, so it usually isn't that severe, but there are some cases when it causes the line wrapping positions to shift (e.g. when a word didn't fit on a line before, but does fit when the spacing becomes narrower); when that happens, it's very noticeable, since it affects all lines that come after it.

So I'm left with a dilemma. If I set NeedAppearances to true, I get a consistent appearance whether the field is focused or not; but if I don't set NeedAppearances to true, Reader stops prompting me to save changes when I didn't make any. My question to you is: can I get both of these advantages at once? Or do I have to resign myself to choosing one or the other?

I'm using iTextSharp 5.5.5, but I tried upgrading to the latest version (5.5.8) and it didn't help.

Here is my sample code:

using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

namespace MultilineTextFieldDemo
{
    public class PdfCreator
    {
        public static void Main(string[] args)
        {
            string outputDirName = "PdfOutput",
                   outputFilePath = "";
            byte[] buf = null;

            if (!Directory.Exists(outputDirName))
            {
                Directory.CreateDirectory(outputDirName);
            }

            // Create a PDF form with NeedAppearances = true.
            //
            // PRO: The text field will have a consistent appearance, whether it's focused or not.
            // CON: Adobe Reader will prompt you to save changes when you try to close the file,
            //      even if you haven't changed anything.

            buf = CreatePdf(true);
            if (buf != null && buf.Length > 0)
            {
                outputFilePath = Path.Combine(outputDirName, "PdfTest_NeedAppsTrue.pdf");
                File.WriteAllBytes(outputFilePath, buf);
            }

            // Create a PDF form with NeedAppearances = false.
            //
            // PRO: Adobe Reader won't prompt you to save changes when you close the file, unless
            //      you actually changed something.
            // CON: The text field's appearance will change when it gains or loses focus; the characters
            //      will be slightly closer together when focused, and some lines will change their
            //      wrapping positions.

            buf = CreatePdf(false);
            if (buf != null && buf.Length > 0)
            {
                outputFilePath = Path.Combine(outputDirName, "PdfTest_NeedAppsFalse.pdf");
                File.WriteAllBytes(outputFilePath, buf);
            }
        }

        public static byte[] CreatePdf(bool needAppearances)
        {
            byte[] buf = null;
            float marginsH = Utilities.InchesToPoints(0.65f),
                  marginsV = Utilities.InchesToPoints(0.5f);

            using (MemoryStream ms = new MemoryStream())
            {
                using (Document doc = new Document(PageSize.LETTER, marginsH, marginsH, marginsV, marginsV))
                {
                    using (PdfWriter writer = PdfWriter.GetInstance(doc, ms))
                    {
                        if (needAppearances)
                        {
                            writer.AcroForm.NeedAppearances = true;
                        }

                        doc.Open();

                        // Create and add a label for the text field.

                        Phrase label = new Phrase(
                            String.Format("Text Field (NeedAppearances = {0})", needAppearances),
                            FontFactory.GetFont(BaseFont.HELVETICA, 10, Font.BOLD));
                        doc.Add(label);

                        // Calculate the text field's rectangle. It will be the full width of the document, minus a 2-point
                        // margin on the left and right. Its top edge will be directly under the label, and it will be
                        // 4 inches tall.

                        float llx = doc.GetLeft(2),
                              urx = doc.GetRight(2),
                              ury = doc.GetTop(0) - label.Font.CalculatedSize,
                              lly = ury - Utilities.InchesToPoints(4);

                        // Create the text field.

                        TextField textField = new TextField(writer, new Rectangle(llx, lly, urx, ury), "textField");
                        textField.FontSize = 8;
                        textField.Options = BaseField.MULTILINE;
                        textField.Text =
                            "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ac convallis libero, quis bibendum " +
                            "risus. Vestibulum gravida aliquam tellus, pharetra semper nulla vulputate nec. Nunc eu ornare " +
                            "ligula. Proin pulvinar erat elit, ac elementum ex placerat in. Sed ligula nisl, viverra hendrerit " +
                            "elit ut, lacinia semper diam. Nam in felis fringilla, dignissim metus et, tristique diam. Proin " +
                            "sodales eros non tellus aliquet bibendum. Fusce volutpat, massa at tincidunt porta, dolor justo " +
                            "tincidunt mi, non iaculis justo sem ac erat. Sed dapibus urna vel hendrerit fermentum. Sed rutrum " +
                            "augue sapien, id suscipit nisi porta eu. Morbi vel leo ut tortor faucibus sagittis. Etiam in maximus " +
                            "dui. Quisque et mattis ligula. Integer cursus suscipit semper. Fusce commodo tellus in mattis " +
                            "lobortis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.";

                        // If NeedAppearances is false, add a vertical margin to compensate for the text shifting downward
                        // when the field gains focus (this still doesn't fix the character spacing issue).

                        if (!needAppearances)
                        {
                            textField.SetExtraMargin(0, 2.8f);
                        }

                        // Get the text field's annotation and add it to the document.

                        PdfFormField annot = textField.GetTextField();
                        writer.AddAnnotation(annot);

                        doc.Close();
                    }
                }

                buf = ms.ToArray();
            }

            return buf;
        }
    }
}
MegaJar
  • 201
  • 1
  • 2
  • 8
  • There is no solution to your problem. You have done ample research and everything you say is true. It is also true that Adobe Reader hasn't been consistent in the way it renders the values of text fields. Different versions of Adobe Reader would render the same document differently. That's... annoying, but we have to tolerate that. Flattening the document obviously solves this problem, but in that case, the end user can no longer change the contents of the fields (because all interactivity has been removed). – Bruno Lowagie Feb 12 '16 at 16:32
  • @BrunoLowagie: Thanks for the information. Is there, perhaps, a way to use iTextSharp to customize a field's _focused_ appearance? As far as I know, there are only three supported appearance states: the "Normal", "Rollover", and "Down" appearances. Is there another option I'm not aware of? – MegaJar Feb 16 '16 at 15:30

0 Answers0