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

使用iText在横向PDF中生成自由文本注释外观流遇问题求助

解决横向PDF(270度旋转)中注释文本垂直显示的问题

这个问题我之前也碰到过,核心原因是PDF注释的外观坐标系和页面旋转是脱节的——页面旋转是页面级的视觉变换,但注释的PdfAppearance默认是基于未旋转的原始页面坐标系绘制的,所以文本方向会和页面旋转不匹配,而矩形能正常显示是因为注释的bbox已经适配了旋转后的页面布局。

下面给你几个可行的解决思路,结合代码示例来说:


思路1:手动适配页面旋转的变换矩阵(最可靠)

我们需要根据页面的旋转角度,给文本绘制的坐标系应用对应的旋转变换,关键是要找对旋转的原点和变换顺序(变换矩阵的顺序很容易搞反)。

步骤:

  1. 先获取当前页面的旋转角度
  2. 在绘制文本前保存状态,应用变换矩阵
  3. 调整文本的起始位置,适配变换后的坐标系
  4. 绘制完成后恢复状态

代码修改示例:

// 先获取页面旋转角度(假设你能拿到当前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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:33:51