【问题标题】:Get the exact Stringposition in PDF获取 PDF 中的确切字符串位置
【发布时间】:2012-11-29 18:44:02
【问题描述】:

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

    int size = reader.getXrefSize();

    for (int i = 0; i < size; ++i)
    {
        PdfObject pdfObject = reader.getPdfObject(i);
        if ((pdfObject == null) || !pdfObject.isStream())
            continue;

        PdfStream stream = (PdfStream) pdfObject;
        PdfObject obj = stream.get(PdfName.FILTER);

        if ((obj != null) && obj.toString().equals(PdfName.FLATEDECODE.toString()))
        {
            byte[] codedText = PdfReader.getStreamBytesRaw((PRStream) stream);
            byte[] text = PdfReader.FlateDecode(codedText);
            FileOutputStream o = new FileOutputStream(new File("/home..../Text" + i + ".txt"));
            o.write(text);
            o.flush();
            o.close();
        }

    }

我居然得到了这样的职位

......
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
.....

但我不知道哪个字符串适合哪个位置 另一方面,在 Itext 中,我可以使用

获取纯文本
PdfReader reader = new PdfReader(new FileInputStream("/home/....xxx.pdf"));
PdfTextExtractor extract = new PdfTextExtractor(reader);

但当然完全没有任何职位......

那么我怎样才能得到每个 text(string,char,...) 的确切位置呢?

【问题讨论】:

  • 我知道例如第 1 行中的 是 Ascii Hex,意思是 START OF HEADING .... 但它们都不是指 PDF 中的实际文本
  • 将解析器包的类与自定义 RenderListener iText 结合使用,您可以提取具有位置等的文本。明天我会写一个完整的答案。

标签: java pdf


【解决方案1】:

正如 plinth 和 David van Driessche 在他们的回答中已经指出的那样,从 PDF 文件中提取文本并非易事。幸运的是,iText 解析器包中的类为您完成了大部分繁重的工作。你已经从那个包中找到了至少一个类,PdfTextExtractor,但是如果你只对页面的纯文本感兴趣,那么这个类本质上是一个使用 iText 解析器功能的便利实用程序。在您的情况下,您必须更深入地查看该包中的类。

iText in Action — 2nd Edition 的第 15.3 节解析 PDF 中获取有关使用 iText 提取文本的信息的起点,尤其是示例ParsingHelloWorld.java 的方法extractText

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();
}

利用RenderListenerimplementation MyTextRenderListener.java:

public class MyTextRenderListener implements RenderListener
{
    [...]

    /**
     * @see RenderListener#renderText(TextRenderInfo)
     */
    public void renderText(TextRenderInfo renderInfo) {
        out.print("<");
        out.print(renderInfo.getText());
        out.print(">");
    }
}

虽然 thisRenderListenerimplementation 仅输出文本,但它检查的 TextRenderInfo 对象提供了更多信息:

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

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

PS:ParsingHelloWorld.extractText()PdfReaderContentParser中的代码有一个包装类,它允许您在给定aPdfReader reader,anint page,和a@987654341的情况下简单地编写以下代码@

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      如果您想了解 Tj 运算符所看到的字节,请查看 PDF 规范: http://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/PDF32000_2008.pdf

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

      另外请记住,您在此处看到这些文本命令的顺序可能根本不反映自然阅读顺序,因此您必须根据您找到的位置找出这些字符的实际正确顺序是什么.

      另外请记住,您的 PDF 文件可能不包含例如空格。由于可以通过简单地将下一个字符向右移动一点来“伪造”空格,因此某些 PDF 生成器会省略空格。但是在坐标中找到一个间隙可能不是一个单词中断。例如,它也可以是列的结尾。

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

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

      【讨论】:

      • Thx... 我实际上是在阅读 ISO_3200 - 9.6.2.2 Standard Type 1 中说字体的编码列在附件 D 中...我查了 10.1 0 Td Tj 9.3 0 Td Tj 3.9 0 Td Tj.... 所以 表示 ^B , 表示 ^C , 表示 ^A.... 如果我查看每个十六进制。在示例中,它们都不是字母......也许我误解了某事。 ...你能告诉我你的意思吗?通过查找,你会发现这段代码所指的实际字符......
      • 您犯的错误是您正在查看标准编码 - 您的字体很可能没有使用标准编码。您需要获取您拥有的那些字符,并将它们通过 PDF 文件中的字体编码(在您的示例中标记为 F1)。根据字体的类型,您需要查看第 9.6.6 节(简单字体)或第 9.7 节(复合字体)以了解如何将这些代码映射到可以理解的内容...
      • 知道了....谢谢...答案非常接近...我正在检查各种 adobe 文档。 ...假设为我的目的获取正确的 CMap 表....但最终我意识到,在使用 CMap 表上方的代码进行扫描时,已经在生成的文件之一中...无论如何,我将使用 Itext 将重播标记为答案,但你们俩都将被授予答案标签...感谢您的帮助....
      • @Fendrix 实际上,您字体中的字符 ID 似乎是按照您的内容中首次出现的顺序发出的:它们按 01、02、03、01、04、05、06 的顺序出现, 06, 07, 08, 09, 0A, 07, 05, 0B, 0C, 0D, ...
      猜你喜欢
      • 2019-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-22
      • 2017-09-30
      相关资源
      最近更新 更多