【问题标题】:How to check if a text is transparent with pdfbox如何使用pdfbox检查文本是否透明
【发布时间】:2013-11-06 10:37:51
【问题描述】:

我继承了PDFStreamEngine 并重载了processTextPosition,我现在能够像PDFTextStripper 那样重构文本,但我不想处理透明文本,这通常是垃圾。

我如何知道某些文本是否透明?

【问题讨论】:

  • 你说的是哪种通常是垃圾的透明文本? OCR 机器添加的文本?
  • @mkl 我猜这是某种版本控制。例如,我有一个标题为“Statistics for 2010”的 PDF,就在它后面,有一个透明的文本“Statistics for 2004”,但您可以使用 PDF 查看器来选择它。我说“垃圾”是因为它不应该被看到。
  • 但是它是怎么隐藏的呢?是白底白字吗?它实际上是使用隐形渲染模式打印的吗?是否使用每个字形为空的字体?是不是在上面涂了一些白色的形状来隐藏它?或者怎么做?这些可能性中的每一种都需要不同的代码来检查。这就是为什么我问你在说什么类型的透明文本...如果你不知道,请提供一个有代表性的示例PDF。
  • @mlk 我不确定它是如何隐藏的,这是一个示例:quitsa.org.au/cms_resources/…。 “南非关键吸烟统计数据 --- 2004”是透明的,位于第一页右上角,位于“南非关键吸烟统计数据 – 2010#”上方。
  • 文字不透明,但被图像覆盖。至少在我的机器上,当我打开 PDF 时,我可以在它被图像覆盖之前看到该文本一秒钟。稍后我会进一步研究。

标签: java pdfbox


【解决方案1】:

事实证明,透明文本实际上根本不透明,而只是被图像覆盖:在201103 Key Smoking Statistic for SA 2010 FINAL.pdf 中,文本“SA --- 2004 年关键吸烟统计数据”已被覆盖通过显示 TC 徽标的图片。

下面展示了一个文本剥离器类的概念证明,它忽略了图像覆盖的文本。

public class VisibleTextStripper extends PDFTextStripper
{
    public VisibleTextStripper() throws IOException
    {
        super();
        registerOperatorProcessor("Do", new Invoke());
    }

    //
    // Hiding operations
    //
    void hide(String name)
    {
        Matrix ctm = getGraphicsState().getCurrentTransformationMatrix();
        float x = ctm.getXPosition();
        float y = ctm.getYPosition();
        float scaledWidth = ctm.getXScale();
        float scaledHeight = ctm.getYScale();

        for(List<TextPosition> characters : charactersByArticle)
        {
            Collection<TextPosition> toRemove = new ArrayList<TextPosition>();
            for (TextPosition character : characters)
            {
                Matrix matrix = character.getTextPos();
                float cx = matrix.getXPosition();
                float cy = matrix.getYPosition();
                float cw = character.getWidth();
                float ch = character.getHeight();
                if (overlaps(x, scaledWidth, cx, cw) && overlaps(y, scaledHeight, cy, cw))
                {
                    System.out.printf("Hidden by '%s': X: %f; Y: %f; Width: %f; Height: %f; Char: '%s'\n", name, cx, cy, cw, ch, character.getCharacter());
                    toRemove.add(character);
                }
            }
            characters.removeAll(toRemove);
        }
    }

    private boolean overlaps(float start1, float width1, float start2, float width2)
    {
        if (width1 < 0)
        {
            start1 += width1;
            width1 = -width1;
        }

        if (width2 < 0)
        {
            start2 += width2;
            width2 = -width2;
        }

        if (start1 < start2)
        {
            return start1 + width1 >= start2;
        }
        else
        {
            return start2 + width2 >= start1;
        }
    }

    //
    // operator processors
    //
    public static class Invoke extends OperatorProcessor
    {
        /**
         * Log instance.
         */
        private static final Log LOG = LogFactory.getLog(Invoke.class);

        /**
         * process : Do : Paint the specified XObject (section 4.7).
         * @param operator The operator that is being executed.
         * @param arguments List
         * @throws IOException If there is an error invoking the sub object.
         */
        public void process(PDFOperator operator, List<COSBase> arguments) throws IOException
        {
            VisibleTextStripper drawer = (VisibleTextStripper)context;
            COSName objectName = (COSName)arguments.get( 0 );
            Map<String, PDXObject> xobjects = drawer.getResources().getXObjects();
            PDXObject xobject = (PDXObject)xobjects.get( objectName.getName() );
            if ( xobject == null )
            {
                LOG.warn("Can't find the XObject for '"+objectName.getName()+"'");
            }
            else if( xobject instanceof PDXObjectImage )
            {
                drawer.hide(objectName.getName());
            }
            else if(xobject instanceof PDXObjectForm)
            {
                PDXObjectForm form = (PDXObjectForm)xobject;
                COSStream formContentstream = form.getCOSStream();
                // if there is an optional form matrix, we have to map the form space to the user space
                Matrix matrix = form.getMatrix();
                if (matrix != null) 
                {
                    Matrix xobjectCTM = matrix.multiply( context.getGraphicsState().getCurrentTransformationMatrix());
                    context.getGraphicsState().setCurrentTransformationMatrix(xobjectCTM);
                }
                // find some optional resources, instead of using the current resources
                PDResources pdResources = form.getResources();
                context.processSubStream( context.getCurrentPage(), pdResources, formContentstream );
            }
        }
    }
}

它适用于您的示例文档。

支票

if (overlaps(x, scaledWidth, cx, cw) && overlaps(y, scaledHeight, cy, cw))

不幸的是,假设不涉及旋转(所有转换聚合),无论是文本还是图像。

对于通用解决方案,您必须将此测试更改为检查由Matrix ctm = getGraphicsState().getCurrentTransformationMatrix() 转换的1x1 正方形是否与由Matrix matrix = character.getTextPos() 转换的具有固定宽度和高度cw = character.getWidth()ch = character.getHeight() 的字符框重叠。也许简单的重叠是不够的,你可能希望字符框被充分覆盖。

此外,此测试忽略图像掩码,即图像的透明度。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    • 2017-02-21
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多