You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@pdfbox.apache.org by "Roman (JIRA)" <ji...@apache.org> on 2017/02/08 09:12:41 UTC

[jira] [Comment Edited] (PDFBOX-3678) JBIG2 decoding error in 2.0

    [ https://issues.apache.org/jira/browse/PDFBOX-3678?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15857701#comment-15857701 ] 

Roman edited comment on PDFBOX-3678 at 2/8/17 9:11 AM:
-------------------------------------------------------

[~tilman], we are migrating code from 1.8 to 2.0, which is used to edit image. The code finds all images which intersect given rectangular area, and fills all such intersections with black color.

The code for 1.8, it is used to populate "transformsMap" with "AffineTransform" for every image:
{code}

public class ImageTransformsCalculator extends PDFStreamEngine {

// <...>


    /**
     * This is used to handle an operation.
     *
     * @param operator The operation to perform.
     * @param arguments The list of arguments.
     *
     * @throws IOException If there is an error processing the operation.
     */
    protected void processOperator( PDFOperator operator, List<COSBase> arguments ) throws IOException {
        String operation = operator.getOperation();
        if (INVOKE_OPERATOR.equals(operation)) {

            COSName objectName = (COSName) arguments.get(0);
            Map<String, PDXObject> xobjects = getResources().getXObjects();
            PDXObject xobject = xobjects.get(objectName.getName());

            if (xobject instanceof PDXObjectImage) {
                PDXObjectImage img = (PDXObjectImage) xobject;
                AffineTransform imageTransform = calcTransform(img);
                transformsMap.put(img.getCOSObject(), imageTransform);
            }
            else if (xobject instanceof PDXObjectForm) {
                // save the graphics state
                getGraphicsStack().push((PDGraphicsState) getGraphicsState().clone());
                PDPage page = getCurrentPage();

                PDXObjectForm form = (PDXObjectForm) xobject;
                COSStream invoke = (COSStream) form.getCOSObject();
                PDResources pdResources = form.getResources();
                if (pdResources == null) {
                    pdResources = page.findResources();
                }
                // 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(getGraphicsState().getCurrentTransformationMatrix());
                    getGraphicsState().setCurrentTransformationMatrix(xobjectCTM);
                }
                processSubStream(page, pdResources, invoke);

                // restore the graphics state
                setGraphicsState( getGraphicsStack().pop());
            }
        }
        else {
            super.processOperator(operator, arguments);
        }
    }

    private AffineTransform calcTransform(PDXObjectImage image) {
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();

        Matrix ctmNew = getGraphicsState().getCurrentTransformationMatrix();
        AffineTransform imageTransform = ctmNew.createAffineTransform();
        imageTransform.scale(1.0 / imageWidth, -1.0 / imageHeight);
        imageTransform.translate(0, -imageHeight);
        imageTransform.translate(0, -1 * getCurrentPage().findCropBox().getHeight() / imageTransform.getScaleY());
        imageTransform.scale(1.0, -1.0);
        imageTransform.translate(0, -2 * imageTransform.getTranslateY() / imageTransform.getScaleY()); // PCC-8330
        try {
            return imageTransform.createInverse();
        } catch (NoninvertibleTransformException e) {
            return null;
        }
    }

// <...>
}
{code}


Then, "AffineTransform" objects are checked for intersection as the following:
{code}

        for (AffineTransform at : transforms) {
            for (Rectangle2D.Double rect : rects) {
                Shape result = at.createTransformedShape(rect);
                if (!result.intersects(0, 0, img.getWidth(), img.getHeight())) // this current rect does not cover any part of this img
                    continue;

                    //else fills image area with black
                    // ...
            }
}

{code}

On 2.0, the complicated thing is that *getGraphicsState().getCurrentTransformationMatrix();* started to return different value. However, our current code for 2.0 is different (we are trying to setup it to work like it was on 1.8 but it still not 100% correct):

{code}
    protected void processOperator(Operator operator, List<COSBase> arguments) throws IOException {
        String operation = operator.getName();
        if (INVOKE_OPERATOR.equals(operation)) {

            COSName objectName = (COSName) arguments.get(0);
            PDXObject xobject = getResources().getXObject(objectName);

            if (xobject instanceof PDImageXObject) {
                PDImageXObject img = (PDImageXObject) xobject;
                AffineTransform imageTransform = calcTransform(img);
                transformsMap.put(img.getCOSObject(), imageTransform);
            }
            else if (xobject instanceof PDFormXObject) {
                PDFormXObject form = (PDFormXObject) xobject;
                showForm(form);
            }
        }
        else {
            super.processOperator(operator, arguments);
        }
    }


    private AffineTransform calcTransform(PDImageXObject image) {
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();

        Matrix ctmNew = getGraphicsState().getCurrentTransformationMatrix();
        AffineTransform imageTransform = ctmNew.createAffineTransform();
        imageTransform.scale(getCurrentPage().getCropBox().getWidth(), getCurrentPage().getCropBox().getHeight());

        imageTransform.scale(1.0 / imageWidth, -1.0 / imageHeight);
        imageTransform.translate(0, -imageHeight);
        imageTransform.translate(0, -1 * getCurrentPage().getCropBox().getHeight() / imageTransform.getScaleY());
        imageTransform.scale(1.0, -1.0);
        imageTransform.translate(0, -2 * imageTransform.getTranslateY() / imageTransform.getScaleY()); // PCC-8330
        try {
            return imageTransform.createInverse();
        } catch (NoninvertibleTransformException e) {
            return null;
        }
    }
{code}


Sorry for mixing different issues, however these code snippets hopingly gives idea of what we are trying to do.


was (Author: rmakarov):
[~tilman], we are migrating code from 1.8 to 2.0, which is used to edit image. The code finds all images which intersect given rectangular area, and fills all such intersections with black color.

The code for 1.8, it is used to populate "transformsMap" with "AffineTransform" for every image:
{code}
    /**
     * This is used to handle an operation.
     *
     * @param operator The operation to perform.
     * @param arguments The list of arguments.
     *
     * @throws IOException If there is an error processing the operation.
     */
    protected void processOperator( PDFOperator operator, List<COSBase> arguments ) throws IOException {
        String operation = operator.getOperation();
        if (INVOKE_OPERATOR.equals(operation)) {

            COSName objectName = (COSName) arguments.get(0);
            Map<String, PDXObject> xobjects = getResources().getXObjects();
            PDXObject xobject = xobjects.get(objectName.getName());

            if (xobject instanceof PDXObjectImage) {
                PDXObjectImage img = (PDXObjectImage) xobject;
                AffineTransform imageTransform = calcTransform(img);
                transformsMap.put(img.getCOSObject(), imageTransform);
            }
            else if (xobject instanceof PDXObjectForm) {
                // save the graphics state
                getGraphicsStack().push((PDGraphicsState) getGraphicsState().clone());
                PDPage page = getCurrentPage();

                PDXObjectForm form = (PDXObjectForm) xobject;
                COSStream invoke = (COSStream) form.getCOSObject();
                PDResources pdResources = form.getResources();
                if (pdResources == null) {
                    pdResources = page.findResources();
                }
                // 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(getGraphicsState().getCurrentTransformationMatrix());
                    getGraphicsState().setCurrentTransformationMatrix(xobjectCTM);
                }
                processSubStream(page, pdResources, invoke);

                // restore the graphics state
                setGraphicsState( getGraphicsStack().pop());
            }
        }
        else {
            super.processOperator(operator, arguments);
        }
    }

    private AffineTransform calcTransform(PDXObjectImage image) {
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();

        Matrix ctmNew = getGraphicsState().getCurrentTransformationMatrix();
        AffineTransform imageTransform = ctmNew.createAffineTransform();
        imageTransform.scale(1.0 / imageWidth, -1.0 / imageHeight);
        imageTransform.translate(0, -imageHeight);
        imageTransform.translate(0, -1 * getCurrentPage().findCropBox().getHeight() / imageTransform.getScaleY());
        imageTransform.scale(1.0, -1.0);
        imageTransform.translate(0, -2 * imageTransform.getTranslateY() / imageTransform.getScaleY()); // PCC-8330
        try {
            return imageTransform.createInverse();
        } catch (NoninvertibleTransformException e) {
            return null;
        }
    }
{code}


Then, "AffineTransform" objects are checked for intersection as the following:
{code}

        for (AffineTransform at : transforms) {
            for (Rectangle2D.Double rect : rects) {
                Shape result = at.createTransformedShape(rect);
                if (!result.intersects(0, 0, img.getWidth(), img.getHeight())) // this current rect does not cover any part of this img
                    continue;

                    //else fills image area with black
                    // ...
            }
}

{code}

On 2.0, the complicated thing is that *getGraphicsState().getCurrentTransformationMatrix();* started to return different value. However, our current code for 2.0 is different (we are trying to setup it to work like it was on 1.8 but it still not 100% correct):

{code}
    protected void processOperator(Operator operator, List<COSBase> arguments) throws IOException {
        String operation = operator.getName();
        if (INVOKE_OPERATOR.equals(operation)) {

            COSName objectName = (COSName) arguments.get(0);
            PDXObject xobject = getResources().getXObject(objectName);

            if (xobject instanceof PDImageXObject) {
                PDImageXObject img = (PDImageXObject) xobject;
                AffineTransform imageTransform = calcTransform(img);
                transformsMap.put(img.getCOSObject(), imageTransform);
            }
            else if (xobject instanceof PDFormXObject) {
                PDFormXObject form = (PDFormXObject) xobject;
                showForm(form);
            }
        }
        else {
            super.processOperator(operator, arguments);
        }
    }


    private AffineTransform calcTransform(PDImageXObject image) {
        int imageWidth = image.getWidth();
        int imageHeight = image.getHeight();

        Matrix ctmNew = getGraphicsState().getCurrentTransformationMatrix();
        AffineTransform imageTransform = ctmNew.createAffineTransform();
        imageTransform.scale(getCurrentPage().getCropBox().getWidth(), getCurrentPage().getCropBox().getHeight());

        imageTransform.scale(1.0 / imageWidth, -1.0 / imageHeight);
        imageTransform.translate(0, -imageHeight);
        imageTransform.translate(0, -1 * getCurrentPage().getCropBox().getHeight() / imageTransform.getScaleY());
        imageTransform.scale(1.0, -1.0);
        imageTransform.translate(0, -2 * imageTransform.getTranslateY() / imageTransform.getScaleY()); // PCC-8330
        try {
            return imageTransform.createInverse();
        } catch (NoninvertibleTransformException e) {
            return null;
        }
    }
{code}


Sorry for mixing different issues, however these code snippets hopingly gives idea of what we are trying to do.

> JBIG2 decoding error in 2.0
> ---------------------------
>
>                 Key: PDFBOX-3678
>                 URL: https://issues.apache.org/jira/browse/PDFBOX-3678
>             Project: PDFBox
>          Issue Type: Bug
>            Reporter: Roman
>         Attachments: BTB0010010004.1-Bit_Jbig2.pdf
>
>
> After migration from PdfBox 1.8 to 2.0, we started to get the following error, when processing attached PDF (on 1.8 it was working fine):
> {code}
> java.io.IOException: Could not read JBIG2 image
> 	at org.apache.pdfbox.filter.JBIG2Filter.decode(JBIG2Filter.java:91) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.cos.COSInputStream.create(COSInputStream.java:69) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.cos.COSStream.createInputStream(COSStream.java:162) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.pdmodel.common.PDStream.createInputStream(PDStream.java:235) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject.<init>(PDImageXObject.java:147) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.pdmodel.graphics.PDXObject.createXObject(PDXObject.java:70) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.pdmodel.PDResources.getXObject(PDResources.java:409) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at com.accusoft.pdfps.redaction.utils.pdfbox.ImageTransformsCalculator.processOperator(ImageTransformsCalculator.java:78) ~[classes/:?]
> 	at org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:472) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:446) ~[pdfbox-2.0.3.jar:2.0.3]
> 	at org.apache.pdfbox.contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:149) ~[pdfbox-2.0.3.jar:2.0.3]
> Caused by: java.io.EOFException
> 	at javax.imageio.stream.ImageInputStreamImpl.readBit(ImageInputStreamImpl.java:652) ~[?:1.8.0_101]
> 	at com.levigo.jbig2.SegmentHeader.e(gn:75) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at com.levigo.jbig2.SegmentHeader.D(gn:208) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at com.levigo.jbig2.SegmentHeader.<init>(gn:67) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at com.levigo.jbig2.e.k(hm:148) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at com.levigo.jbig2.e.<init>(hm:204) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at com.levigo.jbig2.JBIG2ImageReader.D(uf:275) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at com.levigo.jbig2.JBIG2ImageReader.D(uf:233) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at com.levigo.jbig2.JBIG2ImageReader.read(uf:29) ~[pdfone-5.3.17.507.jar:5.3.17.507]
> 	at org.apache.pdfbox.filter.JBIG2Filter.decode(JBIG2Filter.java:86) ~[pdfbox-2.0.3.jar:2.0.3]
> {code}



--
This message was sent by Atlassian JIRA
(v6.3.15#6346)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@pdfbox.apache.org
For additional commands, e-mail: dev-help@pdfbox.apache.org