0

我正在使用 PDFClown 来分析 PDF 文档。在许多文档中,PDFClown 中的某些字符似乎具有不同的高度,即使它们显然具有相同的高度。有解决方法吗?

这是代码:

    while(_level.moveNext()) {
        ContentObject content = _level.getCurrent();
        if(content instanceof Text) {
            ContentScanner.TextWrapper text = (ContentScanner.TextWrapper)_level.getCurrentWrapper();
            for(ContentScanner.TextStringWrapper textString : text.getTextStrings()) {
                List<CharInfo> chars = new ArrayList<>();
                for(TextChar textChar : textString.getTextChars()) {
                    chars.add(new CharInfo(textChar.getBox(), textChar.getValue()));
                }
            }
        }
        else if(content instanceof XObject) {
            // Scan the external level
            if(((XObject)content).getScanner(_level)!=null){
                getContentLines(((XObject)content).getScanner(_level));
            }
        }
        else if(content instanceof ContainerObject){
            // Scan the inner level
            if(_level.getChildLevel()!=null){
                getContentLines(_level.getChildLevel());
            }
        }
    } 

这是一个示例 PDFDocument:

例子

在本文档中,我标记了两个文本块,它们都包含“百万”一词。在分析“百万”中每个字符的大小时,会发生以下情况:

  1. 第一个标记中的“m”具有高度:14,50 和宽度:8,5
  2. 第一个标记中的“i”具有高度:14,50 和宽度:3,0
  3. 第一个标记中的“l”具有高度:14,50 和宽度 3,0
  4. 第二个标记中的“m”具有高度:10,56 和宽度:6,255
  5. 第二个标记中的“i”具有高度:10,56 和宽度:2,23
  6. 第二个标记中的“l”具有高度:10,56 和宽度:2,23

即使两个文本块的所有字符显然具有相同的大小 pdf 小丑说大小是不同的。

4

1 回答 1

1

该问题是由 PDF Clown 中的错误引起的:它假定标记的内容部分和保存/恢复图形状态块正确包含在彼此中并且不重叠。即它假设这些结构仅混合为

begin-marked-content
save-graphics-state
restore-graphics-state
end-marked-content

或者

save-graphics-state
begin-marked-content
end-marked-content
restore-graphics-state

但从来没有像

save-graphics-state
begin-marked-content
restore-graphics-state
end-marked-content

或者

begin-marked-content
save-graphics-state
end-marked-content
restore-graphics-state.

不幸的是,这个假设是错误的,标记的内容部分和保存/恢复图形状态块可以以他们喜欢的任何方式混合。

例如,在手头的文档中有这样的序列:

q
[...1...]
/P <</MCID 0 >>BDC 
Q
[...2...]
EMC

这里[...1...]包含在 和 包围的保存/恢复图形状态块中q并且包含在Q[...2...]包围的标记内容块/P <</MCID 0 >>BDCEMC

但是,由于错误的假设/P <</MCID 0 >>BDC以及Q排列方式,PDF Clown 将上述内容解析为[...1...]空的标记内容块,[...2...]并包含在保存/恢复图形状态块中。

因此,如果 内部的图形状态发生变化[...2...],PDF Clown 会假定它们仅限于上述行,而实际上并非如此。


我发现修复此问题的唯一简单方法是禁用 PDF Clown 中的标记内容解析。

为此,我进行了org.pdfclown.documents.contents.tokens.ContentParser如下更改:

  1. parseContentObjects()我禁用了该contentObject instanceof EndMarkedContent选项:

      public List<ContentObject> parseContentObjects(
        )
      {
        final List<ContentObject> contentObjects = new ArrayList<ContentObject>();
        while(moveNext())
        {
          ContentObject contentObject = parseContentObject();
          // Multiple-operation graphics object end?
          if(contentObject instanceof EndText // Text.
            || contentObject instanceof RestoreGraphicsState // Local graphics state.
           /* || contentObject instanceof EndMarkedContent // End marked-content sequence. */
            || contentObject instanceof EndInlineImage) // Inline image.
            return contentObjects;
    
          contentObjects.add(contentObject);
        }
        return contentObjects;
      }
    
  2. parseContentObject我删除了if(operation instanceof BeginMarkedContent)分支:

      public ContentObject parseContentObject(
        )
      {
        final Operation operation = parseOperation();
        if(operation instanceof PaintXObject) // External object.
          return new XObject((PaintXObject)operation);
        else if(operation instanceof PaintShading) // Shading.
          return new Shading((PaintShading)operation);
        else if(operation instanceof BeginSubpath
          || operation instanceof DrawRectangle) // Path.
          return parsePath(operation);
        else if(operation instanceof BeginText) // Text.
          return new Text(
            parseContentObjects()
            );
        else if(operation instanceof SaveGraphicsState) // Local graphics state.
          return new LocalGraphicsState(
            parseContentObjects()
            );
     /*   else if(operation instanceof BeginMarkedContent) // Marked-content sequence.
          return new MarkedContent(
            (BeginMarkedContent)operation,
            parseContentObjects()
            );
     */   else if(operation instanceof BeginInlineImage) // Inline image.
          return parseInlineImage();
        else // Single operation.
          return operation;
      }
    

随着这些变化的到位,字符大小被正确提取。


顺便说一句,虽然返回的单个字符框似乎暗示该框是完全针对所讨论的字符自定义的,但事实并非如此:仅仅框的宽度是特定于字符的,高度是根据整体字体属性计算的(以及当前字体大小)但不是专门针对字符的,参见。org.pdfclown.documents.contents.fonts.Font方法getHeight(char):_

  /**
    Gets the unscaled height of the given character.

    @param textChar
      Character whose height has to be calculated.
  */
  public final double getHeight(
    char textChar
    )
  {
    /*
      TODO: Calculate actual text height through glyph bounding box.
    */
    if(textHeight == -1)
    {textHeight = getAscent() - getDescent();}
    return textHeight;
  }

单个字符高度计算仍然是一个TODO。

于 2017-08-14T09:55:34.097 回答