I have the below method that merges my PDF documents. However, in some cases where the document has a watermark, the method throws me an error.
public void doMergeUsingItext7(List<InputStream> list, OutputStream outputStream) throws SSException {
try (com.itextpdf.kernel.pdf.PdfWriter writer = new com.itextpdf.kernel.pdf.PdfWriter(outputStream);) {
writer.setSmartMode(Boolean.TRUE);
try (com.itextpdf.kernel.pdf.PdfDocument pdfDoc = new com.itextpdf.kernel.pdf.PdfDocument(writer)) {
pdfDoc.initializeOutlines();
list.forEach((in) -> {
try (com.itextpdf.kernel.pdf.PdfReader reader = new com.itextpdf.kernel.pdf.PdfReader(in);) {
reader.setUnethicalReading(Boolean.TRUE);
try (com.itextpdf.kernel.pdf.PdfDocument addedDoc = new com.itextpdf.kernel.pdf.PdfDocument(reader)) { //ERROR IS THROWN ON THIS LINE
addedDoc.copyPagesTo(1, addedDoc.getNumberOfPages(), pdfDoc);
logger.log(Level.INFO, "Successfully Added the Document to PDF");
} catch (Exception e) {
ExceptionUtils.printRootCauseStackTrace(e);
}
} catch (IOException ex) {
ExceptionUtils.printRootCauseStackTrace(ex);
} catch (Exception e) {
ExceptionUtils.printRootCauseStackTrace(e);
}
});
}
} catch (Exception ex) {
throw new SSException(ex, "Print Version Failed");
}
}
The following error is thrown when the document has a watermark...
com.itextpdf.kernel.PdfException: Illegal length value.
at com.itextpdf.kernel.pdf.PdfEncryption.readAndSetCryptoModeForStdHandler(PdfEncryption.java:523)
at com.itextpdf.kernel.pdf.PdfEncryption.<init>(PdfEncryption.java:229)
at com.itextpdf.kernel.pdf.PdfReader.readDecryptObj(PdfReader.java:1251)
at com.itextpdf.kernel.pdf.PdfReader.readPdf(PdfReader.java:685)
at com.itextpdf.kernel.pdf.PdfDocument.open(PdfDocument.java:1871)
at com.itextpdf.kernel.pdf.PdfDocument.<init>(PdfDocument.java:252)
at com.itextpdf.kernel.pdf.PdfDocument.<init>(PdfDocument.java:234)
at gov.ca.lc.util.PdfUtilFuntions.lambda$doMergeUsingItext7$0(PdfUtilFuntions.java:180)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at gov.ca.lc.util.PdfUtilFuntions.doMergeUsingItext7(PdfUtilFuntions.java:176)
I am not sure what exactly within the document is failing to merge. Any help on fixing this issue is highly appreciated. Thanks.
This is a bug in iText.
The PDF in question is encrypted. Its encryption dictionary has a V value of 1 ("RC4 or AES algorithms with an encryption key length of 40 bits") and an R value of 3 ("for files encrypted with a V value of 2 or 3, or with any “Security handlers of revision 3 or greater” access permissions set to 0").
A Length value for the key length is specified only for V 2 or 3, for V 1 the key length is fixed to 40. Nonetheless, iText in case of R 3 requires a Length value.
As there is no Length value in the encryption dictionary of your document, iText fails reading that file.
As a side note: 40 bit key length security nowadays is no security. So most likely that issue did not pop up earlier because using V 1 security (or 40 bit key length security in general) is useless and, therefore, no use case taken seriously.
If you don't shy away from recompiling iText, fixing the bug is easy. Simply edit the iText kernel class com.itextpdf.kernel.pdf.PdfEncryption
. Its method readAndSetCryptoModeForStdHandler
starts with
int cryptoMode;
int length = 0;
PdfNumber rValue = encDict.getAsNumber(PdfName.R);
if (rValue == null)
throw new PdfException(KernelExceptionMessageConstant.ILLEGAL_R_VALUE);
int revision = rValue.intValue();
boolean embeddedFilesOnlyMode = readEmbeddedFilesOnlyFromEncryptDictionary(encDict);
switch (revision) {
case 2:
cryptoMode = EncryptionConstants.STANDARD_ENCRYPTION_40;
break;
case 3:
PdfNumber lengthValue = encDict.getAsNumber(PdfName.Length);
if (lengthValue == null)
throw new PdfException(KernelExceptionMessageConstant.ILLEGAL_LENGTH_VALUE);
length = lengthValue.intValue();
if (length > 128 || length < 40 || length % 8 != 0)
throw new PdfException(KernelExceptionMessageConstant.ILLEGAL_LENGTH_VALUE);
cryptoMode = EncryptionConstants.STANDARD_ENCRYPTION_128;
break;
Here simply replace
if (lengthValue == null)
throw new PdfException(KernelExceptionMessageConstant.ILLEGAL_LENGTH_VALUE);
length = lengthValue.intValue();
by
length = 40;
if (lengthValue != null)
length = lengthValue.intValue();
(I used a replacement with an identical number of lines to make interpreting later stack traces easier.)