【问题标题】:In PDFBox, how to change the origin (0,0) point of a PDRectangle object?在 PDFBox 中,如何更改 PDRectangle 对象的原点 (0,0)?
【发布时间】:2015-03-21 12:37:27
【问题描述】:

情况:
在 PDFBox 中,PDRectangle 对象的默认原点 (0,0) 似乎是页面的左下角。

例如,以下代码在页面的左下角为您提供一个正方形,每边长 100 个单位。

PDRectangle rectangle = new PDRectangle(0, 0, 100, 100);

问题:
是否可以将原点更改为左上角,例如,上面的代码会在页面的左上角为您提供相同的正方形?

我问的原因:
我使用 PDFTextStripper 来获取文本的坐标(通过使用提取的 TextPosition 对象的 getX() 和 getY() 方法)。从 TextPosition 对象检索的坐标似乎在左上角有一个原点 (0,0)。我希望我的 PDRectangle 对象的坐标与我的 TextPosition 对象的坐标具有相同的原点。

我试图通过“页面高度减去 Y 坐标”来调整我的 PDRectangle 的 Y 坐标。这给了我想要的结果,但它并不优雅。我想要一个优雅的解决方案。

注意: 有人问过类似的问题。答案是我尝试过的,这不是最优雅的。 how to change the coordiantes of a text in a pdf page from lower left to upper left

【问题讨论】:

    标签: java pdf pdfbox


    【解决方案1】:

    您可以稍微更改坐标系,但最终很可能事情不会变得更优雅。

    开始...

    首先让我们澄清一些误解:

    你假设

    在 PDFBox 中,PDRectangle 对象的默认原点 (0,0) 似乎是页面的左下角。

    并非所有情况都如此,只是经常如此。

    包含显示页面区域(在纸上或屏幕上)的区域通常由相关页面的 CropBox 条目定义:

    CropBox 矩形 (可选;可继承) 以默认用户空间单位表示的矩形,它应定义默认用户空间的可见区域。 当页面显示或打印时,其内容将被剪裁(裁剪)到这个矩形,然后以某种实现定义的方式叠加到输出介质上。

    ... 正 x 轴水平向右延伸,正 y 轴垂直向上延伸,就像在标准数学实践中一样(受页面字典中 Rotate 条目的更改)。

    ... 在 PostScript 中,默认用户空间的原点始终对应于输出介质的左下角。虽然这种约定在 PDF 文档中也很常见,但不是必需的;页面字典的 CropBox 条目可以指定默认用户空间的任何矩形在媒体上可见。

    因此,原点 (0,0) 可以在任何地方,它可能在左下角、左上角、页面中间甚至在显示的页面区域之外。

    通过 Rotate 条目,甚至可以旋转该区域(90°、180° 或 270°) .

    将原点(您似乎已经观察到)放在左下方只是按照惯例。

    此外,您似乎认为坐标系是恒定的。情况也并非如此,您可以通过一些操作彻底改变用户空间坐标系,您可以平移、旋转、镜像、倾斜和/或缩放它!

    因此,即使一开始坐标系是通常的坐标系,原点在左下角,x 轴向右,y 轴向上,它可能会以某种方式在页面内容描述中更改为奇怪的东西。在页面中心右侧绘制矩形new PDRectangle(0, 0, 100, 100) 可能会产生一些菱形。

    你能做什么...

    如您所见,PDF 用户空间中的坐标是一个非常动态的问题。你可以做些什么来控制这种情况,取决于你使用矩形的上下文。

    不幸的是,您对自己所做的事情的描述非常含糊。因此,这也会有些模糊。

    页面内容中的坐标

    如果你想在现有页面上绘制一些矩形,你首先需要一个页面内容流来写入,即PDPageContentStream实例,并且它应该以保证原始用户空间坐标的方式准备系统没有受到干扰。您可以通过使用带有三个布尔参数的构造函数将它们全部设置为true 来获得这样的实例:

    PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true, true);
    

    然后您可以对坐标系应用变换。您希望左上角为原点,y 值向下增加。如果页面的裁剪框告诉你左上角有坐标(xtl,ytl),那么,你申请

    contentStream.concatenate2CTM(new AffineTransform(1, 0, 0, -1, xtl, ytl));
    

    从这里你有一个你想要的坐标系,原点左上角和 y 坐标镜像。

    但请注意一件事:如果您也打算绘制文本,那么不仅文本插入点 y 坐标会被镜像,而且文本本身也会被镜像,除非您通过添加一个镜像文本矩阵来抵消它!因此,如果您想添加很多文本,这可能不会像您想要的那样优雅。

    注释坐标

    如果您不想在内容流中使用矩形而是添加注释,则不受上述转换的约束,但也不能使用它。

    因此,在这种情况下,您必须按原样获取裁剪框并相应地变换您的矩形。

    为什么PDFBox文本提取坐标是原样

    本质上是为了将文本行以正确的顺序放在一起并正确排序,您不希望出现这种奇怪的情况,而是需要一个简单的稳定坐标系。一些 PDFBox 开发人员为此选择了左上原点、y 递增向下的变体,因此 TextPosition 坐标已被标准化为该方案。

    在我看来,更好的选择是使用默认的用户空间坐标,以便更轻松地重用坐标。因此,您可能想尝试使用textPosition.getTextMatrix().getTranslateX()textPosition.getTextMatrix().getTranslateY() 来获得TextPosition textPosition

    【讨论】:

    • 感谢您的详细回复。是的,我确实在尝试添加链接注释,所以 concatenate2CTM 方法不适用。当您说“您必须按原样采用裁剪框并相应地转换您的矩形”时,这是什么意思?这是否意味着我需要转换裁剪框矩形,或者我需要转换我要绘制的矩形?
    • 这是什么意思? - 你画的矩形。如果您更改了裁剪框,您将移动所有页面内容。或者你以不同的方式检索你的坐标,c.f.答案的最后一段。
    • 感谢您的详细回复。
    • 我认为您的回答中有错字。你写了You want the top left to be the origin and the x-value increasing downwards。我认为您的意思是 y-value 向下增加,而不是 x-value
    • xtlytl 来自哪里?页面的cropBox是PDRectangle,它没有给出左上角x(xtl)和左上角y(ytl)
    【解决方案2】:

    以下似乎是“调整”TextPosition 坐标的最佳方式:

    x_adjusted =  x_original + page.findCropBox().getLowerLeftX();
    y_adjusted = -y_original + page.findCropBox().getUpperRightY();
    

    其中pagePDPage 对象所在的TextPosition

    【讨论】:

    • 如果页面没有Rotation,这看起来是正确的。
    • 这很好用,除非 X 和 Y 坐标都为 0。对于那个视图,它不绘制任何东西
    【解决方案3】:

    接受的答案给我带来了一些问题。此外,对我来说,被镜像和调整的文本似乎不是正确的解决方案。所以这就是我想出的,到目前为止,它运行得非常顺利。

    解决方案(示例如下):

    • x=0y=0 位于左上角的纸上绘图时,使用原始点调用getAdjustedPoints(...) 方法。
    • 此方法将返回可用于绘制矩形的浮点数组(长度为 4)
    • 数组顺序为x、y、宽、高。只需传递 addRect(...) 方法

    private float[] getAdjustedPoints(PDPage page, float x, float y, float width, float height) {
        float resizedWidth = getSizeFromInches(width);
        float resizedHeight = getSizeFromInches(height);
        return new float[] {
                getAdjustedX(page, getSizeFromInches(x)),
                getAdjustedY(page, getSizeFromInches(y)) - resizedHeight,
                resizedWidth, resizedHeight
        };
    }
    
    private float getSizeFromInches(float inches) {
        // 72 is POINTS_PER_INCH - it's defined in the PDRectangle class
        return inches * 72f;
    }
    
    private float getAdjustedX(PDPage page, float x) {
        return x + page.getCropBox().getLowerLeftX();
    }
    
    private float getAdjustedY(PDPage page, float y) {
        return -y + page.getCropBox().getUpperRightY();
    }
    

    示例:

    private PDPage drawPage1(PDDocument document) {
        PDPage page = new PDPage(PDRectangle.LETTER);
    
        try {
            // Gray Color Box
            PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, false, false);
            contentStream.setNonStrokingColor(Color.decode(MyColors.Gallery));
            float [] p1 = getAdjustedPoints(page, 0f, 0f, 8.5f, 1f);
            contentStream.addRect(p1[0], p1[1], p1[2], p1[3]);
            contentStream.fill();
    
            // Disco Color Box
            contentStream.setNonStrokingColor(Color.decode(MyColors.Disco));
            p1 = getAdjustedPoints(page, 4.5f, 1f, 4, 0.25f);
            contentStream.addRect(p1[0], p1[1], p1[2], p1[3]);
            contentStream.fill();
    
            contentStream.close();
        } catch (Exception e) { }
    
        return page;
    }
    

    如您所见,我绘制了 2 个矩形框。
    为了绘制这个,我使用了以下坐标,假设 x=0y=0 在左上角。

    灰色彩盒:x=0y=0w=8.5h=1
    迪斯科彩盒:x=4.5y=1w=4, h=0.25

    这是我的结果图片。

    【讨论】:

    • “文本被镜像和调整对我来说似乎不是正确的解决方案” - 这就是为什么我也不喜欢镜像坐标系.但是,由于 OP 认为像您一样转换坐标是不优雅,而是想要改变坐标系,我确实向他展示了如何镜像坐标系。
    【解决方案4】:

    添加 PDF 的高度(最简单的解决方案)

    【讨论】:

    • 不。考虑 OP 的示例矩形,通过添加页面的高度,您将其移出页面,而他希望它位于页面内的左上角。
    • 如果您添加页面高度,它不会超出 PDF,它只会变成倒置的 :)
    • “它只是倒转了” - 这是不正确的。我假设通过 将页面高度添加到 OP 的 new PDRectangle(0, 0, 100, 100) 您的意思是 将高度添加到基点,即 new PDRectangle(0, pageHeight, 100, 100)。这个矩形显然在页面之外并且没有倒置。如果你的意思不同,你的回答显然缺乏细节。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-19
    • 1970-01-01
    • 2016-02-21
    相关资源
    最近更新 更多