当前位置: 首页 > 知识库问答 >
问题:

使用带有VB. NET的PDFBox检测粗体、斜体和罢工文本

欧阳翔
2023-03-14

使用PDFBox提取PDF时,有没有办法保留文本格式?

我有一个解析PDF文档以获取信息的程序。当PDF的新版本发布时,作者使用粗体或斜体字文本来指示新信息,并在指示省略的文本中删除或加下划线。在PDFbox中使用基本的Stripper类返回所有文本,但格式被删除,因此我无法判断文本是新的还是省略的。我目前正在使用下面的项目示例代码:

    Dim doc As PDDocument = Nothing

    Try
        doc = PDDocument.load(RFPFilePath)
        Dim stripper As New PDFTextStripper()

        stripper.setAddMoreFormatting(True)
        stripper.setSortByPosition(True)
        rtxt_DocumentViewer.Text = stripper.getText(doc)

    Finally
        If doc IsNot Nothing Then
            doc.close()
        End If
    End Try

如果我简单地将PDF文本复制并粘贴到保存格式的rich text box中,我的解析代码就可以正常工作。我正在考虑通过打开PDF、选择全部、复制、关闭文档然后将其粘贴到我的rich text box来以编程方式执行此操作,但这似乎很笨重。

共有1个答案

东门晓博
2023-03-14

正如OP在评论中提到的那样,一个Java的例子可以做,而且我还只在Java中使用了PDFBox,这个答案有一个Java的例子。此外,这个例子只在PDFBox版本1.8.11中开发和测试过。

正如评论中已经提到的,

OP示例文档中的粗体和斜体效果是通过使用不同的字体(包含字母的粗体或斜体版本)绘制文本生成的。示例文档中的下划线和横线效果是通过在文本行下/穿过文本行绘制一个矩形生成的,该矩形具有文本行的宽度和非常小的高度。因此,要提取这些信息,必须扩展PDFTextStripper,以某种方式对字体更改和文本附近的矩形做出反应。

这是一个扩展PDFTextStripper的示例类,如下所示:

public class PDFStyledTextStripper extends PDFTextStripper
{
    public PDFStyledTextStripper() throws IOException
    {
        super();
        registerOperatorProcessor("re", new AppendRectangleToPath());
    }

    @Override
    protected void writeString(String text, List<TextPosition> textPositions) throws IOException
    {
        for (TextPosition textPosition : textPositions)
        {
            Set<String> style = determineStyle(textPosition);
            if (!style.equals(currentStyle))
            {
                output.write(style.toString());
                currentStyle = style;
            }
            output.write(textPosition.getCharacter());
        }
    }

    Set<String> determineStyle(TextPosition textPosition)
    {
        Set<String> result = new HashSet<>();

        if (textPosition.getFont().getBaseFont().toLowerCase().contains("bold"))
            result.add("Bold");

        if (textPosition.getFont().getBaseFont().toLowerCase().contains("italic"))
            result.add("Italic");

        if (rectangles.stream().anyMatch(r -> r.underlines(textPosition)))
            result.add("Underline");

        if (rectangles.stream().anyMatch(r -> r.strikesThrough(textPosition)))
            result.add("StrikeThrough");

        return result;
    }

    class AppendRectangleToPath extends OperatorProcessor
    {
        public void process(PDFOperator operator, List<COSBase> arguments)
        {
            COSNumber x = (COSNumber) arguments.get(0);
            COSNumber y = (COSNumber) arguments.get(1);
            COSNumber w = (COSNumber) arguments.get(2);
            COSNumber h = (COSNumber) arguments.get(3);

            double x1 = x.doubleValue();
            double y1 = y.doubleValue();

            // create a pair of coordinates for the transformation
            double x2 = w.doubleValue() + x1;
            double y2 = h.doubleValue() + y1;

            Point2D p0 = transformedPoint(x1, y1);
            Point2D p1 = transformedPoint(x2, y1);
            Point2D p2 = transformedPoint(x2, y2);
            Point2D p3 = transformedPoint(x1, y2);

            rectangles.add(new TransformedRectangle(p0, p1, p2, p3));
        }

        Point2D.Double transformedPoint(double x, double y)
        {
            double[] position = {x,y}; 
            getGraphicsState().getCurrentTransformationMatrix().createAffineTransform().transform(
                    position, 0, position, 0, 1);
            return new Point2D.Double(position[0],position[1]);
        }
    }

    static class TransformedRectangle
    {
        public TransformedRectangle(Point2D p0, Point2D p1, Point2D p2, Point2D p3)
        {
            this.p0 = p0;
            this.p1 = p1;
            this.p2 = p2;
            this.p3 = p3;
        }

        boolean strikesThrough(TextPosition textPosition)
        {
            Matrix matrix = textPosition.getTextPos();
            // TODO: This is a very simplistic implementation only working for horizontal text without page rotation
            // and horizontal rectangular strikeThroughs with p0 at the left bottom and p2 at the right top

            // Check if rectangle horizontally matches (at least) the text
            if (p0.getX() > matrix.getXPosition() || p2.getX() < matrix.getXPosition() + textPosition.getWidth() - textPosition.getFontSizeInPt() / 10.0)
                return false;
            // Check whether rectangle vertically is at the right height to underline
            double vertDiff = p0.getY() - matrix.getYPosition();
            if (vertDiff < 0 || vertDiff > textPosition.getFont().getFontDescriptor().getAscent() * textPosition.getFontSizeInPt() / 1000.0)
                return false;
            // Check whether rectangle is small enough to be a line
            return Math.abs(p2.getY() - p0.getY()) < 2;
        }

        boolean underlines(TextPosition textPosition)
        {
            Matrix matrix = textPosition.getTextPos();
            // TODO: This is a very simplistic implementation only working for horizontal text without page rotation
            // and horizontal rectangular underlines with p0 at the left bottom and p2 at the right top

            // Check if rectangle horizontally matches (at least) the text
            if (p0.getX() > matrix.getXPosition() || p2.getX() < matrix.getXPosition() + textPosition.getWidth() - textPosition.getFontSizeInPt() / 10.0)
                return false;
            // Check whether rectangle vertically is at the right height to underline
            double vertDiff = p0.getY() - matrix.getYPosition();
            if (vertDiff > 0 || vertDiff < textPosition.getFont().getFontDescriptor().getDescent() * textPosition.getFontSizeInPt() / 500.0)
                return false;
            // Check whether rectangle is small enough to be a line
            return Math.abs(p2.getY() - p0.getY()) < 2;
        }

        final Point2D p0, p1, p2, p3;
    }

    final List<TransformedRectangle> rectangles = new ArrayList<>();
    Set<String> currentStyle = Collections.singleton("Undefined");
}

(PDFStyledTextStripper.java)

除了PDFTextStripper所做的之外,这个类还

  • 使用AppendRecangleToPath运算符处理器内部类的实例从内容(使用re指令定义)中收集矩形,
  • 检查示例文档中样式变体的文本确定样式,并且
  • 每当样式更改时,都会将新样式添加WriteString中的结果中。

注意:这仅仅是概念的证明!特别是

  • TransformedRectangle中测试的实现。下划线(TextPosition)和transformedlectangle#删除线(TextPosition)非常简单,只适用于没有页面旋转的水平文本和水平矩形删除线和下划线,p0在左下角,p2在右上角

像这样使用PDFStyledTextStripper

String extractStyled(PDDocument document) throws IOException
{
    PDFTextStripper stripper = new PDFStyledTextStripper();
    stripper.setSortByPosition(true);
    return stripper.getText(document);
}

(来自ExtractText.java,从测试方法testExtractStyledFromExampleDocument调用)

一个得到结果

[]This is an example of plain text 
 
[Bold]This is an example of bold text 
[] 
[Underline]This is an example of underlined text[] 
 
[Italic]This is an example of italic text  
[] 
[StrikeThrough]This is an example of strike through text[]  
 
[Italic, Bold]This is an example of bold, italic text 

对于OP的示例文档

PS与此同时,PDFStyledTextStripper的代码也做了一些细微的更改,以适用于github发行版中共享的示例文档,尤其是其内部类TransformedRectangle的代码,参见此处。

 类似资料:
  • 我正在尝试在itext 5中使用TextField。我的字体名是“微軟正黑體英文是“Microsoft JhengHei”。我想用粗体和黑色的字体。 初始化字体(3是字体。BOLD|字体。ITALIC和BC是我的基本颜色) 我有一个文本字段变量,并将fontZh设置为setFont。 pdf结果只有字体样式、大小和颜色是正确的。但这种大胆和不自然是行不通的。

  • 在编辑文本中,用户应选择他们键入的内容是粗体、斜体还是正常。在我的情况下,当用户选择粗体时,整个编辑文本将更改为粗体,如果他再次选择斜体,则整个编辑文本将变为斜体。当用户选择粗体键入某些内容时,它应该是粗体的,当他选择斜体或正常时,他键入的单词应该是斜体或正常,而不是前一个粗体。此外,当我在编辑文本中打印输入时,即使以粗体显示,它也被正常打印。 我在底部有我的代码。 这是我将编辑文本设置为粗体或斜

  • 问题内容: 好吧,我一直在使用PDFBox,但我仍然一点都不了解它,但是 我已经阅读了文档,使用字体和其他一些地方,但是 我发现了如何从PDF中获取文本,并且样式,但我是在创建 它,而不是在阅读它。 我正在努力做某事 像:这样(在同一行上具有粗体和普通文本)。 我一直在使用流: 不确定这是否是帮助我所需的全部代码,因为我刚刚加入了这个 项目,但是当我加入时它才开始。 如果您能帮助我实施此代码,或者

  • 我在用一个工具自动生成CSS,它生成下面的@font-face标签和对应的段落Style 注意,指定的字体已经是Garamond字体的斜体版本,从技术上来说,< code>font-style:italic行是多余的。 然而,这并不奏效(我在FF、Chrome 所以,我有2个问题 > 为什么这不起作用,也就是说,为什么使用会导致它不起作用? 有没有办法通过javascript“覆盖”@font f

  • 问题内容: 我正在使用Apache pdfbox提取文本。我可以从pdf中提取文本,但我不知道该单词是否为粗体?(代码建议会很好!!!)这是从pdf提取纯文本的代码,可以正常工作。 问题答案: 的结果是纯文本。因此,将其提取后为时已晚。但是,您可以覆盖某些方法,只允许根据您的意愿格式化的文本通过。 在这种情况下,您必须覆盖 在您的替代中,您检查所讨论的文本是否满足您的要求(包含有关所讨论文本的很多

  • 我是pdfbox和java的新手——试图复制带有徽标格式等的pdf信件。我需要在句子中使用混合字体(粗体)。目前正在使用WordUtils追加段落字符串。包装,然后开始。文本等进行分析和显示(drawString有删除线,无法选择此项-我确实找到了使用它的多种字体的信息)。由于字段值在文本和长度上会有所不同,我不能简单地搜索、拆分和更改字体以显示。无法使用标签来实现这一点(天哪,我已经尝试了我能想