解析PDF时出现奇怪的空格

时间:2012-08-10 12:36:37

标签: java pdf whitespace itext pdf-parsing

我需要解析PDF文档。我已经实现了解析器并使用了库 iText ,直到现在它没有任何问题。

但是我不需要解析另一个在单词中间会得到非常奇怪的空格的文档。例如,我得到:

  

Vo rber eitung auf die Motorr adsaison 。 Viele Motorr adf ahr er

所有大胆的单词都应该连接,但不知何故,PDF Parser会在单词中添加空格。但是当我将PDF中的内容复制并粘贴到文本文件中时,我得不到这些空格。

首先我认为这是因为我正在使用的PDF解析库,但是对于另一个库,我得到了完全相同的问题。

我从解析过的单词中查看了singleSpaceWidth,我发现当它添加一个空格时,它总是变化的。我试图将它们手动放在一起。但是,由于没有真正重新组合单词的模式,这几乎是不可能的。

其他人是否有类似的问题甚至解决了这个问题?

根据要求,这里有更多信息:

使用SemTextExtractionStrategy进行解析:

PdfReader reader = new PdfReader("data/SpecialTests/SuedostSchweiz/" + src);

SemTextExtractionStrategy semTextExtractionStrategy = new SemTextExtractionStrategy();

for (int i = 1; i <= reader.getNumberOfPages(); i++) {
    // Set the page number on the strategy. Is used in the Parsing strategies.
    semTextExtractionStrategy.pageNumber = i;

    // Parse text from page
    PdfTextExtractor.getTextFromPage(reader, i, semTextExtractionStrategy);
}

这里实际解析文本的SemTextExtractionStrategy方法。我在每个解析的单词之后手动添加一个空格,但不知何故它确实在检测中分割了单词:

@Override
public void parseText(TextRenderInfo renderInfo, int pageNumber) {      

    this.pageNumber = pageNumber;

    String text = renderInfo.getText();

    currTextBlock.getText().append(text + " ");

    ....
}

这是整个SemTextExtraction类,但在那里它只调用上面的方法(parseText):

public class SemTextExtractionStrategy implements TextExtractionStrategy {

    // Text Extraction Strategies
    public ColumnDetecter columnDetecter = new ColumnDetecter();

    // Image Extraction Strategies
    public ImageRetriever imageRetriever = new ImageRetriever();

    public int pageNumber = -1;

    public ArrayList<TextParsingStrategy> textParsingStrategies = new ArrayList<TextParsingStrategy>();
    public ArrayList<ImageParsingStrategy> imageParsingStrategies = new ArrayList<ImageParsingStrategy>();

    public SemTextExtractionStrategy() {

        // Add all text parsing strategies which are later on applied on the extracted text
        // textParsingStrategies.add(fontSizeMatcher);
        textParsingStrategies.add(columnDetecter);

        // Add all image parsing strategies which are later on applied on the extracted text
        imageParsingStrategies.add(imageRetriever);
    }

    @Override
    public void beginTextBlock() {

    }

    @Override
    public void renderText(TextRenderInfo renderInfo) {
        // TEXT PARSING
        for(TextParsingStrategy strategy : textParsingStrategies) {
            strategy.parseText(renderInfo, pageNumber);
        }
    }

    @Override
    public void endTextBlock() {

    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo) {
        for(ImageParsingStrategy strategy : imageParsingStrategies) {
            strategy.parseImage(renderInfo);
        }
    }
}

3 个答案:

答案 0 :(得分:3)

我使用以下Ghostscript命令处理了给定的PDF文件:

gs -o out.pdf -q -sDEVICE=pdfwrite -dOptimize=false -dUseFlageCompression=false -dCompressPages=false -dCompressFonts=false whitespacesProblem.pdf

此命令创建了一个文件out.pdf,它没有流编码,因此更易读。有趣的部分是在第52行,为了便于阅读,我将其拆分为多行:

[
  (&;&)-287.988
  (672744)29.9906
  (+\(%)30.01
  (+!4)29.9876
  (&4)-287.989
  (%4)30.0039
  (&1&8)-287.975
  (3=\)!)-288.021
  (*&4)30.0212
  (&=23)-287.996
  (+1%)-287.99
  (\(=&)-288.011
  (8&1&)-287.974
  (672744)29.9906
  (+\(3+=378$)-250.977
  (#7\)!)
]TJ

括号之间是文本字符。我更改了其中一些并观看了渲染的PDF文件,以查看哪个字符代表哪个字形。然后我解码了文本:

[
  (ele)-287.988
  (Motorr)29.9906 ***
  (adf)30.01 ***
  (ahr)29.9876 ***
  (er)-287.989
  (fr)30.0039
  (euen)-287.975
  (sich)-288.021
  ...
]

所以角色之间确实有空白。在你的情况下,这可能是字体的字距。现在的问题是你的PDF库如何解释这个空格,在我看来,即使是“负空白”也会被渲染到结果字符串中的空格中。

答案 1 :(得分:2)

pdf中的空白是一个已知的问题,正如Roland在这里的回答所描述的那样,并且在第一个评论中也看到了 https://issues.apache.org/jira/browse/TIKA-724

对我来说也是有用的答案就是huuhungus所看到的那个 https://github.com/smalot/pdfparser/issues/72

特定于PDFParser,如果您知道会遇到此问题,则更改实际将此额外空间添加到PDFParser的代码:

  

src / Smalot / PdfParser / Object.php注释掉这一行

   $text .= ' ';
     

没有完全解决它,但它是可接受的

其他库也可能有类似的临时修复,因此在某些情况下他们可以帮助解决此问题。

答案 2 :(得分:0)

因为您拥有的文档被拆分为列,所以明显的错误在

  

SemTextExtractionStrategy

类。我假设类 ColumnDetecter 可能被指责,而不是iText。我只能假设它是根据列的大小实现的,然后根据它检索文本。

如果您只想要文本,那么根据列的大小,实现可能会更简单。