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

Itext与Pdfbox旋转设置兼容问题及签名图片显示异常求助

问题解决方案

一、PDF宽高识别与旋转问题

iText生成PDF时设置的90度旋转,在PDFBox中会被解析为页面旋转属性,但PDFBox默认获取的MediaBox是未旋转的原始尺寸,导致宽高互换。解决方式如下:

1. 获取实际显示的页面尺寸

如果只是需要正确获取显示的宽高,无需修改PDF,可根据页面旋转角度调整:

PDPage pdPage = ...; // 你的页面对象
PDRectangle mediaBox = pdPage.getMediaBox();
int rotation = pdPage.getRotation();
float actualWidth, actualHeight;

if (rotation == 90 || rotation == 270) {
    actualWidth = mediaBox.getHeight();
    actualHeight = mediaBox.getWidth();
} else {
    actualWidth = mediaBox.getWidth();
    actualHeight = mediaBox.getHeight();
}

2. 统一PDF页面旋转状态

如果需要将PDF的旋转状态重置为0度(竖版),同时修正MediaBox尺寸:

PDPage pdPage = ...;
int rotation = pdPage.getRotation();
if (rotation != 0) {
    PDRectangle mediaBox = pdPage.getMediaBox();
    pdPage.setRotation(0);
    // 旋转90/270度时,交换宽高
    if (rotation == 90 || rotation == 270) {
        pdPage.setMediaBox(new PDRectangle(mediaBox.getHeight(), mediaBox.getWidth()));
    }
}

二、签名域图片不显示问题

你的代码中存在坐标变换和外观流创建的问题,以下是修正后的代码及说明:

问题分析

  1. 旋转矩阵应用后,图片绘制位置超出了外观区域,导致不可见
  2. 外观流的创建方式不规范,未正确关联资源和边界
  3. 图片绘制的宽高未匹配旋转后的坐标系

修正后的代码

void addImageOnlySignatureField(PDDocument pdDocument, PDPage pdPage, PDRectangle rectangle, PDSignature signature) throws IOException {
    PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
    if (acroForm == null) {
        acroForm = new PDAcroForm(pdDocument);
        pdDocument.getDocumentCatalog().setAcroForm(acroForm);
    }

    // 创建签名字段
    PDSignatureField signatureField = new PDSignatureField(acroForm);
    signatureField.setValue(signature);
    PDAnnotationWidget widget = signatureField.getWidgets().get(0);
    acroForm.getFields().add(signatureField);

    // 设置widget属性
    widget.setRectangle(rectangle);
    widget.setPage(pdPage);

    // 创建外观流
    PDAppearanceDictionary appearance = new PDAppearanceDictionary();
    appearance.getCOSObject().setDirect(true);
    PDAppearanceStream appearanceStream = new PDAppearanceStream(pdDocument);
    
    // 设置外观流的资源和边界
    PDResources res = new PDResources();
    appearanceStream.setResources(res);
    PDRectangle bbox = new PDRectangle(0, 0, rectangle.getWidth(), rectangle.getHeight());
    appearanceStream.setBBox(bbox);
    
    appearance.setNormalAppearance(appearanceStream);
    widget.setAppearance(appearance);

    try (PDPageContentStream cs = new PDPageContentStream(pdDocument, appearanceStream);
         InputStream imageResource = getClass().getResourceAsStream("/mkl/testarea/pdfbox2/content/Willi-1.jpg")) {
        cs.saveGraphicsState();
        
        // 处理旋转:先平移避免图片转出区域,再旋转90度
        Matrix m = new Matrix();
        // 旋转90度后,需要将图片平移至(0, -rectangle.getHeight()),确保在BBox内
        m.translate(0, -rectangle.getHeight());
        m.rotate(Math.toRadians(90));
        cs.transform(m);
        
        PDImageXObject pdImage = PDImageXObject.createFromByteArray(pdDocument, ByteStreams.toByteArray(imageResource), "Willi");
        // 旋转后图片的宽高对应原rectangle的高和宽
        cs.drawImage(pdImage, 0, 0, rectangle.getHeight(), rectangle.getWidth());
        
        cs.restoreGraphicsState();
    }

    pdPage.getAnnotations().add(widget);
    
    // 标记需要更新的对象
    acroForm.setNeedToBeUpdated(true);
    COSDictionary pageTreeObject = pdPage.getCOSObject(); 
    while (pageTreeObject != null) {
        pageTreeObject.setNeedToBeUpdated(true);
        pageTreeObject = (COSDictionary) pageTreeObject.getDictionaryObject(COSName.PARENT);
    }
}

关键修改点

  1. 增加了AcroForm的空判断,避免空指针
  2. 规范了外观流的创建流程,正确设置资源和BBox
  3. 调整旋转矩阵的顺序:先平移再旋转,确保图片在外观区域内
  4. 旋转后图片的绘制宽高改为rectangle.getHeight()和rectangle.getWidth(),匹配旋转后的坐标系
  5. 添加acroForm.setNeedToBeUpdated(true)确保表单字段被正确更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:47:39