如何在pdf中仅重命名第一个找到的重复acrofield?

在我的PDF上,我有一个名为Text1的重复字段 . 现在我想将名为Text1的第一个找到的acrofield重命名为foobar . 名称为Text1的另一个字段应保持不变,以便我的新表单包含字段Text1和foobar .

我正在使用itext库并在那里使用重命名函数,但是此方法会将名称为Text1的所有字段重命名为foobar .

如果有人想用我的pdf测试它,here is a link .

public class RenameField
{
    public static final String SRC = "c:\\test_duplicate_field2.pdf";
    public static final String DEST = "c:\\test_duplicate_field_mod.pdf";

    public static void main(String[] args)
        throws DocumentException, IOException
    {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new RenameField().manipulatePdf(SRC, DEST);
    }

    public void manipulatePdf(String src, String dest)
        throws DocumentException, IOException
    {
        PdfReader reader = new PdfReader(src);
        PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
        AcroFields form = stamper.getAcroFields();
        form.renameField("Text1", "foobar");
        stamper.close();
        reader.close();
        reader = new PdfReader(dest);
        form = reader.getAcroFields();
        Map<String, AcroFields.Item> fields = form.getFields();
        for (String name : fields.keySet()) {
            System.out.println(name);
        }
    }
}

另一种方法是遍历AcroField.Items . 如果有一个项目具有多个值字典(在这种情况下该字段存在多次),则将进行更改 .

for (Map.Entry<String, AcroFields.Item> entry: fieldMap.entrySet())
{
    // extract Values for Field
    String fieldKey = entry.getKey();

    AcroFields.Item item = entry.getValue();
    PdfDictionary dict;

    int numberOfDuplicates = item.values.size();

    if (numberOfDuplicates > 1) {
        for (int i = 0; i < numberOfDuplicates; i++) {
            if (i == 0) {
                log.info("first field wont be changed");
            } else {
                log.info("renaming field " + fieldKey + " round " + i );
                item.getMerged(i).put(PdfName.T, new PdfString(fieldKey + "_" + i ));
                item.getValue(i).put(PdfName.T, new PdfString(fieldKey + "_" + i));                     
                form.regenerateField(fieldKey); 
            }
        }
    }
}

但这导致与上述方法相同的结果与来自itext的renameField函数,两个字段名称都将被更改 . 在调试过程中,我可以看到项目的两个值字典具有相同的object-id,所以当我更改字典[0]的值时,值也将在字典[1]中更改

enter image description here

回答(1)

2 years ago

在我的PDF上,我有一个名为Text1的重复字段

正如布鲁诺在评论中解释的那样,这是错误的 . 您的PDF只有一个名为“Text1”的字段:

34 0 obj
<</DA(/Helv 12 Tf 0 g)/FT/Tx/Kids[28 0 R 29 0 R]/T(Text1)>>
endobj

这个单场有两个孩子

28 0 obj
<</F 4/MK<<>>/P 3 0 R/Parent 34 0 R/Rect[70.305 698.209 220.305 720.209]/Subtype/Widget/Type/Annot>>
endobj
29 0 obj
<</DA(/Helv 12 Tf 0 g)/F 4/MK<<>>/P 3 0 R/Parent 34 0 R/Rect[240.02 697.453 390.02 719.453]/Subtype/Widget/Type/Annot>>
endobj

这些孩子仅仅是小部件注释, they are not fields in their own right.

因此,您的要求

示例代码如何重命名两个小部件注释之一而不是字段

没有意义:没有两个重命名,因为只有字段被命名,只有一个字段 .

你要做的是创建一个名为"foobar"的新字段(复制除 KidsT 之外的原始字段的所有属性),然后将其中一个Text1孩子移动到foobar .

专注于您的用例的示例代码:

PdfReader reader = new PdfReader(resource);

PdfDictionary form = reader.getCatalog().getAsDict(PdfName.ACROFORM);
PdfArray fields = form.getAsArray(PdfName.FIELDS);
for (PdfObject object: fields)
{
    PdfDictionary field = (PdfDictionary) PdfReader.getPdfObject(object);
    if ("Text1".equals(field.getAsString(PdfName.T).toString()))
    {
        PdfDictionary newField = new PdfDictionary();
        PRIndirectReference newFieldRef = reader.addPdfObject(newField);
        fields.add(newFieldRef);
        newField.putAll(field);
        newField.put(PdfName.T, new PdfString("foobar"));
        PdfArray newKids = new PdfArray();
        newField.put(PdfName.KIDS, newKids);
        PdfArray kids = field.getAsArray(PdfName.KIDS);
        PdfObject widget = kids.remove(0);
        newKids.add(widget);
        PdfDictionary widgetDict = (PdfDictionary) PdfReader.getPdfObject(widget);
        widgetDict.put(PdfName.PARENT, newFieldRef);
        break;
    }
}

PdfStamper stamper = new PdfStamper(reader, result);
stamper.close();

SameFieldTwice.java方法 testWidgetToField

显然,对于一般可用的解决方案,还有很多工作要做 .