You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PDF嵌入JavaScript验证失败求助:依赖与检测代码指导

问题:iText添加PDF JavaScript后无法检测到

我使用iText库给PDF文件添加JavaScript,但自己编写的检测代码无法识别已添加的JS。以下是相关代码和需求:

添加JavaScript的代码

@PostMapping("/addJSInPdf")
public ResponseEntity<String> addJSInPdf(@RequestParam("file") MultipartFile file) {
    try {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(file.getBytes());
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PdfReader reader = new PdfReader(inputStream);
        Document document = new Document(reader.getPageSize(1));
        PdfWriter writer = PdfWriter.getInstance(document, outputStream);

        document.open();
        PdfContentByte contentByte = writer.getDirectContent();
        PdfImportedPage importedPage;
        int totalPages = reader.getNumberOfPages();
        for (int i = 1; i <= totalPages; i++) {
            importedPage = writer.getImportedPage(reader, i);
            contentByte.addTemplate(importedPage, 0, 0);
        }
        writer.addJavaScript("app.alert('This is an alert box in the PDF!');");
        document.close();

        String base64EncodedPdf = Base64.getEncoder().encodeToString(outputStream.toByteArray());
        return ResponseEntity.ok("{\"pdf\":\"" + base64EncodedPdf + "\"}");
    } catch (Exception e) {
        e.printStackTrace();
        return ResponseEntity.status(500).body("{\"error\":\"Internal Server Error\"}");
    }
}

原检测JavaScript的代码

public boolean validatePDF(byte[] fileContent) {
    try {
        PdfReader reader = new PdfReader(new ByteArrayInputStream(fileContent));
        if (containsJavaScript(reader)) {
            return true;
        }
        return false;
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}

private boolean containsJavaScript(PdfReader reader) {
    try {
        PdfDictionary catalog = reader.getCatalog();

        // 1. Check OpenAction for JavaScript
        PdfDictionary openAction = catalog.getAsDict(PdfName.OPENACTION);
        if (openAction != null) {
            PdfObject jsObject = openAction.get(PdfName.JS);
            if (jsObject instanceof PdfString) {
                System.out.println("JavaScript found in OpenAction: " + ((PdfString) jsObject).toUnicodeString());
                return true;
            }
        }

        // 2. Check additional actions (AA) for JavaScript
        PdfDictionary aa = catalog.getAsDict(PdfName.AA);
        if (aa != null) {
            for (PdfName actionType : aa.getKeys()) {
                PdfDictionary action = aa.getAsDict(actionType);
                PdfObject jsObject = action.get(PdfName.JS);
                if (jsObject instanceof PdfString) {
                    System.out.println(
                            "JavaScript found in Additional Actions: " + ((PdfString) jsObject).toUnicodeString());
                    return true;
                }
            }
        }

        // 3. Traverse through annotations and actions in all pages
        for (int i = 1; i <= reader.getNumberOfPages(); i++) {
            PdfDictionary page = reader.getPageN(i);
            PdfArray annots = page.getAsArray(PdfName.ANNOTS);
            if (annots != null) {
                for (PdfObject annotObj : annots) {
                    PdfDictionary annot = (PdfDictionary) PdfReader.getPdfObject(annotObj);
                    PdfObject jsObject = annot.get(PdfName.JS);
                    if (jsObject instanceof PdfString) {
                        System.out.println(
                                "JavaScript found in Annotation: " + ((PdfString) jsObject).toUnicodeString());
                        return true;
                    }
                }
            }
        }

        // 4. Check the Names dictionary for JavaScript
        PdfDictionary names = catalog.getAsDict(PdfName.NAMES);
        if (names != null) {
            PdfDictionary embeddedJS = names.getAsDict(PdfName.JAVASCRIPT);
            if (embeddedJS != null) {
                PdfArray jsArray = embeddedJS.getAsArray(PdfName.NAMES);
                if (jsArray != null) {
                    for (int i = 1; i < jsArray.size(); i += 2) {
                        PdfObject jsObject = jsArray.getDirectObject(i);
                        if (jsObject instanceof PdfString) {
                            System.out.println(
                                    "JavaScript found in Names: " + ((PdfString) jsObject).toUnicodeString());
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

需求

  1. 提供验证PDF中JavaScript所需的正确依赖;
  2. 给出能正确检测PDF中嵌入JavaScript的示例代码。

解决方案

1. 正确的依赖

你的代码基于iText 5(开源版本),对应Maven依赖如下:

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13.3</version>
</dependency>

Gradle依赖:

implementation 'com.itextpdf:itextpdf:5.5.13.3'

2. 修正后的JavaScript检测代码

原检测代码失败的核心原因是:writer.addJavaScript()添加的JS被iText存储为PdfStream(而非PdfString),原代码仅检查PdfString导致遗漏。以下是覆盖所有常见JS场景的检测代码:

public boolean validatePDF(byte[] fileContent) {
    try {
        PdfReader reader = new PdfReader(new ByteArrayInputStream(fileContent));
        return containsJavaScript(reader);
    } catch (IOException e) {
        e.printStackTrace();
        return false;
    }
}

private boolean containsJavaScript(PdfReader reader) {
    try {
        PdfDictionary catalog = reader.getCatalog();

        // 1. 检测文档级JavaScript(writer.addJavaScript()添加的内容在此)
        PdfDictionary names = catalog.getAsDict(PdfName.NAMES);
        if (names != null) {
            PdfDictionary jsNamesDict = names.getAsDict(PdfName.JAVASCRIPT);
            if (jsNamesDict != null) {
                PdfArray jsArray = jsNamesDict.getAsArray(PdfName.NAMES);
                if (jsArray != null) {
                    for (int i = 1; i < jsArray.size(); i += 2) {
                        PdfObject jsObj = jsArray.getDirectObject(i);
                        String jsContent = extractJsContent(jsObj);
                        if (jsContent != null && !jsContent.trim().isEmpty()) {
                            System.out.println("找到文档级JavaScript: " + jsContent);
                            return true;
                        }
                    }
                }
            }
        }

        // 2. 检测OpenAction中的JavaScript
        PdfObject openActionObj = catalog.get(PdfName.OPENACTION);
        if (openActionObj != null) {
            if (openActionObj instanceof PdfDictionary) {
                PdfDictionary action = (PdfDictionary) openActionObj;
                if (isJavaScriptAction(action)) {
                    String jsContent = extractJsContent(action.get(PdfName.JS));
                    if (jsContent != null) {
                        System.out.println("找到OpenAction中的JavaScript: " + jsContent);
                        return true;
                    }
                }
            } else if (openActionObj instanceof PdfArray) {
                PdfArray actionArray = (PdfArray) openActionObj;
                for (PdfObject obj : actionArray) {
                    PdfDictionary action = (PdfDictionary) PdfReader.getPdfObject(obj);
                    if (action != null && isJavaScriptAction(action)) {
                        String jsContent = extractJsContent(action.get(PdfName.JS));
                        if (jsContent != null) {
                            System.out.println("找到OpenAction序列中的JavaScript: " + jsContent);
                            return true;
                        }
                    }
                }
            }
        }

        // 3. 检测页面注释中的JavaScript
        for (int pageNum = 1; pageNum <= reader.getNumberOfPages(); pageNum++) {
            PdfDictionary page = reader.getPageN(pageNum);
            PdfArray annots = page.getAsArray(PdfName.ANNOTS);
            if (annots != null) {
                for (PdfObject annotObj : annots) {
                    PdfDictionary annot = (PdfDictionary) PdfReader.getPdfObject(annotObj);
                    if (annot == null) continue;

                    // 检测注释的触发动作
                    PdfDictionary action = annot.getAsDict(PdfName.A);
                    if (action != null && isJavaScriptAction(action)) {
                        String jsContent = extractJsContent(action.get(PdfName.JS));
                        if (jsContent != null) {
                            System.out.println("找到注释动作中的JavaScript: " + jsContent);
                            return true;
                        }
                    }

                    // 检测注释的额外动作(AA)
                    PdfDictionary annotAA = annot.getAsDict(PdfName.AA);
                    if (annotAA != null) {
                        for (PdfName key : annotAA.getKeys()) {
                            PdfDictionary aaAction = annotAA.getAsDict(key);
                            if (aaAction != null && isJavaScriptAction(aaAction)) {
                                String jsContent = extractJsContent(aaAction.get(PdfName.JS));
                                if (jsContent != null) {
                                    System.out.println("找到注释额外动作中的JavaScript: " + jsContent);
                                    return true;
                                }
                            }
                        }
                    }
                }
            }
        }

        // 4. 检测文档额外动作(AA)中的JavaScript
        PdfDictionary docAA = catalog.getAsDict(PdfName.AA);
        if (docAA != null) {
            for (PdfName key : docAA.getKeys()) {
                PdfDictionary action = docAA.getAsDict(key);
                if (action != null && isJavaScriptAction(action)) {
                    String jsContent = extractJsContent(action.get(PdfName.JS));
                    if (jsContent != null) {
                        System.out.println("找到文档额外动作中的JavaScript: " + jsContent);
                        return true;
                    }
                }
            }
        }

        return false;
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}

// 判断是否为JavaScript动作
private boolean isJavaScriptAction(PdfDictionary action) {
    return action != null && PdfName.JAVASCRIPT.equals(action.getAsName(PdfName.S));
}

// 从PdfObject提取JS内容,支持PdfString和PdfStream
private String extractJsContent(PdfObject obj) throws IOException {
    if (obj == null) return null;
    if (obj instanceof PdfString) {
        return ((PdfString) obj).toUnicodeString();
    } else if (obj instanceof PdfStream) {
        return new String(PdfReader.getStreamBytes((PdfStream) obj), StandardCharsets.UTF_8);
    }
    return null;
}

关键改进点

  • 新增extractJsContent方法,同时支持提取PdfString和PdfStream类型的JS内容;
  • 完善了OpenAction的检测,支持单个动作和动作序列两种情况;
  • 覆盖了文档级JS、OpenAction、注释动作、文档/注释额外动作等所有常见JS嵌入场景;
  • 新增isJavaScriptAction方法统一判断动作类型,代码更简洁。

内容的提问来源于stack exchange,提问作者Ravi Dobariya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 00:00:00