Reputation: 5543
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
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
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