使用iText在横向PDF中生成自由文本注释外观流遇问题求助
解决横向PDF(270度旋转)中注释文本垂直显示的问题
这个问题我之前也碰到过,核心原因是PDF注释的外观坐标系和页面旋转是脱节的——页面旋转是页面级的视觉变换,但注释的PdfAppearance默认是基于未旋转的原始页面坐标系绘制的,所以文本方向会和页面旋转不匹配,而矩形能正常显示是因为注释的bbox已经适配了旋转后的页面布局。
下面给你几个可行的解决思路,结合代码示例来说:
思路1:手动适配页面旋转的变换矩阵(最可靠)
我们需要根据页面的旋转角度,给文本绘制的坐标系应用对应的旋转变换,关键是要找对旋转的原点和变换顺序(变换矩阵的顺序很容易搞反)。
步骤:
- 先获取当前页面的旋转角度
- 在绘制文本前保存状态,应用变换矩阵
- 调整文本的起始位置,适配变换后的坐标系
- 绘制完成后恢复状态
代码修改示例:
// 先获取页面旋转角度(假设你能拿到当前PdfPage对象) int pageRotation = pdfPage.getRotation(); float fontSize = pdfJSAnnotation.getFontSize(); // 绘制注释背景(这部分你原来的代码没问题,保留) cs.rectangle(bbox.getLeft(), bbox.getBottom(), bbox.getWidth(), bbox.getHeight()); cs.fill(); // 准备文本内容 String[] text = new String[1]; text[0] = "BAC"; cs.setFontAndSize(BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED), fontSize); cs.beginText(); cs.setLeading(fontSize + 1.75f); // 保存当前状态,避免影响后续绘制 cs.saveState(); if (pageRotation == 270) { // 270度旋转的变换矩阵:先平移到注释bbox的右上角,再旋转270度 // 变换矩阵参数:a, b, c, d, e, f 对应 [a b; c d] 旋转缩放 + [e f] 平移 cs.concatCTM(0, 1, -1, 0, bbox.getRight(), bbox.getBottom()); // 变换后,文本的起始位置要调整为适配新坐标系 cs.moveText(0, fontSize - 0.75f); } else { // 正常纵向页面,保持原来的起始位置 cs.moveText(0, bbox.getHeight() - fontSize + 0.75f); } // 绘制文本(你的原有逻辑保留) for (String s : text) { if (s.equals("\n")) cs.newlineText(); else cs.showText(s); } // 恢复状态 cs.restoreState(); cs.endText();
为什么之前的旋转没生效?
你之前用transform()或者直接旋转没成功,大概率是因为旋转原点不对——默认是页面左上角(原始坐标系原点),而我们需要绕注释自身的bbox来旋转,所以用concatCTM组合平移和旋转,把坐标系先移到注释的右上角,再旋转,这样文本就会和页面旋转方向一致。
思路2:让注释跟随页面旋转(简单但兼容性稍差)
有些PDF支持给注释设置旋转属性,让注释整体跟随页面旋转,代码很简单:
// 假设pdfAnnotation是你的PdfAnnotation对象 pdfAnnotation.setRotation(pageRotation);
但要注意:不是所有PDF阅读器都支持这个属性,比如一些老版本的阅读器可能会忽略这个设置,所以如果要兼容所有场景,还是思路1更可靠。
思路3:适配所有旋转角度的通用处理
如果你的代码需要支持90、180、270度所有旋转场景,可以扩展判断逻辑,比如:
switch (pageRotation) { case 90: cs.concatCTM(0, -1, 1, 0, bbox.getLeft(), bbox.getTop()); cs.moveText(0, -bbox.getWidth() + fontSize - 0.75f); break; case 180: cs.concatCTM(-1, 0, 0, -1, bbox.getRight(), bbox.getTop()); cs.moveText(0, -bbox.getHeight() + fontSize - 0.75f); break; case 270: cs.concatCTM(0, 1, -1, 0, bbox.getRight(), bbox.getBottom()); cs.moveText(0, fontSize - 0.75f); break; default: // 0度正常情况 cs.moveText(0, bbox.getHeight() - fontSize + 0.75f); break; }
这样就能覆盖所有页面旋转的情况了。
核心要点总结:
- 注释外观的坐标系是原始页面坐标系,和页面旋转无关,必须手动适配
- 变换矩阵的顺序很关键:先平移到旋转原点,再做旋转变换
- 一定要用
saveState()和restoreState()隔离变换,避免影响其他绘制操作
内容的提问来源于stack exchange,提问作者chitgoks
相关产品推荐
相关产品推荐

