背景
我一直在开发一个程序,它可以获取一个pdf,突出显示一些单词(通过pdfbox标记注释)并保存新的pdf。
为此,我扩展了PDFTextStripper类,以覆盖writeString()方法并获取每个单词(框)的TextPositions,这样我就可以准确地知道文本在PDF文档中的坐标位置(TextPosition对象为我提供每个单词框的坐标)。然后,在此基础上,我画了一个矩形,突出显示我想要的单词。
问题
它适用于我迄今为止尝试过的所有文档,但有一个文档除外,我从文本帖子中获得的位置似乎是错误的,导致了错误的突出显示。
这是原始文档:
https://pdfhost.io/v/b1Mcpoy~s_Thomson.pdf
这是在writeString()提供给我的第一个单词框中有一个突出显示的文档,带有setSortByPosition(false),即MicroRNA:
https://pdfhost.io/v/V6INb4Xet_Thomson.pdf
它应该高亮显示MicroRNA,但它高亮显示了其上方的一个空白区域(粉色HL矩形)。
这是在writeString()提供给我的第一个字框中突出显示的文档,带有setSortByPosition(true),它是原始的:
https://pdfhost.io/v/Lndh.j6ji_Thomson.pdf
它应该突出显示原始文档,但它突出显示了PDF文档开头的一个空格(粉色HL矩形)。
我想,这个PDF可能包含一些PDFBox很难找到正确位置的东西,或者这可能是PDFBox中的一种缺陷。
技术规格:
PDFBox 2.0.17
Java 11.0.6 10,采用OpenJDK
MacOS Catalina 10.15.4,16gb,x86_64
坐标值
例如,对于MicroRNA单词框的开头和结尾,writeString()给出的TextPosition坐标是:
M字母
endX = 59.533783
endY = 682.696
maxHeight = 13.688589
rotation = 0
x = 35.886597
y = 99.26935
pageHeight = 781.96533
pageWidth = 586.97034
widthOfSpace = 11.9551
font = PDType1CFont JCFHGD+AdvT108
fontSize = 1.0
unicode = M
direction = -1.0
一封信
endX = 146.34933
endY = 682.696
maxHeight = 13.688589
rotation = 0
x = 129.18181
y = 99.26935
pageHeight = 781.96533
pageWidth = 586.97034
widthOfSpace = 11.9551
font = PDType1CFont JCFHGD+AdvT108
fontSize = 1.0
fontSizePt = 23
unicode = A
direction = -1.0
这会导致我在上面分享的错误HL注释,而对于所有其他PDF文档,这是非常精确的,我已经测试了许多不同的注释。我在这里一无所知,而且我不是PDF定位方面的专家。我尝试过使用PDFbox调试器工具,但无法正确阅读。我们将非常感谢您的帮助。如果我能提供更多证据,请告诉我。谢谢
编辑
请注意,文本提取工作正常。
我的代码
首先,我创建一个数组的坐标与几个值从TextPotion对象的第一个和最后一个字符我想HL:
private void extractHLCoordinates(TextPosition firstPosition, TextPosition lastPosition, int pageNumber) {
double firstPositionX = firstPosition.getX();
double firstPositionY = firstPosition.getY();
double lastPositionEndX = lastPosition.getEndX();
double lastPositionY = lastPosition.getY();
double height = firstPosition.getHeight();
double width = firstPosition.getWidth();
int rotation = firstPosition.getRotation();
double[] wordCoordinates = {firstPositionX, firstPositionY, lastPositionEndX, lastPositionY, pageNumber,
height, width, rotation};
...
}
现在是基于提取的坐标绘制时间:
for (int pageIndex = 0; pageIndex < pdDocument.getNumberOfPages(); pageIndex++) {
DPage page = pdDocument.getPage(pageIndex);
List<PDAnnotation> annotations = page.getAnnotations();
int rotation;
double pageHeight = page.getMediaBox().getHeight();
double pageWidth = page.getMediaBox().getWidth();
// each CoordinatePoint obj holds the double array with the
// coordinates of each word I want to HL - see the previous method
for (CoordinatePoint coordinate : coordinates) {
double[] wordCoordinates = coordinate.getCoordinates();
int pageNumber = (int) wordCoordinates[4];
// if the current coordinates are not related to the current page,
//ignore them
if ((int) pageNumber == (pageIndex + 1)) {
// getting rotation of the page: portrait, landscape...
rotation = (int) wordCoordinates[7];
firstPositionX = wordCoordinates[0];
firstPositionY = wordCoordinates[1];
lastPositionEndX = wordCoordinates[2];
lastPositionY = wordCoordinates[3];
height = wordCoordinates[5];
double height;
double minX;
double maxX;
double minY;
double maxY;
if (rotation == 90) {
double width = wordCoordinates[6];
width = (pageHeight * width) / pageWidth;
//defining coordinates of a rectangle
maxX = firstPositionY;
minX = firstPositionY - height;
minY = firstPositionX;
maxY = firstPositionX + width;
} else {
minX = firstPositionX;
maxX = lastPositionEndX;
minY = pageHeight - firstPositionY;
maxY = pageHeight - lastPositionY + height;
}
// Finally I draw the Rectangle
PDAnnotationTextMarkup txtMark = new PDAnnotationTextMarkup(PDAnnotationTextMarkup.SUB_TYPE_HIGHLIGHT);
PDRectangle pdRectangle = new PDRectangle();
pdRectangle.setLowerLeftX((float) minX);
pdRectangle.setLowerLeftY((float) minY);
pdRectangle.setUpperRightX((float) maxX);
pdRectangle.setUpperRightY((float) ((float) maxY + height));
txtMark.setRectangle(pdRectangle);
// And the QuadPoints
float[] quads = new float[8];
quads[0] = pdRectangle.getLowerLeftX(); // x1
quads[1] = pdRectangle.getUpperRightY() - 2; // y1
quads[2] = pdRectangle.getUpperRightX(); // x2
quads[3] = quads[1]; // y2
quads[4] = quads[0]; // x3
quads[5] = pdRectangle.getLowerLeftY() - 2; // y3
quads[6] = quads[2]; // x4
quads[7] = quads[5]; // y5
txtMark.setQuadPoints(quads);
...
}
}
您的四点坐标是相对于CropBox计算的,但它们需要相对于MediaBox。对于本文档,CropBox比MediaBox小,因此突出显示的位置不正确。用CropBox调整x。LLX-MediaBox。带MediaBox的LLY和y。URY-CropBox。而亮点将位于正确的位置
上述调整适用于旋转=0的页面。如果旋转!=0则可能需要进一步调整,具体取决于PDFBox返回坐标的方式(我对PDFBox API不太熟悉)。
操作编辑
在这里发布我对代码所做的更改,以便帮助他人。请注意,我还没有尝试旋转=90的任何方法。一旦我有了这篇文章,我会在这里更新。
之前
...
if (rotation == 90) {
double width = wordCoordinates[6];
width = (pageHeight * width) / pageWidth;
//defining coordinates of a rectangle
maxX = firstPositionY;
minX = firstPositionY - height;
minY = firstPositionX;
maxY = firstPositionX + width;
} else {
minX = firstPositionX;
maxX = lastPositionEndX;
minY = pageHeight - firstPositionY;
maxY = pageHeight - lastPositionY + height;
}
...
之后
...
PDRectangle mediaBox = page.getMediaBox();
PDRectangle cropBox = page.getCropBox();
if (rotation == 90) {
double width = wordCoordinates[6];
width = (pageHeight * width) / pageWidth;
//defining coordinates of a rectangle
maxX = firstPositionY;
minX = firstPositionY - height;
minY = firstPositionX;
maxY = firstPositionX + width;
} else {
minX = firstPositionX + cropBox.getLowerLeftX() - mediaBox.getLowerLeftY();
maxX = lastPositionEndX + cropBox.getLowerLeftX() - mediaBox.getLowerLeftY();
minY = pageHeight - firstPositionY - (mediaBox.getUpperRightY() - cropBox.getUpperRightY());
maxY = pageHeight - lastPositionY + height - (mediaBox.getUpperRightY() - cropBox.getUpperRightY());
}
...
我正在尝试读取和处理Java中的.doc、.docx、.pdf文件,方法是使用Apache POI(用于doc、docx)和Apache PDFBox(用于pdf)库将它们转换为单个字符串。 在遇到文本框之前,它工作得很好。如果格式是这样的: 第1段 文本框1 第2段 文本框2 第3段 那么输出应该是: 第1段文本框1第2段文本框2第3段 但我得到的输出是: 第1段文本框1文本框2 似乎是在结尾处
问题内容: 如何使用PDFBox从PDF文档中读取特定页面(具有页码)? 问题答案: 这应该工作: 如本教程的“ 书签”部分中所示 更新2015年,版本2.0.0快照 似乎已将其删除并放回(?)。 getPage 在2.0.0 javadoc中。要使用它: 该 getAllPages 方法已更名 GETPAGES
主要内容:将JavaScript添加到PDF文档,示例在前一章中,我们学习了如何将图像插入到PDF文档中。 在本章中,将学习如何将JavaScript添加到PDF文档。 将JavaScript添加到PDF文档 可以使用类将JavaScript操作添加到PDF文档。 它代表了JavaScript操作。 以下是将JavaScript操作添加到现有PDF文档的步骤。 第1步:加载现有的PDF文档 使用类的静态方法加载现有的PDF文档。 此方法接受一个文件对
主要内容:分割PDF文档中的页面,示例在前一章中,我们已经看到了如何将JavaScript添加到PDF文档。 现在来学习如何将给定的PDF文档分成多个文档。 分割PDF文档中的页面 可以使用类将给定的PDF文档分割为多个PDF文档。 该类用于将给定的PDF文档分成几个其他文档。 以下是拆分现有PDF文档的步骤 第1步:加载现有的PDF文档 使用类的静态方法加载现有的PDF文档。 此方法接受一个文件对象作为参数,因为这是一个静态方法,可
主要内容:加密PDF文档,示例在前一章中,我们已经看到了如何在PDF文档中插入图像。 在本章中,我们将学习如何加密PDF文档。 加密PDF文档 使用和类提供的方法加密PDF文档。 类用于通过为其分配访问权限来保护PDF文档。 使用此教程,您可以限制用户执行以下操作。 打印文档 修改文档的内容 复制或提取文档的内容 添加或修改注释 填写交互式表单域 提取文字和图形以便视障人士使用 汇编文件 打印质量下降 类用于向文档添加基于密码
主要内容:创建一个空的PDF文档,实例现在让我们了解如何使用PDFBox库创建PDF文档。 创建一个空的PDF文档 可以通过实例化类来创建一个空的PDF文档。使用这个类的方法将文档保存在所需的位置。 以下是创建一个空的PDF文档的步骤。 第1步: 创建空白文档 包中的类是PDF文档的内存中表示形式。 因此,通过实例化这个类,可以创建一个空的,如下面的代码块所示。 第2步: 保存文档 创建文档后,需要将此文档保存在所需的路径中,可以使用