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())); } }
二、签名域图片不显示问题
你的代码中存在坐标变换和外观流创建的问题,以下是修正后的代码及说明:
问题分析
- 旋转矩阵应用后,图片绘制位置超出了外观区域,导致不可见
- 外观流的创建方式不规范,未正确关联资源和边界
- 图片绘制的宽高未匹配旋转后的坐标系
修正后的代码
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); } }
关键修改点
- 增加了AcroForm的空判断,避免空指针
- 规范了外观流的创建流程,正确设置资源和BBox
- 调整旋转矩阵的顺序:先平移再旋转,确保图片在外观区域内
- 旋转后图片的绘制宽高改为
rectangle.getHeight()和rectangle.getWidth(),匹配旋转后的坐标系 - 添加
acroForm.setNeedToBeUpdated(true)确保表单字段被正确更新
内容的提问来源于stack exchange,提问作者Ghost
相关产品推荐
相关产品推荐

