Java 获取PDF中的确切字符串位置

Java 获取PDF中的确切字符串位置,java,pdf,Java,Pdf,我试图读取一条流,希望得到每个字符串的确切位置(坐标) 但当然没有任何立场 那么,如何才能获得每个文本(字符串、字符等)的确切位置呢 如果您试图进行文本提取,您应该意识到这绝对是一个不平凡的过程。您至少必须实现一台RPN机器来运行代码、累积转换并执行所有文本运算符。您将需要从当前页面资源集中解释字体度量,并且可能需要了解文本编码 当我在Acrobat1.0上工作时,我负责“查找…”命令,该命令将您的问题作为一个子集包含在内。有了更丰富的工具和更多的专业知识,花了几个月的时间才把它做好。如果你想做

我试图读取一条流,希望得到每个字符串的确切位置(坐标)

但当然没有任何立场


那么,如何才能获得每个文本(字符串、字符等)的确切位置呢

如果您试图进行文本提取,您应该意识到这绝对是一个不平凡的过程。您至少必须实现一台RPN机器来运行代码、累积转换并执行所有文本运算符。您将需要从当前页面资源集中解释字体度量,并且可能需要了解文本编码


当我在Acrobat1.0上工作时,我负责“查找…”命令,该命令将您的问题作为一个子集包含在内。有了更丰富的工具和更多的专业知识,花了几个月的时间才把它做好。

如果你想做文本提取,你应该意识到这绝对是一个不平凡的过程。您至少必须实现一台RPN机器来运行代码、累积转换并执行所有文本运算符。您将需要从当前页面资源集中解释字体度量,并且可能需要了解文本编码


当我在Acrobat1.0上工作时,我负责“查找…”命令,该命令将您的问题作为一个子集包含在内。有了更丰富的工具和更多的专业知识,我们花了几个月的时间才把它做好。

如果您想了解Tj操作符的字节数,请查看PDF规范:

更具体地说,请参见第9.4.3节。为了解释该部分,必须在用于绘制文本的字体中查找每个字节或可能的多个字节序列(在您的示例中,字体标识为/F1)。通过查找,您将找到此代码所指的实际字符

同时请记住,您在此处看到这些文本命令的顺序可能根本不反映自然的阅读顺序,因此您必须根据您找到的位置来确定这些字符的正确顺序

另外请记住,您的PDF文件可能不包含空格,例如。由于只需将下一个字符向右移动一点就可以“伪造”空格,因此一些PDF生成器会忽略空格。但在坐标中找到一个缺口可能不是一个断语。例如,它也可以是列的结尾

这是非常非常困难的-尤其是如果您试图在通用PDF文件上执行此操作(而不是仅针对少数您知道总是来自同一来源的布局)。很久以前,我为一款名为PitStop Pro的产品编写了一个PDF文本编辑器,该产品仍然存在(不再与之关联),这是一个非常困难的问题


如果这是一个选项,请尝试使用现有库或工具。对于这样的库或工具,肯定有商业选择;我对开源/免费库不太熟悉,因此无法对此发表评论。

如果您想了解Tj操作符的字节数,请查看PDF规范:

更具体地说,请参见第9.4.3节。为了解释该部分,必须在用于绘制文本的字体中查找每个字节或可能的多个字节序列(在您的示例中,字体标识为/F1)。通过查找,您将找到此代码所指的实际字符

同时请记住,您在此处看到这些文本命令的顺序可能根本不反映自然的阅读顺序,因此您必须根据您找到的位置来确定这些字符的正确顺序

另外请记住,您的PDF文件可能不包含空格,例如。由于只需将下一个字符向右移动一点就可以“伪造”空格,因此一些PDF生成器会忽略空格。但在坐标中找到一个缺口可能不是一个断语。例如,它也可以是列的结尾

这是非常非常困难的-尤其是如果您试图在通用PDF文件上执行此操作(而不是仅针对少数您知道总是来自同一来源的布局)。很久以前,我为一款名为PitStop Pro的产品编写了一个PDF文本编辑器,该产品仍然存在(不再与之关联),这是一个非常困难的问题


如果这是一个选项,请尝试使用现有库或工具。对于这样的库或工具,肯定有商业选择;我对开源/免费图书馆不太熟悉,所以我不能对此发表评论。

正如普林斯和大卫·范·德里斯基在他们的回答中所指出的那样,从PDF文件中提取文本是非常重要的。幸运的是,iText解析器包中的类为您完成了大部分繁重的工作。您已经从该包中找到了至少一个类,
pdftextractor,
,但如果您只对页面的纯文本感兴趣,则该类本质上是一个方便实用程序,可用于使用iText的解析器功能。在您的例子中,您必须更仔细地查看该包中的类

使用iText获取文本提取主题信息的起点是第15.3节解析PDF,特别是示例的方法
extractText

它利用了
RenderListener
实现:

因此,如果您的
RenderListener
除了使用
getText()
检查文本外,还考虑
getBaseline()
甚至
getAscentLine()
getDescentLine()。
您拥有可能需要的所有坐标

PS:
ParsingHelloWorld.extractText()
中有一个代码包装类,它允许您在给定的
PdfReader读取器、
int页面、和
RenderListener RenderListener:

PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(page, renderListener);

正如普林斯和大卫·范·德里希在他们的回答中指出的那样,文本
......
BT                  
70.9 800.9 Td /F1 14 Tf <01> Tj 
10.1 0 Td <02> Tj               
9.3 0 Td <03> Tj
3.9 0 Td <01> Tj
10.1 0 Td <0405> Tj
18.7 0 Td <060607> Tj
21 0 Td <08090A07> Tj
24.9 0 Td <05> Tj
10.1 0 Td <0B0C0D> Tj
28.8 0 Td <0E> Tj
3.8 0 Td <0F> Tj
8.6 0 Td <090B1007> Tj
29.5 0 Td <0B11> Tj
16.4 0 Td <12> Tj
7.8 0 Td <1307> Tj
12.4 0 Td <14> Tj
7.8 0 Td <07> Tj
3.9 0 Td <15> Tj
7.8 0 Td <16> Tj
7.8 0 Td <07> Tj
3.9 0 Td <17> Tj
10.8 0 Td <0D> Tj
7.8 0 Td <18> Tj
10.9 0 Td <19> Tj
ET
.....
PdfReader reader = new PdfReader(new FileInputStream("/home/....xxx.pdf"));
PdfTextExtractor extract = new PdfTextExtractor(reader);
public void extractText(String src, String dest) throws IOException
{
    PrintWriter out = new PrintWriter(new FileOutputStream(dest));
    PdfReader reader = new PdfReader(src);
    RenderListener listener = new MyTextRenderListener(out);
    PdfContentStreamProcessor processor = new PdfContentStreamProcessor(listener);
    PdfDictionary pageDic = reader.getPageN(1);
    PdfDictionary resourcesDic = pageDic.getAsDict(PdfName.RESOURCES);
    processor.processContent(ContentByteUtils.getContentBytesForPage(reader, 1), resourcesDic);
    out.flush();
    out.close();
}
public class MyTextRenderListener implements RenderListener
{
    [...]

    /**
     * @see RenderListener#renderText(TextRenderInfo)
     */
    public void renderText(TextRenderInfo renderInfo) {
        out.print("<");
        out.print(renderInfo.getText());
        out.print(">");
    }
}
public LineSegment getBaseline();    // the baseline for the text (i.e. the line that the text 'sits' on)
public LineSegment getAscentLine();  // the ascentline for the text (i.e. the line that represents the topmost extent that a string of the current font could have)
public LineSegment getDescentLine(); // the descentline for the text (i.e. the line that represents the bottom most extent that a string of the current font could have)
public float getRise()             ; // the rise which  represents how far above the nominal baseline the text should be rendered

public String getText();             // the text to render
public int getTextRenderMode();      // the text render mode
public DocumentFont getFont();       // the font
public float getSingleSpaceWidth();  // the width, in user space units, of a single space character in the current font

public List<TextRenderInfo> getCharacterRenderInfos(); // details useful if a listener needs access to the position of each individual glyph in the text render operation
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(page, renderListener);