首页 > 解决方案 > 如何检索 PdfStampAnnotation 旋转

问题描述

参考 如何检索 PdfStampAnnotation 的图像,如何检索图章注释的旋转?

我创建一个文档,贴上邮票 1,翻页并添加另一个邮票 2。文档没问题(stamp1 图像已旋转,而 stamp2 未旋转),但提取的图像完全相同(方向相同)。

我可以使用

page.getRotation()  // 90

如果我想获得邮票(图像)方向

// I expect to get 0 but get 90
stamp1.getPdfObject().getAsDictionary(PdfName.P).get(PdfName.Rotate)

// I get 90
stamp2.getPdfObject().getAsDictionary(PdfName.P).get(PdfName.Rotate)

我努力了好几个小时,试图为邮票找到正确的轮换……

编辑:

pdf 是使用 pdf 专家创建的。创建一个空白 pdf,添加图章注释(图像),旋转页面并添加另一个注释。

我像这样更新了这段代码来测试行为:

public void testExtractFromAddStamp() throws IOException {
    try (InputStream resource = new FileInputStream("/tmp/test.pdf");
         PdfReader pdfReader = new PdfReader(resource);
         PdfDocument pdfDocument = new PdfDocument(pdfReader)    ) {
         saveAnnotationImages(pdfDocument, new File(RESULT_FOLDER, "add_stamp").getPath());

         // TEST    
         PdfAnnotation stamp1 = pdfDocument.getPage(1).getAnnotations().get(0);
         PdfAnnotation stamp2 = pdfDocument.getPage(1).getAnnotations().get(1);

         PdfNumber rotation1 = (PdfNumber) stamp1.getPdfObject().getAsDictionary(PdfName.P).get(PdfName.Rotate);
         PdfNumber rotation2 = (PdfNumber) stamp2.getPdfObject().getAsDictionary(PdfName.P).get(PdfName.Rotate);

         System.out.println(rotation1); // Shows 90
         System.out.println(rotation2); // Shows 90
    }
}

标签: javaitextitext7

解决方案


一般来说

PDF 对象的许多属性决定了注释中图像的最终旋转。您必须将所有这些都考虑在内。

页面旋转

首先,可以旋转显示注解的页面:

Rotate integer (可选;可继承)页面在显示或打印时应顺时针旋转的度数。该值应为 90 的倍数。默认值:0

(ISO 32000-2 表 31 - 页面对象中的条目)

注释是否与页面一起旋转,由注释的NoRotate标志决定:

5 NoRotate (PDF 1.3)如果设置,则不要旋转注释的外观以匹配页面的旋转。注释矩形的左上角应保持在页面上的固定位置,与页面旋转无关。

(ISO 32000-2 表 167 - 注释标志)

在此处输入图像描述

(ISO 32000-2 图 78 - 使用 NoRotate 标志进行坐标调整)

因此,只有在注释的NoRotate标志是明确的情况下才需要考虑页面旋转。

外观矩阵

如果一个注解有一个外观流——本例中的注解就是这样做的,因为这是位图图像绘制指令所在的位置——这个外观流包含一个变换矩阵属性,该属性控制外观流在注解矩形中的旋转方式:

AP 字典 (可选;PDF 1.2)外观字典,指定注释应如何在页面上以可视方式呈现(参见 12.5.5,“外观流”)。

(ISO 32000-2 表 166 - 所有注释词典通用的条目)

从 PDF 1.2 开始,注释可以指定一个或多个外观流,以替代早期版本中可用的简单边框和颜色特征。外观流使注释能够以不同的方式直观地呈现,以反映其与用户的交互。每个外观流都是一个表单 XObject(参见 8.10,“表单 XObjects”):一个应在注释矩形内呈现的自包含内容流。

本小节中概述的算法应用于从外观 XObject 的坐标系(由其Matrix条目定义;参见“表 95 - 参考字典中的条目”)映射到默认用户空间中的注释矩形:

算法:外观流

  1. 外观的边界框(由其BBox条目指定)应使用Matrix进行转换,以生成具有任意方向的四边形。转换后的外观框是包含此四边形的最小直立矩形。

  2. 应计算一个矩阵A来缩放和平移转换后的外观框以与注释矩形的边缘(由Rect条目指定)对齐。A将变换后的外观框的左下角( xy坐标最小的角)和右上角(xy坐标最大的角)映射到注释矩形的相应角。

  3. 矩阵应与A连接以形成矩阵AA,该矩阵从外观坐标系映射到默认用户空间中的注释矩形:

    AA = 矩阵 × A

(ISO 32000-2 第 12.5.5 节 - 外观流)

因此,必须检查这个外观矩阵的旋转因子。如果矩阵也倾斜或镜像,您首先必须决定如何分解矩阵,因为旋转角度值取决于该分解。

当前变换矩阵

当通过内容流中的绘图指令绘制位图图像时,不一定在外观边界框中竖直绘制,而是按照绘制时间当前变换矩阵的值来绘制。

就像外观矩阵一样,这个变换矩阵可能必须被分解以确定它应用的旋转角度。

在你的情况下

让我们看一下您的示例 PDF。

页面旋转

页面旋转

页面旋转显式为 0。

外观矩阵

在此处输入图像描述在此处输入图像描述

您的两个注释外观流都没有Matrix条目。因此,默认情况下使用单位矩阵,这意味着没有旋转。

当前变换矩阵

在此处输入图像描述在此处输入图像描述在此处输入图像描述在此处输入图像描述

注释的外观流构建非常简单,在保存图形状态和恢复图形状态指令对之间改变当前变换矩阵并绘制图像。

四个注释的变换矩阵包含(1)不旋转,(2)逆时针旋转90°,(3)旋转180°,以及(4)逆时针旋转270°。

这对应于您页面上可见的 4 个注释。


推荐阅读