PDFClown 在一行中使用不同的字体大小

PDFClown Different font-size in one line

我正在使用 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 示例:

Example

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

  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 小丑说大小不同。

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

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.

不幸的是,这个假设是错误的,标记的内容部分和 save/restore 图形状态块可以任意混合。

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

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

此处[...1...]包含在被qQ包围的save/restore图形状态块中,[...2...]包含在被标记的内容块包围中通过 /P <</MCID 0 >>BDCEMC.

但是,由于错误的假设,以及 /P <</MCID 0 >>BDCQ 的排列方式,PDF Clown 将上述内容解析为 [...1...] 和一个空的标记内容块,并且 [...2...] 包含在 save/restore 图形状态块中。

因此,如果 [...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。