0

如何提取文本信息,如文本位置坐标、宽度、高度等??我用'Pdf clown'库尝试了这个,它对于普通文本非常有效,但是对于 旋转文本(90/-90度),它输出宽度/高度为0(零)。

并且 (90/-90 度) 文本的缩放因子(scaleX, scaleY) 分别显示为 (0, 0),而对于反转文本 (旋转 180 度),它是 (-1, -1)。

我想要旋转文本的信息来突出显示它们(因为宽度值为零,我无法突出显示它们)。请帮我。我正在研究.NET 环境。

我正在使用的文件: https ://nofile.io/f/Kvf2DkXvfj4/edit9.pdf

代码:使用pdfclown 示例中的TextInfoExtractionSample.cs

输出 (用于上述文件中文本的三种不同对齐方式)

文本 [x:283,y:104,w:126,h:-23] [字体大小:-24,字体样式:ArialMT]:inverted_text

文本 [x:265,y:244,w:0,h:121] [字体大小:0,字体样式:ArialMT]:vertical_text

文本 [x:347,y:131,w:0,h:167] [字体大小:0,字体样式:ArialMT]:vertical_minus90

4

1 回答 1

1

由于我更熟悉 Java 而不是 .Net,因此我分析了问题并在 PDF Clown / Java 中创建了第一个解决方法;稍后我会尝试将其移植到.Net。不过,自己做应该不会太难。

问题

您提供的示例文件在通过 PDF Clown 运行时使问题变得非常清楚TextInfoExtractionSample

截图edit9.pdf

原图截图

edit9.pdf申请后截图TextInfoExtractionSample

应用 <code>TextInfoExtractionSample</code> 后的屏幕截图

直立文字

一切看起来都很好。

颠倒的文字

单个字符框(绿色)看起来不错,但整个字符串“inverted_text”(黑色虚线)的框不包括最外面的字符。

竖排文本

单个字符框缩小为 0x0 矩形(在屏幕截图中不可见,但在内容流分析中很明显)。整个字符串的框在缺少位长度的字符串的基线上缩小为一条线(黑色虚线)。

中间夹角的文字

字符框是直立的,与页面边界平行,其基线段位于框内。但是,由于文本是倾斜的,字符的上部和下部部分位于其各自的字符框之外,而相邻的字符部分位于其内部。

整个字符串的框也与页面平行。

简而言之

文本字符和字符串框仅适用于直立文本。

在来源

这与在源代码中找到的内容相匹配:

  • 设计用于字符框的 JavaRectangle2D和 .Net类适用于平行于坐标系轴的矩形,并在 PDF Clown 中以这种方式使用。RectangleF因此,它们不能以任意角度正确表示字符的宽度和高度。

  • PDF Clown 类不包含Angle表示字符旋转的属性。

  • 字符框尺寸的计算只考虑聚合变换矩阵的主对角线上的值,即ScaleXScaleY,而忽略ShearXShearY。但是,对于不直立或颠倒的文本ShearX并且ShearY很重要,对于垂直文本ScaleX并且ScaleY为 0。

  • 从基线(定位文本的本机 PDF 方式)到字符顶部(PDF Clown 文本定位)的转换是通过单独更改 y 坐标完成的,因此,仅适用于直立和倒置文本。

一种解决方法

真正解决这个问题需要对字符框和字符串框使用完全不同的类,该类可以对任意角度的矩形进行建模。

但是,更快的解决方法是向 类和实现添加angle成员,然后在处理框时考虑该角度。此解决方法在此处实施。TextCharITextString

正如上面已经提到的,解决方法首先是在 Java 中实现的。

在 Java 中

首先我们添加一个角度成员,在操作类TextChar中计算盒子尺寸和角度的正确值,并在.ShowTextContentScanner.TextStringWrapper

然后我们添加一个角度获取器TextStringWrapperITextString通常),它返回字符串的第一个文本字符的角度。我们改进了在确定字符串框时考虑文本字符角度的TextStringWrapper方法。getBox

最后,我们将扩展TextInfoExtractionSample以在绘制框时考虑角度值。

Alpha我在草图中命名该角度 α 时命名了该角度构件。事后看来Theta,或者只是Angle更合适。

文本字符

新成员变量alpha

  private final double alpha;

一个新的和改变的构造函数

  // <constructors>
  public TextChar(
    char value,
    Rectangle2D box,
    TextStyle style,
    boolean virtual
    )
  {
      this(value, box, 0, style, virtual);
  }

  public TextChar(
    char value,
    Rectangle2D box,
    double alpha,
    TextStyle style,
    boolean virtual
    )
  {
    this.value = value;
    this.box = box;
    this.alpha = alpha;
    this.style = style;
    this.virtual = virtual;
  }
  // </constructors>

角度的吸气剂

  public double getAlpha() {
      return alpha;
  }

( TextChar.java )

显示文本

更新内部接口IScanner方法scanChar以传输角度

void scanChar(
  char textChar,
  Rectangle2D textCharBox,
  double alpha
  );

ShowText.java内部接口IScanner

更新scan方法以正确计算矩形尺寸和角度并将它们转发给IScanner实现

[...]
for(char textChar : textString.toCharArray())
{
  double charWidth = font.getWidth(textChar) * scaledFactor;

  if(textScanner != null)
  {
    /*
      NOTE: The text rendering matrix is recomputed before each glyph is painted
      during a text-showing operation.
    */
    AffineTransform trm = (AffineTransform)ctm.clone(); trm.concatenate(tm);
    double charHeight = font.getHeight(textChar,fontSize);

    // vvv--- changed
    double ascent = font.getAscent(fontSize);
    double x = trm.getTranslateX() + ascent * trm.getShearX();
    double y = contextHeight - trm.getTranslateY() - ascent * trm.getScaleY();
    double dx = charWidth * trm.getScaleX();
    double dy = charWidth * trm.getShearY();
    double alpha = Math.atan2(dy, dx);
    double w = Math.sqrt(dx*dx + dy*dy);
    dx = charHeight * trm.getShearX();
    dy = charHeight * trm.getScaleY();
    double h = Math.sqrt(dx*dx + dy*dy);
    Rectangle2D charBox = new Rectangle2D.Double(x, y, w, h);

    textScanner.scanChar(textChar,charBox, alpha);
    // ^^^--- changed
  }

  /*
    NOTE: After the glyph is painted, the text matrix is updated
    according to the glyph displacement and any applicable spacing parameter.
  */
  tm.translate(charWidth + charSpace + (textChar == ' ' ? wordSpace : 0), 0);
}
[...]

( ShowText.java )

ContentScanner 内部类 TextStringWrapper

更新TextStringWrapper构造函数ShowText.IScanner回调以接受角度参数并将其用于构造TextChar

getBaseDataObject().scan(
  state,
  new ShowText.IScanner()
  {
    @Override
    public void scanChar(
      char textChar,
      Rectangle2D textCharBox,
      double alpha
      )
    {
      textChars.add(
        new TextChar(
          textChar,
          textCharBox,
          alpha,
          style,
          false
          )
        );
    }
  }
  );

角度的吸气剂

public double getAlpha() {
    return textChars.isEmpty() ? 0 : textChars.get(0).getAlpha();
}

getBox考虑角度的实现

public Rectangle2D getBox(
  )
{
  if(box == null)
  {
    AffineTransform rot = null;
    Rectangle2D tempBox = null;
    for(TextChar textChar : textChars)
    {
      Rectangle2D thisBox = textChar.getBox();
      if (rot == null) {
          rot = AffineTransform.getRotateInstance(textChar.getAlpha(), thisBox.getX(), thisBox.getY());
          tempBox = (Rectangle2D)thisBox.clone();
      } else {
          Point2D corner = new Point2D.Double(thisBox.getX(), thisBox.getY());
          rot.transform(corner, corner);
          tempBox.add(new Rectangle2D.Double(corner.getX(), corner.getY(), thisBox.getWidth(), thisBox.getHeight()));
      }
    }
    if (tempBox != null) {
        try {
            Point2D corner = new Point2D.Double(tempBox.getX(), tempBox.getY());
            rot.invert();
            rot.transform(corner, corner);
            box = new Rectangle2D.Double(corner.getX(), corner.getY(), tempBox.getWidth(), tempBox.getHeight());
        } catch (NoninvertibleTransformException e) {
            e.printStackTrace();
        }
    }
  }
  return box;
}

ContentScanner.java内部类TextStringWrapper

文本字符串

新角度吸气剂

  public double getAlpha();

( ITextString.java )

TextExtractor 内部类 TextString

新角度吸气剂

public double getAlpha() {
    return textChars.isEmpty() ? 0 : textChars.get(0).getAlpha();
}

( TextExtractor.java )

文本信息提取示例

更改为extract正确使用角度来勾勒框

[...]
for (ContentScanner.TextStringWrapper textString : text.getTextStrings())
{
    Rectangle2D textStringBox = textString.getBox();
    System.out.println("Text [" + "x:" + Math.round(textStringBox.getX()) + "," + "y:" + Math.round(textStringBox.getY()) + "," + "w:"
            + Math.round(textStringBox.getWidth()) + "," + "h:" + Math.round(textStringBox.getHeight()) + "] [font size:"
            + Math.round(textString.getStyle().getFontSize()) + "]: " + textString.getText());

    // Drawing text character bounding boxes...
    colorIndex = (colorIndex + 1) % textCharBoxColors.length;
    composer.setStrokeColor(textCharBoxColors[colorIndex]);
    for (TextChar textChar : textString.getTextChars())
    {
        // vvv--- changed
        Rectangle2D box = textChar.getBox();
        composer.beginLocalState();
        AffineTransform rot = AffineTransform.getRotateInstance(textChar.getAlpha());
        composer.applyMatrix(rot.getScaleX(), rot.getShearY(), rot.getShearX(), rot.getScaleY(),
                box.getX(), composer.getScanner().getContextSize().getHeight() - box.getY());
        composer.add(new DrawRectangle(0, - box.getHeight(), box.getWidth(), box.getHeight()));

        composer.stroke();
        composer.end();
        // ^^^--- changed
    }

    // Drawing text string bounding box...
    composer.beginLocalState();
    composer.setLineDash(new LineDash(new double[] { 5 }));
    composer.setStrokeColor(textStringBoxColor);
    // vvv--- changed
    AffineTransform rot = AffineTransform.getRotateInstance(textString.getAlpha());
    composer.applyMatrix(rot.getScaleX(), rot.getShearY(), rot.getShearX(), rot.getScaleY(),
            textStringBox.getX(), composer.getScanner().getContextSize().getHeight() - textStringBox.getY());
    composer.add(new DrawRectangle(0, - textStringBox.getHeight(), textStringBox.getWidth(), textStringBox.getHeight()));
    // ^^^--- changed
    composer.stroke();
    composer.end();
}
[...]

TextInfoExtractionSample方法extract

结果

字符框和字符串框现在都符合预期:

解决方法的屏幕截图

所以现在宽度和高度输出也可以了:

Text [x:415,y:104,w:138,h:23] [font size:-24]: inverted_text
Text [x:247,y:365,w:128,h:23] [font size:0]: vertical_text
Text [x:364,y:131,w:180,h:23] [font size:0]: vertical_minus90
于 2018-05-22T10:30:21.787 回答