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

使用iText为横版PDF添加批注:坐标错位问题求助

解决横版显示但MediaBox竖版+旋转270度的PDF批注错位问题

首先可以明确:这个PDF本身没有创建问题——PDF规范允许通过Rotate属性指定页面显示时的旋转角度,MediaBox定义的是页面原始尺寸,两者结合实现横版显示是完全合规的。你遇到的批注错位,本质是坐标坐标系不匹配的问题,完全可以用iText解决。

核心思路:坐标转换

你的批注坐标是基于显示时的横版坐标系(1224x729),但PDF内部的批注坐标是基于MediaBox的原始竖版坐标系(729x1224),再加上270度的旋转,导致两者错位。解决的关键是把显示坐标系的坐标转换为MediaBox坐标系下的正确坐标。

iText具体实现步骤

  1. 获取页面关键参数:先拿到页面的旋转角度、MediaBox原始尺寸,以及显示时的页面尺寸:

    PdfDocument pdfDoc = new PdfDocument(new PdfReader("input.pdf"), new PdfWriter("output.pdf"));
    PdfPage page = pdfDoc.getPage(1); // 假设处理第一页
    int rotation = page.getRotation(); // 这里会得到270
    Rectangle mediaBox = page.getMediaBox(); // 729x1224
    Rectangle displaySize = page.getPageSizeWithRotation(); // 1224x729,即显示时的尺寸
    
  2. 针对270度旋转做坐标转换
    当页面旋转270度时,显示坐标系和MediaBox坐标系的映射关系如下:

    • 显示坐标系的x轴(向右)对应MediaBox坐标系的y轴(向上)
    • 显示坐标系的y轴(向上)对应MediaBox坐标系的x轴(向左),需要反向计算
    • 批注的宽高也会因为旋转互换

    假设你原本的批注矩形是基于显示坐标系的Rectangle originalRect = new Rectangle(x, y, w, h);,转换代码如下:

    // 转换坐标到MediaBox坐标系
    float transformedX = originalRect.getY();
    float transformedY = mediaBox.getWidth() - originalRect.getX() - originalRect.getWidth();
    // 旋转后宽高互换
    float transformedW = originalRect.getHeight();
    float transformedH = originalRect.getWidth();
    
    Rectangle transformedRect = new Rectangle(transformedX, transformedY, transformedW, transformedH);
    
  3. 添加转换后的批注
    用转换后的矩形创建并添加批注即可,比如添加文本批注:

    PdfTextAnnotation annotation = new PdfTextAnnotation(transformedRect)
            .setTitle(new PdfString("批注"))
            .setContents("这是转换后的正确批注");
    page.addAnnotation(annotation);
    
    pdfDoc.close();
    

通用适配(支持所有旋转角度)

如果你的代码需要处理任意旋转角度(90/180/270度),可以用iText的Matrix类来做通用变换,避免硬编码逻辑:

// 创建旋转对应的变换矩阵,然后取逆矩阵(因为要从显示坐标转回原始坐标)
Matrix rotationMatrix = new Matrix().rotate(rotation);
Matrix inverseMatrix = rotationMatrix.createInverse();

// 将原始批注矩形的四个点做逆变换
float[] rectPoints = {
        originalRect.getX(), originalRect.getY(),
        originalRect.getX() + originalRect.getWidth(), originalRect.getY(),
        originalRect.getX(), originalRect.getY() + originalRect.getHeight(),
        originalRect.getX() + originalRect.getWidth(), originalRect.getY() + originalRect.getHeight()
};
inverseMatrix.applyTo(rectPoints);

// 计算变换后的矩形边界
float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE;
float maxX = Float.MIN_VALUE, maxY = Float.MIN_VALUE;
for (int i = 0; i < rectPoints.length; i += 2) {
    minX = Math.min(minX, rectPoints[i]);
    minY = Math.min(minY, rectPoints[i+1]);
    maxX = Math.max(maxX, rectPoints[i]);
    maxY = Math.max(maxY, rectPoints[i+1]);
}

Rectangle transformedRect = new Rectangle(minX, minY, maxX - minX, maxY - minY);

这样不管页面是哪种旋转角度,都能正确转换坐标,避免批注错位。

内容的提问来源于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:26:58