Java 在PDFBox中,如何更改PDRectangle对象的原点(0,0)?

Java 在PDFBox中,如何更改PDRectangle对象的原点(0,0)?,java,pdf,pdfbox,Java,Pdf,Pdfbox,情况: 在PDFBox中,PDRectangle对象的默认原点(0,0)似乎是页面的左下角 例如,下面的代码在页面的左下角提供了一个正方形,每边有100个单位长 PDRectangle rectangle = new PDRectangle(0, 0, 100, 100); 问题是: 是否可以将原点更改为左上角,例如,上面的代码将在页面的左上角为您提供相同的正方形 我问的原因是: 我使用PDFTextStripper获取文本的坐标(通过使用提取的TextPosition对象的getX()和ge

情况:
在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坐标。这给了我想要的结果,但它并不优雅。我想要一个优雅的解决方案

注: 有人提出了类似的问题。答案是我尝试过的,这不是最优雅的。

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

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

你认为

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

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

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

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

。。。正x轴水平向右延伸,正y轴垂直向上延伸,如标准数学实践中所示(可通过页面字典中的旋转条目进行更改)

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

因此,<强>原点(0,0)可以是任何地方< /强>,它可以在左下角,在左上角,在页面中间,甚至在显示页面区域之外。 通过旋转条目,该区域甚至可以旋转(90°、180°或270°)

将原点(正如您所观察到的)放在左下角仅仅是按照惯例

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

因此,即使在开始时坐标系是通常的坐标系,原点在左下角,x轴向右,y轴向上,它也可能在页面内容描述中以某种方式被更改为奇怪的东西。绘制矩形
新的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
坐标已标准化为该方案

在我看来,更好的选择是使用默认的用户空间坐标,以便于重用
x_adjusted =  x_original + page.findCropBox().getLowerLeftX();
y_adjusted = -y_original + page.findCropBox().getUpperRightY();
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;
}