Setting AcroField Position using iTextSharp

2019-08-12 03:15发布

问题:

The iTextSharp AcroFields class has a GetFieldPosition method.

I am attempting to modify a field's position programmatically. I'm wondering why there is no corresponding SetFieldPosition, or is there another way of changing a field's size or position?

回答1:

See this for part of the reason. There isn't actually a singular GetFieldPosition but instead a plural GetFieldPositions since you can technically have multiple fields with the same name. So a theoretical SetFieldPositions would need to take this into account or it might erase some fields.

The other reason is that a field's defining areas aren't always that simple. Take this very simple bit of code:

var tf = new TextField(writer, new iTextSharp.text.Rectangle(100, 400, 500, 500), "name");
writer.AddAnnotation(tf.GetTextField());

It creates a 400x100 text field with corners at 100,400 and 500,500. It also creates an appearance entry /AP with a bounding box of 0,0 and 400,100 giving the normal appearance area that you see before clicking into the field. If you literally just changed the "position" by moving the two corners accordingly you'd be okay but since the position is really just a Rectangle there's nothing stopping you from making it wider or taller. In that case this BBox entry would need to get updated, too. This is a simple case and iText could probably work through it. Check boxes, however, usually have at least two appearance states that need to be altered. There could also be other cases that it might not make sense to propagate these changes through.

If you want to reposition a field, however, it can still be done, but the burden is on you to do it sanely. Imagine a simple PDF with just this single text field:

var tf = new TextField(writer, new iTextSharp.text.Rectangle(100, 400, 500, 500), "name");
tf.Text = "Hi";
tf.BorderColor = BaseColor.BLACK;
tf.BorderWidth = 1;

writer.AddAnnotation(tf.GetTextField());

You can "move" the text field with a PdfStamper by getting the actual field and adjusting its RECT array. The code below.

using (var fs = new FileStream(testFile, FileMode.Create,FileAccess.Write,FileShare.None)) {
    using (var reader = new PdfReader(bytes)) {
        using (var stamper = new PdfStamper(reader, fs)) {

            //Get our name field
            var nameField = reader.AcroFields.GetFieldItem("name");

            //Grab the first widget inside of it (there could be more)
            var w = nameField.GetWidget(0);

            //Grab the bounding array
            var r = w.GetAsArray(PdfName.RECT);

            //Check both of the Y values
            r[1] = new PdfNumber(r.GetAsNumber(1).IntValue - 300);
            r[3] = new PdfNumber(r.GetAsNumber(3).IntValue - 300);


        }
    }
}