Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
348 views
in Technique[技术] by (71.8m points)

java - How to check if a text is transparent with pdfbox

I subclassed PDFStreamEngine and overloaded processTextPosition, I am now able to reconstruct the text like PDFTextStripper but I don't want to process transparent text, which is often garbage.

How can I know if some text is transparent ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As turned out the transparent text actually was not transparent at all but instead merely covered by an image: In 201103 Key Smoking Statistic for SA 2010 FINAL.pdf the text "Key Smoking Statistics for SA --- 2004" has been covered by an image showing a TC logo.

The following shows a proof of concept of a text stripper class ignoring text covered by images.

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'
", 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 );
            }
        }
    }
}

It works well with your sample document.

The check

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

unfortunately assumes that there are no rotations (all transformations aggregated) involved, neither of the text nor of the image.

For a generic solution you have to change this test to something that checks whether the 1x1 square transformed by the Matrix ctm = getGraphicsState().getCurrentTransformationMatrix() overlaps the character box transformed by the Matrix matrix = character.getTextPos() with fixed width and height cw = character.getWidth() and ch = character.getHeight(). And maybe simple overlapping does not suffice, you might want the character box to be covered sufficiently.

Furthermore this test ignores image masks, i.e. transparency of the image.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...