Java PDFClown在一行中显示不同的字体大小

Java PDFClown在一行中显示不同的字体大小,java,pdf,pdfclown,Java,Pdf,Pdfclown,我正在使用PDFClown分析PDF文档。在许多文档中,PDFClown中的某些字符似乎具有不同的高度,即使它们显然具有相同的高度。 有解决办法吗 代码如下: while(_level.moveNext()) { ContentObject content = _level.getCurrent(); if(content instanceof Text) { ContentScanner.TextWrapper text = (C

我正在使用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());
            }
        }
    } 
while(_level.moveNext()){
ContentObject内容=_level.getCurrent();
如果(文本的内容实例){
ContentScanner.TextWrapper text=(ContentScanner.TextWrapper)u level.getCurrentWrapper();
对于(ContentScanner.TextStringWrapper textString:text.getTextStrings()){
列表字符=新的ArrayList();
for(TextChar TextChar:textString.getTextChars()){
添加(新的CharInfo(textChar.getBox(),textChar.getValue());
}
}
}
else if(XObject的内容实例){
//扫描外部级别
if((XObject)content.getScanner(_level)!=null){
getContentLines((XObject)content.getScanner(_级别));
}
}
else if(ContainerObject的内容实例){
//扫描内部级别
如果(_level.getChildLevel()!=null){
getContentLines(_level.getChildLevel());
}
}
} 
以下是一个示例PDF文档:

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

  • 第一个标记中的“m”的高度为14.50,宽度为8.5
  • 第一个标记中的“i”的高度为14.50,宽度为3.0
  • 第一个标记中的“l”的高度为14.50,宽度为3.0
  • 第二个标记中的“m”的高度为10,56,宽度为6255
  • 第二个标记中的“i”的高度为10,56,宽度为2,23
  • 第二个标记中的“l”的高度为10,56,宽度为2,23

  • 即使两个文本块的所有字符显然大小相同,pdf小丑也表示大小不同。

    问题是由pdf小丑中的错误引起的:它假设标记的内容部分和保存/恢复图形状态块正确地包含在彼此中,并且不重叠。也就是说,假设这些结构仅在

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

    但从来没有

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

    不幸的是,这种假设是错误的,标记的内容部分和保存/恢复图形状态块可以以任意方式混合

    例如,在手头的文件中有如下顺序:

    q
    [...1...]
    /P <</MCID 0 >>BDC 
    Q
    [...2...]
    EMC
    
  • parseContentObject
    中,我删除了
    if(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)


    单个字符的高度计算仍然是一项任务。

    您是否可以共享一个文档,并在其中指出明显具有相同高度但PDF小丑声称具有不同高度的标志符号?A高度指的是
    textChar.getBox()
    的高度?(A)请提供一份PDF,以便复制该问题。屏幕截图根本帮不了什么忙。正如您所说,许多文档中都会出现这种问题,因此提供一个没有敏感数据的PDF应该很容易。(B) 即使一个屏幕截图就足够了,你的第一个矩形似乎被切掉了;可能在“百万”之前有一些更大的字母,…A)好的,我会搜索没有敏感数据的PDF,B)在第一个“百万”之前没有更大的字母。看起来是这样的。@mkl:很抱歉我迟到了,但我终于上传了一个例子,我稍后再看。一个有趣的文件共享服务,我得到了你的PDF和一个xml文件,声明“对这个对象的所有访问都已被禁用”…对不起,我最近几周很忙!但现在我尝试了你的方法,它似乎是有效的!谢谢!!了不起的工作!:)使用上述示例测试您的方法是有效的。但是现在,当我处理这些新信息时,我在我的项目中得到了一个空指针<代码>:java.lang.NullPointerException位于org.pdfclown.documents.contents.fonts.CompositeFont.loadEncoding(CompositeFont.java:189)@Jannik我的答案中建议的更改不会直接影响字体加载(间接地,他们这样做了:没有他们,保存/恢复图形状态块识别是错误的,因此错误的字体可能被认为是当前字体;有了他们,这些块被正确识别,因此当前字体可能不同,更确切地说:正确的字体)。因此,您的观察很可能发现了PDF中的一个问题或PDF小丑中的另一个问题。请将该问题本身作为一个问题,并提供足够的信息来重现该问题。谢谢!我将在未来几天内就此问题提出一个问题!了不起的工作!:)
    q
    [...1...]
    /P <</MCID 0 >>BDC 
    Q
    [...2...]
    EMC
    
      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;
      }
    
      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;
      }
    
      /**
        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;
      }