在 iText7 上发布合并 PDF

Issue merging PDFs on iText7

我有以下合并 PDF 文档的方法。但是,在某些情况下文档有水印时,该方法会抛出错误。

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");
    }
}

文档有水印时抛出以下错误...

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[=11=](PdfUtilFuntions.java:180)
at java.util.ArrayList.forEach(ArrayList.java:1257)
at gov.ca.lc.util.PdfUtilFuntions.doMergeUsingItext7(PdfUtilFuntions.java:176)

我不确定文档中到底有什么内容无法合并。非常感谢解决此问题的任何帮助。谢谢

这是 iText 中的错误。

相关 PDF 已加密。它的加密字典的 V 值为 1(“RC4 或 AES 算法,加密密钥长度为 40 位”)和 R 值为3(“对于使用 V 值 2 或 3 加密的文件,或者任何“修订版 3 或更高版本的安全处理程序”访问权限设置为 0 的文件)。

A Length 密钥长度的值仅针对 V 2 或 3 指定,对于 V 1 密钥长度固定为 40。尽管如此,iText 在 R 3 的情况下需要 Length 值。

由于文档的加密字典中没有 Length 值,iText 无法读取该文件。


附带说明:如今 40 位密钥长度的安全性已不安全。所以很可能这个问题没有更早出现,因为使用 V 1 安全性(或一般的 40 位密钥长度安全性)是无用的,因此没有认真对待用例。


快速修复

如果您不回避重新编译 iText,修复错误很容易。只需编辑 iText 内核 class com.itextpdf.kernel.pdf.PdfEncryption。它的方法readAndSetCryptoModeForStdHandler

开头
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;

这里简单替换

if (lengthValue == null)
    throw new PdfException(KernelExceptionMessageConstant.ILLEGAL_LENGTH_VALUE);
length = lengthValue.intValue();

来自

length = 40;
if (lengthValue != null)
    length = lengthValue.intValue();

(我使用了相同行数的替换,以便更轻松地解释以后的堆栈跟踪。)