Is it possible to modify PDF Form Field Names?

2020-05-28 08:39发布

问题:

Here's the situation. I have a PDF with automatically generated pdf form field names. The problem is that these names are not very user friendly. They look something like : topmostSubform[0].Page1[0].Website_Address[0]

I want to be able to change them so that they are something like WebsiteAddress. I have access to ABCPDF and I have experience with iTextSharp, but I have tried using these API's to do this (access form fields and try to rename), but it does not seem as if it is possible.

Does anybody have any experience trying to do this via an API of some sort (preferably open source). Code is .Net also.

回答1:

The good news: you can change field names in iTextSharp.

You can't actually edit a PDF though. You'd read in an existing PDF, update your field names in memory and then write out your revised PDF. To change a field name call the AcroFields.RenameField method.

Here's a snippet:

PdfReader reader = new PdfReader(PDF_PATH);
using (FileStream fs = new FileStream("Test Out.pdf", FileMode.Create)) {
    PdfStamper stamper = new PdfStamper(reader, fs);
    AcroFields fields = stamper.AcroFields;
    fields.RenameField("oldFieldName", "newFieldName");
    stamper.Close();
}

Now the bad news: there appear to be limitations in the characters you can use in the renamed fields.

I tested the snippet above with your example field name and it didn't work. Remove the periods though and it does work. I'm not sure if there's a workaround but this may be a problem for you,



回答2:

The full name of an AcroForm field isn't explicitly stored within a field. It's actually derived from a hierarchy of fields, with a dot delimited list of ancestors appearing on the left.

Simply renaming a field from 'topmostSubform[0].Page1[0].Website_Address[0]' to 'WebsiteAddress' is therefore unlikely to produce a correct result.

You'll find section 8.6.2 'Field Dictionaries' of the PDF reference provides a good explanation of how field naming works ;-)

Basically, each field in an AcroForm is define by a dictionary, which may contain certain optional entries pertaining to a field's name.

  • Key '/T' specifies the partial name. In your question, 'topmostSubform[0]', 'Page1[0]', and Website_Address[0], all represent partial names.

  • Key '/TU' specifies an alternative 'user-friendly' name for fields, which can be used in place of the actual field name for identifying fields in a user interface.

Instead of renaming the field in question, think about adding a /TU entry!

The example below uses ABCpdf to iterate through all the fields in an AcroForm and insert an alternate name into a field based on its partial name.

VBScript:

Set theDoc = CreateObject("ABCpdf7.Doc")
theDoc.Read "myForm.pdf"

Dim theFieldIDs, theList
theFieldIDs = theDoc.GetInfo(theDoc.Root, "Field IDs")
theList = Split(theFieldIDs, ",")

For Each fieldID In theList
    thePartialName = theDoc.GetInfo(fieldID, "/T:text")
    theDoc.SetInfo fieldID, "/TU:text", thePartialName
Next

theDoc.Save "output.pdf"
theDoc.Clear

Changing "/TU:text" to "/T:text" will set a field's partial name.

Examples written in C# and VB.NET of the functions used can be found here: Doc.GetInfo, Doc.SetInfo. See also the documentation on Object Paths.



回答3:

I had the problem yesterday and after trying out answers in the forum and others but made no headway. My code looked like this.

// Open up the file and read the fields on it.
var pdfReader = new PdfReader(PATH_TO_PDF);
var fs = new FileStream(pdfFilename, FileMode.Create, FileAccess.ReadWrite)
var stamper = new PdfStamper(pdfReader, fs);
var pdfFields = stamper.AcroFields;

//I thought this next line of code I commented out will do it
//pdfFields.RenameField("currentFieldName", "newFieldName");

// It did for some fields, but returned false for others.
// Then I looked at the AcroFields.RenameField method in itextSharp source and noticed some restrictions. You may want to do the same.
//  So I replaced that line  pdfFields.RenameField(currentFieldName, newFieldName); with these 5 lines

AcroFields.Item item = pdfFields.Fields[currentFieldName];
PdfString ss = new PdfString(newFieldName, PdfObject.TEXT_UNICODE);
item.WriteToAll(PdfName.T, ss, AcroFields.Item.WRITE_VALUE | AcroFields.Item.WRITE_MERGED);
item.MarkUsed(pdfFields, AcroFields.Item.WRITE_VALUE);
pdfFields.Fields[newFieldName] = item;

And that did the job



回答4:

Maybe you can consider this:

  • iText is not free, in a propietary software enviroment (2.1.7 it/s last 'free' version).
  • AbcPDF is not free too.

I have to say that we have AbcPDF license, and we use iText 2.1.7 too in our Java projects... so I can say I agree with previous answers, BUT, it you can't use/buy this products, you can try to replace the name into the pure pdf code (as a plain txt file), following PDF specification:

Example for a signature field:

35 0 obj
<<
/AP <<
/N 37 0 R
>>
/DA (/TimesRoman 0 Tf 0 g)
/F 4
/FT /Sig
/P 29 0 R
/Rect [ 86 426 266 501 ]
/Subtype /Widget
/T (FIELD_MODIF)
/Type /Annot
/V 36 0 R
>>
endobj

Where "FIELD_MODIF" it's the place where put the NEW name.



回答5:

Although you can't rename fields using javascript you can add new fields and delete existing fields. You can also cut and paste between documents. So...

One. Develop renaming script, e.g.:

    var doc = app.activeDocs[0];
    var fnames = new Array();
    for ( var i = 0; i < doc.numFields - 1; i++) {      
        fnames[i] = doc.getNthFieldName(i);
    }
    for (var i = 0; i < doc.numFields - 1; i++){        
        var f = doc.getField(fnames[i] + ".0");
        var nfn = fnames[i].replace("1","2");
        var rb = doc.addField(nfn,"radiobutton",0,f.rect)
        for ( var j = 1; j < 9; j++){//Add the other 8
            f = doc.getField(fnames[i] + "." + j);
            doc.addField(nfn,"radiobutton",0,f.rect)
        }
        rb.setExportValues([1,2,3,4,5,6,7,8,9]);
        rb.borderStyle = f.borderStyle;
        rb.strokeColor = f.strokeColor;
        rb.fillColor = f.fillColor;
        doc.removeField(fnames[i]);
        console.println(fnames[i] + " to " + nfn);
    }

Notes:
Renaming fields may change order of fields for getNthFieldName, so get these first.
A group of radiobuttons is one field (getField("Groupname")), to get the n'th in the same group use getField("Groupname.n"), you need these for the position rectangle. Properties which apply to all can be set enmase.
addField parameters are: field name, field type, page, position rectangle)
In the example there are 9 radiobuttons in each group

Two. Cut and paste the fields you want to rename to a blank pdf (I've assumed one page above). Run the script. Cut and paste back.

You may need to change app.activeDocs[0] to app.activeDocs[1] or replace "doc" with "this" if keeping both documents open when running the script



回答6:

best way is to store fields rect and then create new field with desired name and stored rect position, new field will be on same position as old field, ta da :) here is the code:

Sub CreateNewField()
 Create = "var f = this.getField('" & oldFieldName & "'); var rect = f.rect; 
 this.addField('" & newFieldName & "','text',0,rect);"
 FormFields.ExecuteThisJavascript Create
 DeleteField 'sub to delete old field
End Sub


回答7:

Yes, it's possible to rename form fields. I don't have any experience with an source code API that will help you with this, but my companies PDF SDK can help you do this, and from a little bit of searching it appears that iText will indeed let you rename form fields.



回答8:

Here is a java example found in the book, "iText in Action"

This is taken from their example source code for the book and helped me immensely with this same issue. part2.chapter06.ConcatenateForms2

public static void main(String[] args)
    throws IOException, DocumentException {
    // Create a PdfCopyFields object
    PdfCopyFields copy
        = new PdfCopyFields(new FileOutputStream(RESULT));
    // add a document
    PdfReader reader1 = new PdfReader(renameFieldsIn(DATASHEET, 1));
    copy.addDocument(reader1);
    // add a document
    PdfReader reader2 = new PdfReader(renameFieldsIn(DATASHEET, 2));
    copy.addDocument(reader2);
    // Close the PdfCopyFields object
    copy.close();
    reader1.close();
    reader2.close();
}


private static byte[] renameFieldsIn(String datasheet, int i)
    throws IOException, DocumentException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // Create the stamper
    PdfStamper stamper = new PdfStamper(new PdfReader(datasheet), baos);
    // Get the fields
    AcroFields form = stamper.getAcroFields();
    // Loop over the fields
    Set<String> keys = new HashSet<String>(form.getFields().keySet());
    for (String key : keys) {
        // rename the fields
        form.renameField(key, String.format("%s_%d", key, i));
    }
    // close the stamper
    stamper.close();
    return baos.toByteArray();
}