使用PDFBOX基于PDF输出识别文本

时间:2014-01-29 11:52:15

标签: pdfbox

我使用PDF BOX获取PDF格式的文本颜色信息。我可以使用以下代码获取输出。但我怀疑StrokingColor代表什么,非抚摸颜色代表什么。基于此,我将如何决定哪个文本具有哪种颜色。有人建议我吗?我的cuurent输出是这样的:DeviceRGB DeviceCMYK java.awt.Color中[R = 63,G = 240,B = 0] java.awt.Color中[R = 35,G = 31,B = 32] 34.934998 31.11 31.875

PDDocument doc = null;
        try {
            doc = PDDocument.load(strFilepath);
            PDFStreamEngine engine = new PDFStreamEngine(ResourceLoader.loadProperties("org/apache/pdfbox/resources/PageDrawer.properties"));
            PDPage page = (PDPage)doc.getDocumentCatalog().getAllPages().get(1);
            engine.processStream(page, page.findResources(), page.getContents().getStream());
            PDGraphicsState graphicState = engine.getGraphicsState();
            System.out.println(graphicState.getStrokingColor().getColorSpace().getName());
            System.out.println(graphicState.getNonStrokingColor().getColorSpace().getName());
            System.out.println(graphicState.getNonStrokingColor().getJavaColor()); 
            System.out.println(graphicState.getStrokingColor().getJavaColor());
            float colorSpaceValues[] = graphicState.getStrokingColor().getColorSpaceValue();
            for (float c : colorSpaceValues) {
                System.out.println(c * 255);
            }
        }
        finally {
            if (doc != null) {
                doc.close();
            }
        }

1 个答案:

答案 0 :(得分:5)

根据评论中的澄清,OP希望

  

将一个pdf页面的字体颜色与另一个pdf页面进行比较[...]如果有一个黑色的文本“Sample”和一些灰色的其他文本“sample1”....我需要知道样品 - >黑色,sample1 - >像这样的灰色......我想要全文及其颜色

PDFBox有一个文本提取引擎PDFTextStripper。然而,将它用于手头的任务存在一些挑战:

  • 最初设计用于在文本旁边提取颜色信息;它使用的TextPosition对象甚至没有任何颜色属性。因此,我们将不得不进行一些扩展。

    • 我们将首先注册颜色操作的监听器,以便跟踪颜色。

    • 我们还会将TextPosition对象的颜色信息存储在另一个结构中(我更愿意相应地扩展文本位置,但由于几个难以访问的私有成员,这意味着相当麻烦)。 / p>

    • 这已在this answer中详细显示;对于背景,请看那里。

  • PDF允许多种绘制文字的方式。字母可以用一种颜色填充,其边界可以用另一种颜色填充。它们的边界甚至可以作为后续绘图操作的剪切路径。我们只考虑填充和抚摸颜色。

  • 以后绘制的文字可能被其他图纸覆盖,要么完全隐藏它,要么改变其外观颜色。我们暂时不理会这一点。

如上所述,我们像这样扩展PDFTextStripper

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.pdfbox.util.PDFTextStripper;
import org.apache.pdfbox.util.TextPosition;

public class ColorTextStripper extends PDFTextStripper
{
    public ColorTextStripper() throws IOException
    {
        super();
        setSuppressDuplicateOverlappingText(false);

        registerOperatorProcessor("CS", new org.apache.pdfbox.util.operator.SetStrokingColorSpace());
        registerOperatorProcessor("cs", new org.apache.pdfbox.util.operator.SetNonStrokingColorSpace());
        registerOperatorProcessor("SC", new org.apache.pdfbox.util.operator.SetStrokingColor());
        registerOperatorProcessor("sc", new org.apache.pdfbox.util.operator.SetNonStrokingColor());
        registerOperatorProcessor("SCN", new org.apache.pdfbox.util.operator.SetStrokingColor());
        registerOperatorProcessor("scn", new org.apache.pdfbox.util.operator.SetNonStrokingColor());
        registerOperatorProcessor("G", new org.apache.pdfbox.util.operator.SetStrokingGrayColor());
        registerOperatorProcessor("g", new org.apache.pdfbox.util.operator.SetNonStrokingGrayColor());
        registerOperatorProcessor("RG", new org.apache.pdfbox.util.operator.SetStrokingRGBColor());
        registerOperatorProcessor("rg", new org.apache.pdfbox.util.operator.SetNonStrokingRGBColor());
        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().getColorSpaceValue());
        nonStrokingColor.put(text, getGraphicsState().getNonStrokingColor().getColorSpaceValue());

        super.processTextPosition(text);
    }

    Map<TextPosition, Integer> renderingMode = new HashMap<TextPosition, Integer>();
    Map<TextPosition, float[]> strokingColor = new HashMap<TextPosition, float[]>();
    Map<TextPosition, float[]> nonStrokingColor = new HashMap<TextPosition, float[]>();

    final static List<Integer> FILLING_MODES = Arrays.asList(0, 2, 4, 6);
    final static List<Integer> STROKING_MODES = Arrays.asList(1, 2, 5, 6);
    final static List<Integer> CLIPPING_MODES = Arrays.asList(4, 5, 6, 7);

    @Override
    protected void writeString(String text, List<TextPosition> textPositions) throws IOException
    {
        for (TextPosition textPosition: textPositions)
        {
            Integer charRenderingMode = renderingMode.get(textPosition);
            float[] charStrokingColor = strokingColor.get(textPosition);
            float[] charNonStrokingColor = nonStrokingColor.get(textPosition);

            StringBuilder textBuilder = new StringBuilder();
            textBuilder.append(textPosition.getCharacter())
                       .append("{");

            if (FILLING_MODES.contains(charRenderingMode))
            {
                textBuilder.append("FILL:")
                           .append(toString(charNonStrokingColor))
                           .append(';');
            }

            if (STROKING_MODES.contains(charRenderingMode))
            {
                textBuilder.append("STROKE:")
                           .append(toString(charStrokingColor))
                           .append(';');
            }

            if (CLIPPING_MODES.contains(charRenderingMode))
            {
                textBuilder.append("CLIP;");
            }

            textBuilder.append("}");
            writeString(textBuilder.toString());
        }
    }

    String toString(float[] values)
    {
        if (values == null)
            return "null";
        StringBuilder builder = new StringBuilder();
        switch(values.length)
        {
        case 1:
            builder.append("GRAY"); break;
        case 3:
            builder.append("RGB"); break;
        case 4:
            builder.append("CMYK"); break;
        default:
            builder.append("UNKNOWN");
        }
        for (float f: values)
        {
            builder.append(' ')
                   .append(f);
        }

        return builder.toString();
    }
}

您可以这样称呼它:

PDFTextStripper stripper = new ColorTextStripper();

PDDocument document = PDDocument.load(SOURCE_FILE);

String text = stripper.getText(document);

结果文本包含以下内容:

P{FILL:RGB 0.803 0.076 0.086;}e{FILL:RGB 0.803 0.076 0.086;}l{FILL:RGB 0.803 0.076 0.086;}l{FILL:RGB 0.803 0.076 0.086;}e{FILL:RGB 0.803 0.076 0.086;}

G{FILL:RGB 0.102 0.101 0.095;}r{FILL:RGB 0.102 0.101 0.095;}a{FILL:RGB 0.102 0.101 0.095;}z{FILL:RGB 0.102 0.101 0.095;}i{FILL:RGB 0.102 0.101 0.095;}e{FILL:RGB 0.102 0.101 0.095;}

Pelle Grazie 来自

Pelle and Grazie

K{FILL:RGB 0.0 0.322 0.573;}E{FILL:RGB 0.0 0.322 0.573;}Y{FILL:RGB 0.0 0.322 0.573;}

C{FILL:GRAY 0.0;}o{FILL:GRAY 0.0;}m{FILL:GRAY 0.0;}b{FILL:GRAY 0.0;}i{FILL:GRAY 0.0;}n{FILL:GRAY 0.0;}e{FILL:GRAY 0.0;}d{FILL:GRAY 0.0;}

KEY Combined 来自:

KEY and Combined

您可以创建一些以结构化方式同时包含颜色和字符信息的类,而不是将所有信息序列化为String结果。就像现在在writeString中创建String结果一样,您可以更改此方法以将此类的实例添加到其中的某个列表中。

要求

至少需要PDFBox版本1.8.4才能完成这项工作。我使用2.0.0-SNAPSHOT测试它,但1.8.4就足够了。另一方面,1.8.3有一个错误,有时会将错误的TextPosition个对象转发给writeString,参见PDFBOX-1804,早期版本根本不向TextPosition提供writeString集合。