Vad1mo
Vad1mo

Reputation: 5543

iText attaching file to existing PDF/A-3 results in PdfAConformanceException

I am trying to attach a file to a existing PDF/A-3.

This example explains how to create a PDF/A-3 and attach content to it.

My next step was to adapt the code and use the PdfAStamper instead of the document.

So here is my resulting code.

private ByteArrayOutputStream append(byte[] content, InputStream inPdf) throws IOException, DocumentException {

  ByteArrayOutputStream result = new ByteArrayOutputStream(16000);
  PdfReader reader = new PdfReader(inPdf);
  PdfAStamper stamper = new PdfAStamper(reader, result, PdfAConformanceLevel.PDF_A_3B);

  stamper.createXmpMetadata();

  // Creating PDF/A-3 compliant attachment.
  PdfDictionary embeddedFileParams = new PdfDictionary();
  embeddedFileParams.put(PARAMS, new PdfName(ZF_NAME));
  embeddedFileParams.put(MODDATE, new PdfDate());
  PdfFileSpecification fs = PdfFileSpecification.fileEmbedded(stamper.getWriter(), null,ZF_NAME, content , "text/xml", embeddedFileParams,0);
  fs.put(AFRELATIONSHIP, Alternative);
  stamper.addFileAttachment("file description",fs);

  stamper.close();
  reader.close();
  return result;
}

Here is the Stacktrace of the error.

    com.itextpdf.text.pdf.PdfAConformanceException: EF key of the file specification dictionary for an embedded file shall contain dictionary with valid F key.
    at com.itextpdf.text.pdf.internal.PdfA3Checker.checkFileSpec(PdfA3Checker.java:95)
    at com.itextpdf.text.pdf.internal.PdfAChecker.checkPdfAConformance(PdfAChecker.java:198)
    at com.itextpdf.text.pdf.internal.PdfAConformanceImp.checkPdfIsoConformance(PdfAConformanceImp.java:70)
    at com.itextpdf.text.pdf.PdfWriter.checkPdfIsoConformance(PdfWriter.java:3380)
    at com.itextpdf.text.pdf.PdfWriter.checkPdfIsoConformance(PdfWriter.java:3376)
    at com.itextpdf.text.pdf.PdfFileSpecification.toPdf(PdfFileSpecification.java:309)
    at com.itextpdf.text.pdf.PdfIndirectObject.writeTo(PdfIndirectObject.java:157)
    at com.itextpdf.text.pdf.PdfWriter$PdfBody.write(PdfWriter.java:424)
    at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:402)
    at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:381)
    at com.itextpdf.text.pdf.PdfWriter$PdfBody.add(PdfWriter.java:334)
    at com.itextpdf.text.pdf.PdfWriter.addToBody(PdfWriter.java:819)
    at com.itextpdf.text.pdf.PdfFileSpecification.getReference(PdfFileSpecification.java:256)
    at com.itextpdf.text.pdf.PdfDocument.addFileAttachment(PdfDocument.java:2253)
    at com.itextpdf.text.pdf.PdfWriter.addFileAttachment(PdfWriter.java:1714)
    at com.itextpdf.text.pdf.PdfStamper.addFileAttachment(PdfStamper.java:497)

Now when I try to analyze the Stacktrace and take a look atPdfFileSpecification.fileEmbedded I see that an EF is created with an F and UF entry.

Looking inside PdfA3Checker I see that line PdfDictionary embeddedFile = getDirectDictionary(dict.get(PdfName.F)); is not a directory but a strem.

if (fileSpec.contains(PdfName.EF)) {
    PdfDictionary dict = getDirectDictionary(fileSpec.get(PdfName.EF));
    if (dict == null || !dict.contains(PdfName.F)) {
        throw new PdfAConformanceException(obj1, MessageLocalization.getComposedMessage("ef.key.of.file.specification.dictionary.shall.contain.dictionary.with.valid.f.key"));
    }

    PdfDictionary embeddedFile = getDirectDictionary(dict.get(PdfName.F));
    if (embeddedFile == null) {
        throw new PdfAConformanceException(obj1, MessageLocalization.getComposedMessage("ef.key.of.file.specification.dictionary.shall.contain.dictionary.with.valid.f.key"));
    }

    checkEmbeddedFile(embeddedFile);
}

Is this a bug in iText or am I missing something? By the way I am using iText 5.4.5.

Update 1

As suggested by Bruno an mkl the 4.5.6-Snapshot should contains the fix. I tried my Test case Gist link to full test case against the current trunk. But the result was the same error.

Upvotes: 1

Views: 2746

Answers (2)

mkl
mkl

Reputation: 96029

You ran into a bug very similar to the one focused in Creating PDF/A-3: Embedded file shall contain valid Params key:

The problem (as you found out) is in this code

PdfDictionary embeddedFile = getDirectDictionary(dict.get(PdfName.F));
if (embeddedFile == null) {
    throw new PdfAConformanceException(obj1, MessageLocalization.getComposedMessage("ef.key.of.file.specification.dictionary.shall.contain.dictionary.with.valid.f.key"));
}

in PdfA3Checker.checkFileSpec(PdfWriter, int, Object); even though dict contains a stream named F, getDirectDictionary(dict.get(PdfName.F)) does not return it. The reason is not, though, that a dictionary is sought here (a stream essentially is a dictionary with some additions), but it is an issue in PdfAChecker.getDirectObject which is called by PdfAChecker.getDirectDictionary:

protected PdfObject getDirectObject(PdfObject obj) {
    if (obj == null)
        return null;
    //use counter to prevent indirect reference cycling
    int count = 0;
    while (obj.type() == 0) {
        PdfObject tmp = cachedObjects.get(new RefKey((PdfIndirectReference)obj));
        if (tmp == null)
            break;
        obj = tmp;
        //10 - is max allowed reference chain
        if (count++ > 10)
            break;
    }
    return obj;
}

This method only looks for cached objects (i.e. in cachedObjects) but in your case (and a test of mine, too) this stream has already been written to file and is not in cache anymore resulting in a null returned... correction, cf the PPS: it has been written but it has not been cached to begin with.

PS: PDF/A-3 conform file attachments work if added during PDF creation (using a PdfAWriter), but not if added during PDF manipulation (using a PdfAStamper); maybe the caching is different in these use cases.

PPS: Indeed there is a difference: PdfAWriter overrides the addToBody overloads by adding the added objects to a cache. PdfAStamperImp does not do so and furthermore is derived from PdfStamperImp and PdfWriter, not from PdfAWriter.

Upvotes: 3

Bruno Lowagie
Bruno Lowagie

Reputation: 77606

You've indeed hit a bug in iText 5.4.5. This bug was reported here: Creating PDF/A-3: Embedded file shall contain valid Params key It was fixed in the SVN version of iText. We're preparing the next version of iText (due this week).

Upvotes: 2

Related Questions