情况:
在PDFBox中,PDRectangle对象'默认原点(0,0)似乎是页面的左下角。
例如,以下代码在页面的左下角为您提供了一个正方形,每边的长度为100个单位。
PDRectangle rectangle = new PDRectangle(0, 0, 100, 100);
问题:
是否可以将原点更改为UPPER-LEFT转角,例如,上面的代码会在页面的左上角为您提供相同的方块?
我问的原因是:
我使用PDFTextStripper来获取文本的坐标(通过使用提取的TextPosition对象的getX()和getY()方法)。从TextPosition对象检索的坐标似乎在UPPER-LEFT CORNER处具有原点(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
答案 0 :(得分:13)
你可以稍微改变坐标系,但最有可能的事情最终不会变得更优雅。
首先让我们澄清一些误解:
你假设
在PDFBox中,PDRectangle对象的默认原点(0,0)似乎是页面的左下角。
对于所有情况都不是这样,只是经常发生。
包含显示的页面区域(在纸上或屏幕上)的区域通常由相关页面的 CropBox 条目定义:
CropBox 矩形(可选;可继承)以默认用户空间单位表示的矩形,用于定义默认用户空间的可见区域。 当显示或打印页面时,其内容应被剪裁(裁剪)到该矩形,然后以某种实现定义的方式施加在输出介质上。
...正x轴水平向右延伸,正y轴垂直向上延伸,如标准数学实践(可通过页面词典中的旋转条目进行更改)。 / p>
...在PostScript中,默认用户空间的来源始终对应于输出媒体的左下角。虽然这种约定在PDF文档中也很常见,但并不是必需的。页面词典的 CropBox 条目可以指定要在媒体上显示的任何默认用户空间矩形。
因此, 原点(0,0)可以在任何地方 ,它可能位于页面中间的左下角,左上角甚至远在显示的页面区域之外。
通过旋转条目, 该区域甚至可以旋转 (90°,180°或270°)
将原点(如您所观察到的)置于左下方仅按惯例完成。
此外,您似乎认为坐标系是不变的。事实并非如此,您可以通过这些操作大幅度转换用户空间坐标系,您可以平移,旋转,镜像,倾斜和/或缩放它!
因此,即使在开始时坐标系是通常的坐标系,原点在左下方,x轴向右,y轴向上,也可能在某种程度上改变为页面内容描述中的奇怪之处。绘制矩形new PDRectangle(0, 0, 100, 100)
可能会在页面中心右侧产生一些菱形形状。
如您所见,PDF用户空间中的坐标是一个非常动态的问题。你可以做些什么来驯服情况,取决于你使用矩形的上下文。
不幸的是,你在描述你所做的事情时非常模糊。因此,这也有些模糊。
如果要在现有页面上绘制一些矩形,首先需要一个要写入的页面内容流,即PDPageContentStream
实例,并且应该以保证原始用户的方式准备它空间坐标系没有受到干扰。通过使用带有三个布尔参数的构造函数将所有它们设置为true
来获得这样的实例:
PDPageContentStream contentStream = new PDPageContentStream(doc, page, true, true, true);
然后,您可以将变换应用于坐标系。您希望左上角是原点,x值向下增加。如果页面的裁剪框告诉你左上角有坐标(xtl,ytl),那么你应用
contentStream.concatenate2CTM(new AffineTransform(1, 0, 0, -1, xtl, ytl));
从这里开始,你有一个你想要的坐标系,原点左上角和y坐标镜像。
但要注意一件事:如果你要绘制文本,不仅文本插入点y坐标是镜像的,而且文本本身除非你通过添加一个镜像文本矩阵来抵消它!因此,如果您想添加更多文字,这可能不如您想要的那么优雅。
如果您不想在内容流中使用矩形,而是要添加注释,则不会受到上述转换的影响,但您也无法使用它。
因此,在这种情况下,您必须按原样拍摄裁剪框并相应地变换矩形。
基本上,为了将文本行以正确的顺序放在一起并正确排序,你不需要这么奇怪的情况,而是一个简单的稳定坐标系。一些PDFBox开发人员为此选择了左上角,y增加向下的变体,因此TextPosition
坐标已经标准化为该方案。
在我看来,更好的选择是使用默认的用户空间坐标,以便更容易地重复使用坐标。因此,您可能希望尝试使用textPosition.getTextMatrix().getTranslateX()
,textPosition.getTextMatrix().getTranslateY()
来TextPosition textPosition
答案 1 :(得分:4)
以下似乎是“调整”TextPosition坐标的最佳方法:
x_adjusted = x_original + page.findCropBox().getLowerLeftX();
y_adjusted = -y_original + page.findCropBox().getUpperRightY();
其中page
是PDPage
对象所在的TextPosition
答案 2 :(得分:1)
被接受的答案给我带来了一些问题。此外,对我来说,镜像文本并为此进行调整似乎不是正确的解决方案。所以这是我想出的,到目前为止,它的工作非常顺利。
getAdjustedPoints(...)
方法,其中x=0
和y=0
在左上角。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=0
和y=0
位于左上方。
灰色颜色框::x=0
,y=0
,w=8.5
,h=1
Disco Color Box::x=4.5
y=1
,w=4
,h=0.25
答案 3 :(得分:0)
添加PDF的高度(最简单的解决方案)