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

使用PDFBox(Java)添加PDF按钮部件/标注无显示,求解决

PDFBox添加按钮标注不显示的问题修复

你的代码核心问题在于只设置了按钮按下状态的外观(DownAppearance),但没有设置默认的正常状态外观(NormalAppearance),PDF阅读器默认加载正常状态的外观,所以按钮不会显示。另外createAppearanceStream方法需要具体实现来绘制按钮的视觉样式。

以下是修正后的完整代码:

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDRectangle;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationWidget;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import java.io.IOException;
import java.util.List;

public class AddButtonToPDF {
    public static void main(String[] args) throws IOException {
        // 创建PDF文档对象
        PDDocument document = new PDDocument();
        System.out.println("PDF created");
        
        PDPage page = new PDPage(PDRectangle.A4);
        document.addPage(page);

        PDAnnotationWidget buttonWidget = new PDAnnotationWidget();
        // 设置按钮位置和尺寸(PDF坐标:左下角为原点,x向右,y向上)
        PDRectangle buttonRect = new PDRectangle(100, 100, 100, 30);
        buttonWidget.setRectangle(buttonRect);
        buttonWidget.setPage(page);
        buttonWidget.setPrinted(true);

        // 设置按钮点击事件
        PDActionJavaScript javascript = new PDActionJavaScript("app.alert('Button Clicked!');");
        buttonWidget.setAction(javascript);

        // 创建外观字典,同时设置正常状态和按下状态的外观
        PDAppearanceDictionary appearanceDict = new PDAppearanceDictionary();
        // 正常状态外观
        PDAppearanceStream normalAppearance = createAppearanceStream(document, buttonRect, "Click Me");
        appearanceDict.setNormalAppearance(normalAppearance);
        // 按下状态外观(可以和正常状态区分,比如换颜色)
        PDAppearanceStream downAppearance = createAppearanceStream(document, buttonRect, "Clicked!", true);
        appearanceDict.setDownAppearance(downAppearance);
        
        buttonWidget.setAppearance(appearanceDict);

        // 将按钮添加到页面标注列表
        List<PDAnnotation> annotations = page.getAnnotations();
        annotations.add(buttonWidget);
        page.setAnnotations(annotations);

        document.save("C:\\Musaub\\Hp_Notebook\\Freelancing\\Jobs\\3d_Point_Cloud_Floorplan\\E57_Files\\Bundle_4\\Panoramas\\PDF 23-282_out.pdf");
        document.close();
    }

    // 实现外观流绘制:绘制按钮边框、背景和文字
    private static PDAppearanceStream createAppearanceStream(PDDocument document, PDRectangle rect, String text) throws IOException {
        return createAppearanceStream(document, rect, text, false);
    }

    private static PDAppearanceStream createAppearanceStream(PDDocument document, PDRectangle rect, String text, boolean isDown) throws IOException {
        PDAppearanceStream appearanceStream = new PDAppearanceStream(document);
        appearanceStream.setBBox(rect); // 外观流的边界必须和按钮矩形一致

        try (PDPageContentStream contentStream = new PDPageContentStream(document, appearanceStream)) {
            // 绘制按钮背景
            contentStream.setNonStrokingColor(isDown ? 0.7f : 0.9f); // 按下时颜色稍深
            contentStream.addRect(rect.getLowerLeftX(), rect.getLowerLeftY(), rect.getWidth(), rect.getHeight());
            contentStream.fill();

            // 绘制按钮边框
            contentStream.setStrokingColor(0.2f);
            contentStream.addRect(rect.getLowerLeftX(), rect.getLowerLeftY(), rect.getWidth(), rect.getHeight());
            contentStream.stroke();

            // 绘制按钮文字
            contentStream.beginText();
            contentStream.setFont(org.apache.pdfbox.pdmodel.font.PDType1Font.HELVETICA_BOLD, 12);
            contentStream.setNonStrokingColor(0f);
            // 计算文字居中位置
            float textWidth = org.apache.pdfbox.pdmodel.font.PDType1Font.HELVETICA_BOLD.getStringWidth(text) / 1000 * 12;
            float textX = rect.getLowerLeftX() + (rect.getWidth() - textWidth) / 2;
            float textY = rect.getLowerLeftY() + (rect.getHeight() - 12) / 2 + 4; // 微调垂直居中
            contentStream.newLineAtOffset(textX, textY);
            contentStream.showText(text);
            contentStream.endText();
        }

        return appearanceStream;
    }
}

关键修改点说明:

  • 添加NormalAppearance:必须为按钮设置默认的正常状态外观,这是PDF阅读器显示按钮的基础。
  • 完善createAppearanceStream实现:该方法负责绘制按钮的视觉样式,包括背景、边框和文字,同时确保外观流的BBox与按钮矩形匹配,否则绘制内容可能被裁剪。
  • 区分按下状态外观:可选地为按下状态设置不同的样式,提升交互反馈。

内容的提问来源于stack exchange,提问作者Musaub Shaikh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 01:37:21