PDFBox 1.8.10: заполнить и подписать документ, повторное заполнение не удается

В моем предыдущем вопросе SO PDFBox 1.8.10: «Заполнить и подписать PDF» дает неверные подписи Я объяснил, как мне не удалось заполнить, а затем подписать PDF-документ, используя PDFBox 1.8.10. После того, как с этим разобрались с какой-то помощью, я продолжаю работать над той же темой. Начиная с doc_v2.pdf (ссылки на файл ниже!), Я заполняю и подписываю его, в результате получается doc_v2_fillsigned.pdf (делаю это за один раз, сохраняя его постепенно. ). Я снова открываю отредактированный документ (снова используя PDFBox) и пытаюсь заполнить другое поле.

Затем сохранение документа приводит к следующей трассировке стека:

    Exception in thread "main" java.lang.NullPointerException
        at org.apache.pdfbox.pdmodel.interactive.form.PDAppearance.calculateFontSize(PDAppearance.java:930)
        at org.apache.pdfbox.pdmodel.interactive.form.PDAppearance.setAppearanceValue(PDAppearance.java:359)
        at org.apache.pdfbox.pdmodel.interactive.form.PDVariableText.setValue(PDVariableText.java:131)
        at com.c10n.scalibur.ehealthdemo.examples.PdfEditor.fill(PdfEditor.java:100)
        at com.c10n.scalibur.ehealthdemo.examples.SignPdf_ProfileLayer.start(SignPdf_ProfileLayer.java:66)
        at com.c10n.scalibur.ehealthdemo.examples.SignPdf_ProfileLayer.main(SignPdf_ProfileLayer.java:28)

Что я делаю при неудачном заполнении:

    File curentDocument new File("doc_v2_fillsigned.pdf);
    File newDocument = new File("doc_v2_fillsigned_filled.pdf);

    String fieldName ="New Emergency Contact";
    String value="test";
    PDDocument doc = null;

    try(FileOutputStream fos = new FileOutputStream(newDocument)){

        try(FileInputStream fis = new FileInputStream(currentDocument);){
            int c;
            while ((c = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, c);
            }
        }

        doc = PDDocument.load(currentDocument);
        PDDocumentCatalog catalog = doc.getDocumentCatalog();

        catalog.getCOSObject().setNeedToBeUpdate(true);
        catalog.getPages().getCOSObject().setNeedToBeUpdate(true);

        PDAcroForm form = catalog.getAcroForm();

        form.getCOSObject().setNeedToBeUpdate(true);
        form.getDefaultResources().getCOSObject().setNeedToBeUpdate(true);

        PDField field = form.getField(fieldName);
        field.setValue(value); // here the exception occurs.

        // What should happen afterwards:
        field.getCOSObject().setNeedToBeUpdate(true);
        field.getAcroForm().getCOSObject().setNeedToBeUpdate(true);

        ((COSDictionary) field.getDictionary().getDictionaryObject("AP")).getDictionaryObject("N").setNeedToBeUpdate(true);

        try(FileInputStream fis = new FileInputStream(newDocument)){
            doc.saveIncremental(fis, fos);
        }
    }finally{
        if(null != doc){
            doc.close();
            doc=null;
        }
    }

Файлы:

пустой документ: https://www.dropbox.com/s/xf5pb0ng8k9zd4i/doc_v2.pdf?dl=0

заполненный и подписанный экземпляр: https://www.dropbox.com/s/s8295tfyjpe1l4l/doc_v2_fillsigned.pdf?dl=0

Опять же, любая помощь в разрешении этой проблемы приветствуется!

Обновление:

mkl попросил в комментариях код для создания подписанного pdf. До сих пор я узнал, что достаточно только подписи, чтобы код заполнения, указанный выше, впоследствии не сработал. Итак, вот некоторые исключения из моего кода подписи:

@Override
public byte[] sign(InputStream data) throws SignatureException, IOException {
    CMSTypedDataInputStream input = new CMSTypedDataInputStream(data);
    try {

        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        InputStream in = new ByteArrayInputStream(certData);

        X509Certificate signCert = (X509Certificate)certFactory.generateCertificate(in);

        ContentSigner signer = new MyContentSigner(profile);

        SignerInfoGenerator i = new JcaSignerInfoGeneratorBuilder(
                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())

        .build(signer,signCert);

        Store<?> certStore = new JcaCertStore(Collections.singletonList(signCert));

        CMSSignedDataGenerator cmsGen = new CMSSignedDataGenerator();

        cmsGen.addCertificates(certStore);
        cmsGen.addSignerInfoGenerator(i);

        CMSSignedData signedData = cmsGen.generate(input);

        byte[] result =signedData.getEncoded(); 
        return result;
    } catch (Exception e) {
        e.printStackTrace();
        throw new SignatureException(e);
    }
}

это создает подпись, используя BouncyCastle 1.52, код вызывается из

public void sign(SignatureInterface signer, String signatureFieldName, int pageNumber, String location, String reason, boolean lock) throws IOException, SignatureException{
    PDSignature signature = new PDSignature();
    signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
    signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); // for visible sigs!

    signature.setLocation(location);
    signature.setReason(reason);

    signature.setSignDate(Calendar.getInstance());

    SignatureOptions options = makeSignatureVisible(signature,signatureFieldName, pageNumber, lock );

    doc.addSignature(signature, signer, options);
}

который использует следующий метод для создания видимой подписи в некотором поле подписи, добавляя туда изображение и правильно размещая его:

SignatureOptions makeSignatureVisible( PDSignature signature, String fieldName, int pageNumber, boolean lock) throws IOException{
    PDDocumentCatalog catalog = doc.getDocumentCatalog();

    catalog.getCOSObject().setNeedToBeUpdate(true);
    catalog.getPages().getCOSObject().setNeedToBeUpdate(true);

    PDAcroForm form = catalog.getAcroForm();

    form.getCOSObject().setNeedToBeUpdate(true);
    form.getDefaultResources().getCOSObject().setNeedToBeUpdate(true);

    PDSignatureField field = (PDSignatureField) form.getField(fieldName);

    field.setSignature(signature);      
    field.setReadonly(lock);

    FileInputStream image = new FileInputStream("MUniverse_Signature.jpg");

    PDVisibleSignDesigner visibleSig =  new PDVisibleSignDesigner(newDocument.getName(), image, 1);

    PDRectangle area = getFieldArea(field);

    float max_width = area.getWidth();
    float max_height = area.getHeight();

    float scale = 1;

    if(max_height < visibleSig.getHeight()){
        scale = max_height / visibleSig.getHeight();
        System.out.println("scale: "+scale);
    }

    if(max_width < scale*visibleSig.getWidth()){
        scale = max_width / visibleSig.getWidth();
        System.out.println("scale: "+scale);
    }

    float zoom = ((scale-1)*100);

    visibleSig.zoom(zoom);

    PDPage page = (PDPage) doc.getDocumentCatalog().getAllPages().get(pageNumber);
    visibleSig.coordinates(area.getLowerLeftX(),page.getMediaBox().getHeight()-area.getUpperRightY());
    visibleSig.signatureFieldName(fieldName);

    PDVisibleSigProperties signatureProperties = new PDVisibleSigProperties();

    signatureProperties.signerName("name").signerLocation("location").signatureReason("Security")
    .visualSignEnabled(true).setPdVisibleSignature(visibleSig).buildSignature();

    SignatureOptions options = new SignatureOptions();
    options.setVisualSignature(signatureProperties);

    return options;
}

Я подозреваю, что эти фрагменты не нужны, и применение примеров подписи, которые поставляются с PDFBox, приводит к тому же конфликту при попытке впоследствии заполнить PDF-файл с сохранением приращения.

С уважением,

Дэниел


person Daniel Heldt    schedule 08.10.2015    source источник


Ответы (1)


Причина проблемы в том, что каким-то образом при первоначальном заполнении и подписи шрифты в ресурсах по умолчанию словаря интерактивной формы потерялись.

PDFBox при заполнении формы пытается получить доступ к определению шрифта для создания потока внешнего вида. Он не находит его и, следовательно, в конечном итоге терпит неудачу.

В деталях

В исходном документе doc_v2.pdf словарь интерактивной формы выглядит так:

Словарь акроформ в doc_v2.pdf

Вы можете четко видеть записи для ZaDb и Helv в словаре Font в словаре ресурсов по умолчанию DR.

Напротив, интерактивный словарь формы заполненного и подписанного документа doc_v2_fillsigned.pdf выглядит так:

Словарь акроформ в doc_v2_fillsigned.pdf

Как видите, словарь Font в словаре ресурсов DR по умолчанию отсутствует.

Причина

ОП далее заметил:

Я еще немного поигрался и достаточно расписаться. После этого заполнить его не удается. Я предполагаю, что создание моей подписи более или менее похоже на то, как в примере PDFBox, описывающем, как добавить видимую подпись.

Исходя из этого, я просто применил пример PDFBox CreateVisibleSignature к исходному doc_v2.pdf файлу OP. Действительно, это уже удалило словарь ресурсов по умолчанию Font.

Таким образом, это однозначно выглядит как ошибка PDFBox.

PS: На Apache Jira ...

Просматривая PDFBox Jira, можно найти проблемы в этом отношении:

Таким образом, это известная проблема. Я не уверен, учитывает ли разработка PDFBox голосование за проблемы при расстановке приоритетов, но если вы заинтересованы в решении этой проблемы, голосование не повредит ...;)

person mkl    schedule 16.10.2015
comment
Еще раз спасибо @mkl, еще немного поигрался и достаточно подписать. После этого заполнить его не удается. Я предполагаю, что создание моей подписи более или менее похоже на то, как в примере PDFBox, описывающем, как добавить видимую подпись. Кроме того, я добавил свой SignatureInterface: sign () выше (кроме BouncyCastle Signer, который здесь не имеет значения) и код для инкрементального сохранения. - person Daniel Heldt; 19.10.2015
comment
Кстати, какой инструмент вы использовали для просмотра словарей PDF? - person Daniel Heldt; 21.10.2015
comment
Приведенные выше снимки экрана были созданы из RUPS. - person mkl; 21.10.2015
comment
@DanielHeldt Были ли у вас новые результаты? Может быть, от людей PDFBox? - person mkl; 30.10.2015
comment
@mkl Я не уверен, учитывает ли разработка PDFBox голоса по вопросам при расстановке приоритетов - в большинстве случаев мы этого не делаем, но в случае PDFBOX-3114 я увидел огромное количество наблюдателей и голосов по новой проблеме, которую я решил исследовать это я, несмотря на то, что плохо разбирался в подписях. (Но теперь я знаю больше). - person Tilman Hausherr; 17.12.2015
comment
Я только что посмотрел на эту проблему. Много чего произошло за последнее время ...;) - person mkl; 18.12.2015