首页 文章

如何使用PDFBOX确定人工粗体样式,人工斜体样式和人工轮廓样式的文本

提问于
浏览
0

我使用PDFBox验证pdf文档 . 有必要检查PDF中存在的以下类型的文本

  • 人工粗体样式文字

  • 人造斜体样式文本 .

  • 人工轮廓样式文本

我在PDFBOX api列表中搜索但是无法找到这种api .

任何人都可以帮助我,并告诉我如何使用PDFBOX确定PDF中存在的不同类型的人工字体/文本样式 .

2 回答

  • 0

    一般程序和PDFBox问题

    理论上,我们应该从 PDFTextStripper 派生一个类并重写它的方法来开始:

    /**
     * Write a Java string to the output stream. The default implementation will ignore the <code>textPositions</code>
     * and just calls {@link #writeString(String)}.
     *
     * @param text The text to write to the stream.
     * @param textPositions The TextPositions belonging to the text.
     * @throws IOException If there is an error when writing the text.
     */
    protected void writeString(String text, List<TextPosition> textPositions) throws IOException
    {
        writeString(text);
    }
    

    然后你的覆盖应该使用 List<TextPosition> textPositions 而不是 String text ;每个 TextPosition 本质上代表一个单个字母,并且在绘制该字母时活动图形状态的信息 .

    不幸的是 textPositions 列表确实 not 包含当前版本1.8.3中的正确内容 . 例如 . 对于PDF中的行"This is normal text.",方法 writeString 被调用四次,每次为字符串"This"," is"," normal"和" text."不幸 textPositions 列表每次都包含 TextPosition 实例,用于最后一个字符串" text."的字母

    事实证明,这已经被认为是PDFBox问题PDFBOX-1804,同时已经解决为1.8.4和2.0.0版本的固定问题 .

    有人说,只要你有一个固定的PDFBox版本,你可以检查一些人工样式,如下所示:

    人工斜体文字

    此文本样式在页面内容中创建如下:

    BT
    /F0 1 Tf
    24 0 5.10137 24 66 695.5877 Tm
    0 Tr
    [<03>]TJ
    ...
    

    相关部分发生在设置文本矩阵 Tm 中 . 5.10137是剪切文本的一个因素 .

    当您检查 TextPosition textPosition 时,如上所示,您可以使用查询此值

    textPosition.getTextPos().getValue(1, 0)
    

    如果此值大于0.0,则表示人为斜体 . 如果它相关地小于0.0,则会有人为的倒退斜体 .

    人工粗体或大纲文字

    这些人工样式使用不同的渲染模式使用双重打印字母;例如首都'T',如果是粗体:

    0 0 0 1 k
    ...
    BT
    /F0 1 Tf 
    24 0 0 24 66.36 729.86 Tm 
    <03>Tj 
    4 M 0.72 w 
    0 0 Td 
    1 Tr 
    0 0 0 1 K
    <03>Tj
    ET
    

    (即首先以常规模式绘制字母,填充字母区域,然后以轮廓模式绘制,沿着字母边框绘制一条线,均为黑色,CMYK 0,0,0,1;这留下了印象更粗的字母 . )

    如果是大纲:

    BT
    /F0 1 Tf
    24 0 0 24 66 661.75 Tm
    0 0 0 0 k
    <03>Tj
    /GS1 gs
    4 M 0.288 w 
    0 0 Td
    1 Tr
    0 0 0 1 K
    <03>Tj
    ET
    

    (即首先以常规模式白色绘制字母,CMYK 0,0,0,0,填充字母区域,然后以轮廓模式绘制,沿字母边框绘制一条线,黑色,CMYK 0,0,0 ,1;这会在白色字母上留下轮廓黑色的印象 . )

    不幸的是,PDFBox PDFTextStripper 没有跟踪文本渲染模式 . 此外,它明确地在大致相同的位置丢弃重复的字符出现 . 因此,它不能承认识别这些人造风格 .

    如果你真的需要这样做,你必须更改 TextPosition 以包含渲染模式, PDFStreamEngine 将其添加到生成的 TextPosition 实例,并且 PDFTextStripper 不要删除 processTextPosition 中的重复字形 .

    更正

    我写

    不幸的是,PDFBox PDFTextStripper不跟踪文本呈现模式 .

    这不完全正确,您可以使用 getGraphicsState().getTextState().getRenderingMode() 找到当前的渲染模式 . 这意味着在 processTextPosition 期间,您确实可以使用渲染模式,并且可以尝试在某处给出给定 TextPosition 的渲染模式(和颜色!)信息,例如在某些 Map<TextPosition, ...> 中,供以后使用 .

    此外,它明确地在大致相同的位置丢弃重复的字符出现 .

    您可以通过调用 setSuppressDuplicateOverlappingText(false) 来禁用此功能 .

    通过这两个更改,您应该能够进行必要的测试以检查人工粗体和轮廓 .

    如果您在 processTextPosition 早期存储和检查样式,则甚至可能不需要后一种更改 .

    如何检索渲染模式和颜色

    正如在更正中所提到的,通过在 processTextPosition 覆盖中收集该信息,确实可以检索渲染模式和颜色信息 .

    对此,OP评论说

    总是抚摸和不抚摸的颜色变成黑色

    这开始有点令人惊讶,但在看了 PDFTextStripper.properties 之后在文本提取期间支持的运算符被初始化),原因变得清晰:

    # The following operators are not relevant to text extraction,
    # so we can silently ignore them.
    ...
    K
    k
    

    因此,在此上下文中忽略颜色设置操作符(尤其是如本文档中的CMYK颜色的操作符)!幸运的是, PageDrawer 的这些运算符的实现也可以在这种情况下使用 .

    因此,以下概念验证显示了如何检索所有必需的信息 .

    public class TextWithStateStripperSimple extends PDFTextStripper
    {
        public TextWithStateStripperSimple() throws IOException {
            super();
            setSuppressDuplicateOverlappingText(false);
            registerOperatorProcessor("K", new org.apache.pdfbox.util.operator.SetStrokingCMYKColor());
            registerOperatorProcessor("k", new org.apache.pdfbox.util.operator.SetNonStrokingCMYKColor());
        }
    
        @Override
        protected void processTextPosition(TextPosition text)
        {
            renderingMode.put(text, getGraphicsState().getTextState().getRenderingMode());
            strokingColor.put(text, getGraphicsState().getStrokingColor());
            nonStrokingColor.put(text, getGraphicsState().getNonStrokingColor());
    
            super.processTextPosition(text);
        }
    
        Map<TextPosition, Integer> renderingMode = new HashMap<TextPosition, Integer>();
        Map<TextPosition, PDColorState> strokingColor = new HashMap<TextPosition, PDColorState>();
        Map<TextPosition, PDColorState> nonStrokingColor = new HashMap<TextPosition, PDColorState>();
    
        protected void writeString(String text, List<TextPosition> textPositions) throws IOException
        {
            writeString(text + '\n');
    
            for (TextPosition textPosition: textPositions)
            {
                StringBuilder textBuilder = new StringBuilder();
                textBuilder.append(textPosition.getCharacter())
                           .append(" - shear by ")
                           .append(textPosition.getTextPos().getValue(1, 0))
                           .append(" - ")
                           .append(textPosition.getX())
                           .append(" ")
                           .append(textPosition.getY())
                           .append(" - ")
                           .append(renderingMode.get(textPosition))
                           .append(" - ")
                           .append(toString(strokingColor.get(textPosition)))
                           .append(" - ")
                           .append(toString(nonStrokingColor.get(textPosition)))
                           .append('\n');
                writeString(textBuilder.toString());
            }
        }
    
        String toString(PDColorState colorState)
        {
            if (colorState == null)
                return "null";
            StringBuilder builder = new StringBuilder();
            for (float f: colorState.getColorSpaceValue())
            {
                builder.append(' ')
                       .append(f);
            }
    
            return builder.toString();
        }
    }
    

    使用此功能,您可以获得周期' . '在正常文本中:

    . - shear by 0.0 - 256.5701 88.6875 - 0 -  0.0 0.0 0.0 1.0 -  0.0 0.0 0.0 1.0
    

    你会得到人工粗体文字;

    . - shear by 0.0 - 378.86 122.140015 - 0 -  0.0 0.0 0.0 1.0 -  0.0 0.0 0.0 1.0
    . - shear by 0.0 - 378.86002 122.140015 - 1 -  0.0 0.0 0.0 1.0 -  0.0 0.0 0.0 1.0
    

    在人造斜体:

    . - shear by 5.10137 - 327.121 156.4123 - 0 -  0.0 0.0 0.0 1.0 -  0.0 0.0 0.0 1.0
    

    在人工轮廓中:

    . - shear by 0.0 - 357.25 190.25 - 0 -  0.0 0.0 0.0 1.0 -  0.0 0.0 0.0 0.0
    . - shear by 0.0 - 357.25 190.25 - 1 -  0.0 0.0 0.0 1.0 -  0.0 0.0 0.0 0.0
    

    所以,你有,识别这些人工风格所需的所有信息 . 现在你只需要分析数据 .

    顺便说一句,看看人工大胆的情况:坐标可能并不总是相同,而是仅仅非常相似 . 因此,测试是否需要一些宽容来确定两个文本位置对象是否描述相同的位置 .

  • 4

    我解决这个问题的方法是创建一个扩展PDFTextStripper类的新类并覆盖该函数:

    getCharactersByArticle()

    注意:PDFBox版本1.8.5

    CustomPDFTextStripper class

    public class CustomPDFTextStripper extends PDFTextStripper
    {
        public CustomPDFTextStripper() throws IOException {
        super();
        }
    
        public Vector<List<TextPosition>> getCharactersByArticle(){
        return charactersByArticle;
        }
    }
    

    这样我就可以解析pdf文档,然后从自定义提取函数中获取TextPosition:

    private void extractTextPosition() throws FileNotFoundException, IOException {
    
        PDFParser parser = new PDFParser(new FileInputStream(pdf));
        parser.parse();
        StringWriter outString = new StringWriter();
        CustomPDFTextStripper stripper = new CustomPDFTextStripper();
        stripper.writeText(parser.getPDDocument(), outString);
        Vector<List<TextPosition>> vectorlistoftps = stripper.getCharactersByArticle();
        for (int i = 0; i < vectorlistoftps.size(); i++) {
            List<TextPosition> tplist = vectorlistoftps.get(i);
            for (int j = 0; j < tplist.size(); j++) {
                TextPosition text = tplist.get(j);
                System.out.println(" String "
              + "[x: " + text.getXDirAdj() + ", y: "
              + text.getY() + ", height:" + text.getHeightDir()
              + ", space: " + text.getWidthOfSpace() + ", width: "
              + text.getWidthDirAdj() + ", yScale: " + text.getYScale() + "]"
              + text.getCharacter());
            }       
        }
    }
    

    TextPositions包含有关pdf文档字符的大量信息 .

    OUTPUT:

    String [x:168.24,y:64.15997,身高:6.061287,空间:8.9664,宽度:3.4879303,yScale:8.9664] J String [x:171.69745,y:64.15997,身高:6.061287,空间:8.9664,宽度:2.2416077, yScale:8.9664] N String [x:176.25777,y:64.15997,身高:6.0343876,空间:8.9664,宽度:6.4737396,yScale:8.9664] N String [x:182.73778,y:64.15997,身高:4.214208,空间:8.9664,宽度:3.981079,yScale:8.9664] e .....

相关问题