Package org.jpedal.parser

Source Code of org.jpedal.parser.ImageDecoder

/**
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info:  http://www.jpedal.org
*
* (C) Copyright 2011, IDRsolutions and Contributors.
*
*   This file is part of JPedal
*
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


  *
  * ---------------

  * ImageDecoder.java
  * ---------------
  * (C) Copyright 2011, by IDRsolutions and Contributors.
  *
  *
  * --------------------------
*/
package org.jpedal.parser;

import org.jpedal.PdfDecoder;
import org.jpedal.color.*;
import org.jpedal.constants.PDFImageProcessing;
import org.jpedal.exception.PdfException;
import org.jpedal.external.ImageHandler;
import org.jpedal.images.ImageOps;
import org.jpedal.images.ImageTransformer;
import org.jpedal.images.ImageTransformerDouble;
import org.jpedal.images.SamplingFactory;
import org.jpedal.io.*;
import org.jpedal.objects.GraphicsState;
import org.jpedal.objects.PdfImageData;
import org.jpedal.objects.PdfPageData;
import org.jpedal.objects.raw.*;
import org.jpedal.render.RenderUtils;
import org.jpedal.utils.LogWriter;

import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import java.awt.image.*;

public class ImageDecoder extends BaseDecoder{

    PdfImageData pdfImages=null;

    private boolean getSamplingOnly=false;

    /**flag to show if image transparent*/
    boolean isMask=true;

    String imagesInFile=null;

    boolean isPrinting;

    ImageHandler customImageHandler;

    boolean useHiResImageForDisplay;

    boolean isType3Font;

    boolean renderDirectly;

    int formLevel;

    PdfPageData pageData;

    ObjectStore objectStoreStreamRef;

    /**flag to show raw images extracted*/
    boolean clippedImagesExtracted=true;

    private boolean extractRawCMYK=false;

    /**flag to show raw images extracted*/
    boolean finalImagesExtracted=true;

    private boolean doNotRotate=false;

    /**flag to show if we physical generate a scaled version of the
     * images extracted*/
    boolean createScaledVersion = true;

    /**flag to show content is being rendered*/
    boolean renderImages=false;

    /**flag to show raw images extracted*/
    boolean rawImagesExtracted=true;

    //used internally to show optimisations
    private int optionsApplied= PDFImageProcessing.NOTHING;

    /**name of current image in pdf*/
    private String currentImage = "";

    private String formName;

    public ImageDecoder(ImageHandler customImageHandler, ObjectStore objectStoreStreamRef, boolean renderDirectly, PdfImageData pdfImages, int formLevel, PdfPageData pageData, String imagesInFile,String formName) {

        this.formName=formName;

        this.customImageHandler=customImageHandler;
        this.objectStoreStreamRef=objectStoreStreamRef;
        this.renderDirectly=renderDirectly;

        this.pdfImages=pdfImages;
        this.formLevel=formLevel;
        this.pageData=pageData;

        this.imagesInFile=imagesInFile;

    }

    private GenericColorSpace setupXObjectColorspace(PdfObject XObject,int depth, int width, int height,byte[] objectData) {

        PdfObject ColorSpace=XObject.getDictionary(PdfDictionary.ColorSpace);

        //handle colour information
        GenericColorSpace decodeColorData=new DeviceRGBColorSpace();

        if(ColorSpace!=null){
            decodeColorData= ColorspaceFactory.getColorSpaceInstance(currentPdfFile, ColorSpace, cache.XObjectColorspaces);

            decodeColorData.setPrinting(isPrinting);

            //track colorspace use
            cache.put(PdfObjectCache.ColorspacesUsed, decodeColorData.getID(),"x");

            if(depth==1 && decodeColorData.getID()== ColorSpaces.DeviceRGB && XObject.getDictionary(PdfDictionary.Mask)==null){

                byte[] data=decodeColorData.getIndexedMap();

                //no index or first colour is white so use grayscale
                if(decodeColorData.getIndexedMap()==null || (data.length==6 && data[0]==0 && data[1]==0 && data[2]==0))
                    decodeColorData=new DeviceGrayColorSpace();
            }
        }

        //fix for odd itext file (/PDFdata/baseline_screens/debug3/Leistung.pdf)
        byte[] indexData=decodeColorData.getIndexedMap();
        if(depth==8 && indexData!=null && decodeColorData.getID()==ColorSpaces.DeviceRGB && width*height==objectData.length){

            PdfObject newMask=XObject.getDictionary(PdfDictionary.Mask);
            if(newMask!=null){

                int[] maskArray=newMask.getIntArray(PdfDictionary.Mask);

                //this specific case has all zeros
                if(maskArray!=null && maskArray.length==2 && maskArray[0]==255 && maskArray[0]==maskArray[1] && decodeColorData.getIndexedMap()!=null && decodeColorData.getIndexedMap().length==768){

                    //see if index looks corrupt (ie all zeros) We exit as soon as we have disproved
                    boolean isCorrupt=true;
                    for(int jj=0;jj<768;jj++){
                        if(indexData[jj]!=0){
                            isCorrupt=false;
                            jj=768;
                        }
                    }

                    if(isCorrupt)
                        decodeColorData=new DeviceGrayColorSpace();
                }
            }
        }

        //pass through decode params
        PdfObject parms=XObject.getDictionary(PdfDictionary.DecodeParms);
        if(parms!=null)
            decodeColorData.setDecodeParms(parms);

        //set any intent
        decodeColorData.setIntent(XObject.getName(PdfDictionary.Intent));

        return decodeColorData;
    }

    public BufferedImage processImageXObject(PdfObject XObject, String image_name, byte[] objectData, boolean saveRawData, String details) throws PdfException {

        boolean imageMask;

        BufferedImage image=null;

        //add filename to make it unique
        image_name = fileName+ '-' + image_name;

        int depth=1;
        int width = XObject.getInt(PdfDictionary.Width);
        int height = XObject.getInt(PdfDictionary.Height);
        int newDepth = XObject.getInt(PdfDictionary.BitsPerComponent);
        if(newDepth!=PdfDictionary.Unknown)
            depth=newDepth;

        isMask= XObject.getBoolean(PdfDictionary.ImageMask);
        imageMask= isMask;

        GenericColorSpace decodeColorData = setupXObjectColorspace(XObject, depth, width, height, objectData);

        //tell user and log
        if(LogWriter.isOutput())
            LogWriter.writeLog("Processing XObject: "+ image_name+ ' ' +XObject.getObjectRefAsString()+ " width="+ width+ " Height="+ height+
                    " Depth="+ depth+ " colorspace="+ decodeColorData);

        /**
         * allow user to process image
         */
        if(customImageHandler!=null)
            image= customImageHandler.processImageData(gs,XObject);

        /**
         * fix for add case where image blank and actual image on SMask
         * (customer-June2011/10664.pdf)
         */
        PdfObject newSMask=XObject.getDictionary(PdfDictionary.SMask);
        byte[] index=decodeColorData.getIndexedMap();
        if(newSMask!=null && index!=null && index.length==3){ //swap out the image with inverted SMask if empty

            XObject=newSMask;
            XObject.setFloatArray(PdfDictionary.Decode, new float[]{1,0});
            objectData = currentPdfFile.readStream(XObject, true, true, false, false, false, null);

            depth=1;
            width = XObject.getInt(PdfDictionary.Width);
            height = XObject.getInt(PdfDictionary.Height);
            newDepth = XObject.getInt(PdfDictionary.BitsPerComponent);
            if(newDepth!=PdfDictionary.Unknown)
                depth=newDepth;

            decodeColorData = setupXObjectColorspace(XObject, depth, width, height, objectData);
        }

        //deal with special case of 1x1 pixel backed onto large inverted Smask which would be very slow in Generic code
        //see (Customers-dec2011/grayscale.pdf)
        if(newSMask!=null && XObject.getInt(PdfDictionary.Width)==1 && XObject.getInt(PdfDictionary.Height)==1 && XObject.getInt(PdfDictionary.BitsPerComponent)==8){ //swap out the image with inverted SMask if empty

            byte opacity =(byte)255;

            XObject=newSMask;
            newSMask=null;

            /**
             * get opacity if not default
             */
            float[] maskDecode=XObject.getFloatArray(PdfDictionary.Decode);
            if(maskDecode!=null){
                opacity = (byte)((maskDecode[0])*255);
            }

            //use black/white colour
            byte[] maskIndex=new byte[]{0,0,0,(byte)255,(byte)255,(byte)255};

            //get raw 1 bit data for smask
            byte[] data = currentPdfFile.readStream(XObject, true, true, false, false, false, null);

            //get dimensions
            width = XObject.getInt(PdfDictionary.Width);
            height = XObject.getInt(PdfDictionary.Height);

            //buffer for byte data
            int length=width*height*4;
            objectData=new byte[length];

            //create ARGB data from 1bit data and opacity
            ColorSpaceConvertor.flatten1bpc(width, data, maskIndex, true, length, opacity, objectData);

            //build image
            image=new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
            DataBuffer db = new DataBufferByte(objectData, objectData.length);
            WritableRaster raster =Raster.createInterleavedRaster(db,width,height,width * 4,4,new int[]{0,1,2,3},null);
            image.setData(raster);

        }

        //extract and process the image
        else if(customImageHandler==null ||(image==null && !customImageHandler.alwaysIgnoreGenericHandler()))
            image= processImage(decodeColorData,
                    objectData,
                    image_name,
                    width,
                    height,
                    depth,
                    imageMask,
                    XObject, saveRawData,ImageCommands.XOBJECT);


        //add details to string so we can pass back
        if(ImageCommands.trackImages && image!=null && details!=null){

            //work out effective dpi
            float dpi = gs.CTM[0][0];
            if(dpi ==0)
                dpi = gs.CTM[0][1];
            if(dpi <0)
                dpi =-dpi;

            dpi =(int)(width/dpi*100);

            //add details to string
            StringBuilder imageInfo=new StringBuilder(details);
            imageInfo.append(" w=");
            imageInfo.append(String.valueOf(width));
            imageInfo.append(" h=");
            imageInfo.append(String.valueOf(height));
            imageInfo.append(' ');
            imageInfo.append(String.valueOf((int)dpi));
            imageInfo.append(' ');
            imageInfo.append(ColorSpaces.IDtoString(decodeColorData.getID()));

            imageInfo.append(" (");
            imageInfo.append(String.valueOf(image.getWidth()));
            imageInfo.append(' ');
            imageInfo.append(String.valueOf(image.getHeight()));
            imageInfo.append(" type=");
            imageInfo.append(String.valueOf(image.getType()));
            imageInfo.append(')');

            if(imagesInFile.length()==0)
                imagesInFile=imageInfo.toString();
            else {
                imageInfo.append('\n');
                imageInfo.append(imagesInFile);
                imagesInFile=imageInfo.toString();
            }

        }

        return image;



    }

    /**
     * process image in XObject (XForm handled in PdfStreamDecoder)
     */
    public int processDOImage(String name,int dataPointer,PdfObject XObject) throws PdfException {

        //name is not unique if in form so we add form level to separate out
        if(formLevel>0)
            name= formName+'_'+ formLevel+'_'+name;

        //set if we need
        String key = null;
        if (ImageCommands.rejectSuperimposedImages) {
            key = ((int) gs.CTM[2][0]) + "-" + ((int) gs.CTM[2][1]) + '-'
                    + ((int) gs.CTM[0][0]) + '-' + ((int) gs.CTM[1][1]) + '-'
                    + ((int) gs.CTM[0][1]) + '-' + ((int) gs.CTM[1][0]);
        }

        try {
            processXImage(name, name, key, XObject);
        } catch (Error e) {
            e.printStackTrace();
            imagesProcessedFully=false;
            errorTracker.addPageFailureMessage("Error " + e + " in DO");
        } catch (Exception e) {

            if(LogWriter.isOutput())
                LogWriter.writeLog("Exception "+e);

            imagesProcessedFully=false;
            errorTracker.addPageFailureMessage("Error " + e + " in DO");
        }

        return dataPointer;

    }

    private void processXImage(String name, String details, String key, PdfObject XObject) throws PdfException {

        int previousUse = -1;

        if(ImageCommands.trackImages){
            details=details+" Image";
            if(imagesInFile==null)
                imagesInFile="";
        }

        /**don't process unless needed*/
        if (renderImages || finalImagesExtracted || clippedImagesExtracted || rawImagesExtracted) {

            //read stream for image
            byte[] objectData = null;
            if (previousUse == -1) {

                objectData = currentPdfFile.readStream(XObject, true, true, false, false, false, XObject.getCacheName(currentPdfFile.getObjectReader()));

                //flag issue
                if(objectData==null)
                    imagesProcessedFully=false;
            }

            if (objectData != null || previousUse > 0) {

                boolean alreadyCached = false;//(useHiResImageForDisplay && current.isImageCached(this.pageNum));

                BufferedImage image = null;

                //generate name including filename to make it unique less /
                currentImage = fileName + '-' + name;

                //process the image and save raw version
                if (!alreadyCached && previousUse == -1) {

                    //last flag change from true to false to fix issue
                    image = processImageXObject(XObject, name, objectData, true, details);

                }

                //fix for oddity in Annotation
                if (image != null && image.getWidth() == 1 && image.getHeight() == 1 && isType3Font) {
                    image.flush();
                    image = null;
                }

                //save transformed image
                if (image != null || alreadyCached || previousUse > 0) {

                    //manipulate CTM to allow for image truncated
                    float[][] savedCMT = null;

                    if (renderDirectly || useHiResImageForDisplay || previousUse > 0) {

                        gs.x = gs.CTM[2][0];
                        gs.y = gs.CTM[2][1];

                        if(renderDirectly){ //in own bit as other code not needed
                            current.drawImage(pageNum, image, gs, alreadyCached, name, optionsApplied, -1);
                        }else if (image != null || alreadyCached || previousUse > 0) {

                            int id = current.drawImage(pageNum, image, gs, alreadyCached, name, optionsApplied,previousUse);

                            /**
                             * delete previous used if this not transparent
                             */
                            /**
                             * ignore multiple overlapping images
                             */
                            if (ImageCommands.rejectSuperimposedImages && image != null && image.getType() != BufferedImage.TYPE_INT_ARGB) {

                                Object lastRef = cache.getImposedKey(key);

                                //delete under image....
                                if (lastRef != null && gs.getClippingShape() == null//limit to avoid issues on other files
                                    current.flagImageDeleted((Integer) lastRef);
                            }

                            /**
                             * store last usage in case it reappears unless it is transparent
                             */
                            if (ImageCommands.rejectSuperimposedImages && key != null)
                                cache.setImposedKey(key,id);
                        }
                    } else {

                        if (clippedImagesExtracted  || current.isScalingControlledByUser()) {
                            generateTransformedImage(image, name);
                        } else {
                            try {
                                generateTransformedImageSingle(image, name);
                            } catch (Exception e) {

                                if(LogWriter.isOutput())
                                    LogWriter.writeLog("Exception " + e + " on transforming image in file");
                            }
                        }
                    }

                    if (image != null)
                        image.flush();

                    //restore
                    if (savedCMT != null)
                        gs.CTM = savedCMT;
                }
            }
        }
    }

    ImageDecoder() {}

    public ImageDecoder(ImageHandler customImageHandler, boolean useHiResImageForDisplay, ObjectStore objectStoreStreamRef, boolean renderDirectly, PdfImageData pdfImages, int formLevel, PdfPageData pageData) {

        this.customImageHandler=customImageHandler;
        this.useHiResImageForDisplay=useHiResImageForDisplay;
        this.objectStoreStreamRef=objectStoreStreamRef;
        this.renderDirectly=renderDirectly;

        this.pdfImages=pdfImages;
        this.formLevel=formLevel;
        this.pageData=pageData;
    }

    public void setSamplingOnly(boolean getSamplingOnly){

        this.getSamplingOnly=getSamplingOnly;

    }

    public String getImagesInFile() {
        return this.imagesInFile;
    }

    public void setParameters(boolean isPageContent, boolean renderPage, int renderMode, int extractionMode, boolean isPrinting, boolean isType3Font, boolean useHiResImageForDisplay) {

        this.isPageContent=isPageContent;
        this.renderPage=renderPage;
        this.renderMode=renderMode;
        this.extractionMode=extractionMode;

        this.isPrinting=isPrinting;

        this.isType3Font=isType3Font;

        this.useHiResImageForDisplay=useHiResImageForDisplay;

        renderImages=renderPage &&(renderMode & PdfDecoder.RENDERIMAGES )== PdfDecoder.RENDERIMAGES;

        finalImagesExtracted=(extractionMode & PdfDecoder.FINALIMAGES) == PdfDecoder.FINALIMAGES;

        extractRawCMYK=(extractionMode &PdfDecoder.CMYKIMAGES)==PdfDecoder.CMYKIMAGES;

        clippedImagesExtracted=(extractionMode &PdfDecoder.CLIPPEDIMAGES)==PdfDecoder.CLIPPEDIMAGES;

        rawImagesExtracted=(extractionMode & PdfDecoder.RAWIMAGES) == PdfDecoder.RAWIMAGES;

        createScaledVersion = finalImagesExtracted || renderImages;

    }

    public void setImageName(String currentImage) {
        this.currentImage =currentImage;
    }

    public int getOptionsApplied() {
        return optionsApplied;
    }

    public int processIDImage(int dataPointer, int startInlineStream,byte[] stream, int tokenNumber) throws Exception{

        /**
         * read Dictionary
         */
        PdfObject XObject=new XObject(PdfDictionary.ID);

        IDObjectDecoder objectDecoder=new IDObjectDecoder(currentPdfFile.getObjectReader());
        objectDecoder.setEndPt(dataPointer-2);

        objectDecoder.readDictionaryAsObject(XObject,startInlineStream,stream);

        BufferedImage image =   null;

        boolean inline_imageMask;

        //store pointer to current place in file
        int inline_start_pointer = dataPointer + 1,i_w, i_h, i_bpc;

        //find end of stream
        int i = inline_start_pointer,streamLength=stream.length;

        //find end
        while (true) {
            //look for end EI

            //handle Pdflib variety
            if (streamLength-i>3 &&  stream[i + 1] == 69 && stream[i + 2] == 73 && stream[i+3] == 10)
                break;

            //general case
            if ((streamLength-i>3)&&(stream[i] == 32 || stream[i] == 10 || stream[i] == 13 ||  (stream[i+3] == 32 && stream[i+4] == 'Q'))
                    && (stream[i + 1] == 69)
                    && (stream[i + 2] == 73)
                    && ( stream[i+3] == 32 || stream[i+3] == 10 || stream[i+3] == 13))
                break;

            i++;

            if(i==streamLength)
                break;
        }

        if(renderImages || finalImagesExtracted || clippedImagesExtracted || rawImagesExtracted){

            //load the data
            //    generate the name including file name to make it unique
            String image_name =fileName+ "-IN-" + tokenNumber;

            int endPtr=i;
            //hack for odd files
            if(i<stream.length && stream[endPtr] != 32 && stream[endPtr] != 10 && stream[endPtr] != 13)
                endPtr++;

            //correct data (ie idoq/FC1100000021259.pdf )
            if(stream[inline_start_pointer]==10)
                inline_start_pointer++;

            /**
             * put image data in array
             */
            byte[] i_data = new byte[endPtr - inline_start_pointer];
            System.arraycopy(
                    stream,
                    inline_start_pointer,
                    i_data,
                    0,
                    endPtr - inline_start_pointer);

//            System.out.print(">>");
            //for(int ss=inline_start_pointer-5;ss<endPtr+15;ss++)
//            System.out.print((char)stream[ss]);
//            System.out.println("<<"+i_data.length+" end="+endPtr);
            //pass in image data
            XObject.setStream(i_data);

            /**
             * work out colorspace
             */
            PdfObject ColorSpace=XObject.getDictionary(PdfDictionary.ColorSpace);

            //check for Named value
            if(ColorSpace!=null){
                String colKey=ColorSpace.getGeneralStringValue();

                if(colKey!=null){
                    Object col=cache.get(PdfObjectCache.Colorspaces,colKey);

                    if(col!=null)
                        ColorSpace=(PdfObject) col;
                    else{
                        //throw new RuntimeException("error with "+colKey+" on ID "+colorspaces);
                    }
                }
            }

           // if(ColorSpace!=null && ColorSpace.getParameterConstant(PdfDictionary.ColorSpace)==PdfDictionary.Unknown)
           //     ColorSpace=null; //no values set

            /**
             * allow user to process image
             */
            if(customImageHandler!=null)
                image=customImageHandler.processImageData(gs,XObject);

            PdfArrayIterator filters = XObject.getMixedArray(PdfDictionary.Filter);

            //check not handled elsewhere
            int firstValue;
            boolean needsDecoding=false;
            if(filters!=null && filters.hasMoreTokens()){
                firstValue=filters.getNextValueAsConstant(false);

                needsDecoding=(firstValue!= PdfFilteredReader.JPXDecode && firstValue!=PdfFilteredReader.DCTDecode);
            }

            i_w=XObject.getInt(PdfDictionary.Width);
            i_h=XObject.getInt(PdfDictionary.Height);
            i_bpc=XObject.getInt(PdfDictionary.BitsPerComponent);
            inline_imageMask=XObject.getBoolean(PdfDictionary.ImageMask);

            //handle filters (JPXDecode/DCT decode is handle by process image)
            if(needsDecoding){
                PdfFilteredReader filter=new PdfFilteredReader();
                i_data=filter.decodeFilters(ObjectUtils.setupDecodeParms(XObject, currentPdfFile.getObjectReader()), i_data, filters, i_w, i_h, null);
            }

            //handle colour information
            GenericColorSpace decodeColorData=new DeviceRGBColorSpace();
            if(ColorSpace!=null){
                decodeColorData= ColorspaceFactory.getColorSpaceInstance(currentPdfFile, ColorSpace);
                decodeColorData.setPrinting(isPrinting);

                //track colorspace use
                cache.put(PdfObjectCache.ColorspacesUsed, decodeColorData.getID(),"x");

                //use alternate as preference if CMYK
                //if(newColorSpace.getID()==ColorSpaces.ICC && ColorSpace.getParameterConstant(PdfDictionary.Alternate)==ColorSpaces.DeviceCMYK)
                //newColorSpace=new DeviceCMYKColorSpace();
            }
            if(i_data!=null){

                if(customImageHandler==null ||(image==null && !customImageHandler.alwaysIgnoreGenericHandler())){


                    image=processImage(decodeColorData,
                            i_data,
                            image_name,
                            i_w,
                            i_h,
                            i_bpc,
                            inline_imageMask,
                            XObject,false, ImageCommands.ID);

                    //generate name including filename to make it unique
                    currentImage = image_name;

                }

                //skip if smaller than zero as work around for JPS bug
                if(isPrinting && image!=null && gs!=null && image.getHeight()==1
                        && gs.CTM[1][1]<1){
                    image=null;
                }

                if (image != null){

                    if(renderDirectly || this.useHiResImageForDisplay){// || current.getType()== org.jpedal.render.output.html.HTMLDisplay.CREATE_HTML){

                        gs.x=gs.CTM[2][0];
                        gs.y=gs.CTM[2][1];

                        current.drawImage(pageNum, image, gs, false, image_name, optionsApplied, -1);
                    }else{
                        if(clippedImagesExtracted)
                            generateTransformedImage(image,image_name);
                        else
                            generateTransformedImageSingle(image, image_name);
                    }

                    if(image!=null)
                        image.flush();
                }
            }
        }

        dataPointer = i + 3;

        return dataPointer;

    }

    /**
     * save the current image, clipping and
     *  resizing. This gives us a
     * clipped hires copy. In reparse, we don't
     * need to repeat some actions we know already done.
     */
    public void generateTransformedImage(BufferedImage image,String image_name) {

        float x = 0, y = 0, w, h;

        //if valid image then process
        if (image != null) {

            /**
             * scale the raw image to correct page size (at 72dpi)
             */

            //object to scale and clip. Creating instance does the scaling
            ImageTransformerDouble image_transformation =new ImageTransformerDouble(PdfDecoder.dpi,gs,image,createScaledVersion,true);

            //extract images either scaled/clipped or scaled then clipped

            image_transformation.doubleScaleTransformShear(false);

            //get intermediate image and save
            image = image_transformation.getImage();

            //save the scaled/clipped version of image if allowed
            {//if(currentPdfFile.isExtractionAllowed()){

                /**make sure the right way*/
                /*
               int dx=1,dy=1,iw=0,ih=0;
               if(currentGraphicsState.CTM[0][0]<0){
                   dx=-dx;
                   iw=image.getWidth();
               }

               if(currentGraphicsState.CTM[1][1]<0){
                   dy=-dy;
                   ih=image.getHeight();
               }
               if((dy<0)|(dx<0)){

                   AffineTransform image_at =new AffineTransform();
                   image_at.scale(dx,dy);
                   image_at.translate(-iw,-ih);
                   AffineTransformOp invert= new AffineTransformOp(image_at,  ColorSpaces.hints);
                   image = invert.filter(image,null);

               }

                */

                String image_type = objectStoreStreamRef.getImageType(currentImage);
                if(image_type==null)
                    image_type="tif";

                if(objectStoreStreamRef.saveStoredImage(
                        "CLIP_"+currentImage,
                        ImageCommands.addBackgroundToMask(image, isMask),
                        false,
                        false,
                        image_type))
                    errorTracker.addPageFailureMessage("Problem saving " + image);

            }

            /**
             * HTML5/JavaFX code is piggybacking on existing functionality to extract hires clipped image.
             * We do not need other functionality so we ignore that
             */
            if(current.isScalingControlledByUser()){

                if (image != null) {
                    gs.x=x;
                    gs.y=y;
                    current.drawImage(pageNum,image,gs,false,image_name,optionsApplied, -1);
                }

            }else{

                if(finalImagesExtracted || renderImages)
                    image_transformation.doubleScaleTransformScale();

                //complete the image and workout co-ordinates
                image_transformation.completeImage();

                //get initial values
                x = image_transformation.getImageX();
                y = image_transformation.getImageY();
                w = image_transformation.getImageW();
                h = image_transformation.getImageH();

                //get final image to allow for way we draw 'upside down'
                image = image_transformation.getImage();

                //allow for null image returned (ie if too small)
                if (image != null) {

                    //store  final image on disk & in memory
                    if(renderImages || finalImagesExtracted || clippedImagesExtracted || rawImagesExtracted){
                        pdfImages.setImageInfo(currentImage, pageNum, x, y, w, h,null);
                    }

                    //add to screen being drawn
                    if ((renderImages || !isPageContent) && image != null) {
                        gs.x=x;
                        gs.y=y;
                        current.drawImage(pageNum,image,gs,false,image_name,optionsApplied, -1);
                    }

                    /**save if required*/
                    if(!renderDirectly && isPageContent && finalImagesExtracted) {

                        //save the scaled/clipped version of image if allowed
                        if(ImageCommands.isExtractionAllowed(currentPdfFile)){
                            String image_type = objectStoreStreamRef.getImageType(currentImage);
                            objectStoreStreamRef.saveStoredImage(
                                    currentImage,
                                    ImageCommands.addBackgroundToMask(image, isMask),
                                    false,
                                    false,
                                    image_type);
                        }
                    }
                }
            }
        } else if(LogWriter.isOutput())
            //flag no image and reset clip
            LogWriter.writeLog("NO image written");

    }


    /**
     * save the current image, clipping and resizing. Id reparse, we don't
     * need to repeat some actions we know already done.
     */
    public void generateTransformedImageSingle(BufferedImage image,String image_name) {

        float x, y, w, h;

        //if valid image then process
        if (image != null) {

            // get clipped image and co-ords
            Area clipping_shape = gs.getClippingShape();

            /**
             * scale the raw image to correct page size (at 72dpi)
             */
            //object to scale and clip. Creating instance does the scaling
            ImageTransformer image_transformation;

            //object to scale and clip. Creating instance does the scaling
            image_transformation =new ImageTransformer(PdfDecoder.dpi,gs,image,true);

            //get initial values
            x = image_transformation.getImageX();
            y = image_transformation.getImageY();
            w = image_transformation.getImageW();
            h = image_transformation.getImageH();

            //get back image, which will become null if TOO small
            image = image_transformation.getImage();

            //apply clip as well if exists and not inline image
            if (image != null && customImageHandler!=null && clipping_shape != null && clipping_shape.getBounds().getWidth()>1 &&
                    clipping_shape.getBounds().getHeight()>1 && !customImageHandler.imageHasBeenScaled()) {

                //see if clip is wider than image and ignore if so
                boolean ignore_image = clipping_shape.contains(x, y, w, h);

                if (!ignore_image) {
                    //do the clipping
                    image_transformation.clipImage(clipping_shape);

                    //get ALTERED values
                    x = image_transformation.getImageX();
                    y = image_transformation.getImageY();
                    w = image_transformation.getImageW();
                    h = image_transformation.getImageH();
                }
            }

            //alter image to allow for way we draw 'upside down'
            image = image_transformation.getImage();

            //allow for null image returned (ie if too small)
            if (image != null) {

                /**turn correct way round if needed*/
                //if((currentGraphicsState.CTM[0][1]!=0 )&&(currentGraphicsState.CTM[1][0]!=0 )&&(currentGraphicsState.CTM[0][0]>=0 )){

                /*if((currentGraphicsState.CTM[0][1]>0 )&&(currentGraphicsState.CTM[1][0]>0 )&&(currentGraphicsState.CTM[0][0]>=0 )){
                       double dx=1,dy=1,scaleX=0,scaleY=0;

                       if(currentGraphicsState.CTM[0][1]>0){
                           dx=-1;
                           scaleX=image.getWidth();
                       }
                       if(currentGraphicsState.CTM[1][0]>0){
                           dy=-1;
                           scaleY=image.getHeight();
                       }

                       AffineTransform image_at =new AffineTransform();
                       image_at.scale(dx,dy);
                       image_at.translate(-scaleX,-scaleY);
                       AffineTransformOp invert= new AffineTransformOp(image_at,  ColorSpaces.hints);
                       image = invert.filter(image,null);


                   }
                */
                //store  final image on disk & in memory
                if(finalImagesExtracted || rawImagesExtracted){
                    pdfImages.setImageInfo(currentImage, pageNum, x, y, w, h,null);

                    //          if(includeImagesInData){
                    //
                    //            float xx=x;
                    //            float yy=y;
                    //
                    //            if(clipping_shape!=null){
                    //
                    //              int minX=(int)clipping_shape.getBounds().getMinX();
                    //              int maxX=(int)clipping_shape.getBounds().getMaxX();
                    //
                    //              int minY=(int)clipping_shape.getBounds().getMinY();
                    //              int maxY=(int)clipping_shape.getBounds().getMaxY();
                    //
                    //              if((xx>0 && xx<minX)||(xx<0))
                    //                xx=minX;
                    //
                    //              float currentW=xx+w;
                    //              if(xx<0)
                    //                currentW=w;
                    //              if(maxX<(currentW))
                    //                w=maxX-xx;
                    //
                    //              if(yy>0 && yy<minY)
                    //                yy=minY;
                    //
                    //              if(maxY<(yy+h))
                    //                h=maxY-yy;
                    //
                    //            }
                    //
                    //            pdfData.addImageElement(xx,yy,w,h,currentImage);
                    //          }
                }
                //add to screen being drawn
                if (renderImages || !isPageContent) {

                    //check it is not null
                    if (image != null) {
                        gs.x=x;
                        gs.y=y;
                        current.drawImage(pageNum,image,gs,false,image_name, optionsApplied, -1);

                    }
                }

                /**save if required*/
                if(isPageContent && finalImagesExtracted) {

                    //save the scaled/clipped version of image if allowed
                    if(ImageCommands.isExtractionAllowed(currentPdfFile)){

                        String image_type = objectStoreStreamRef.getImageType(currentImage);
                        objectStoreStreamRef.saveStoredImage(
                                currentImage,
                                ImageCommands.addBackgroundToMask(image, isMask),
                                false,
                                false,
                                image_type);
                    }
                }
            }
        } else if(LogWriter.isOutput())
            //flag no image and reset clip
            LogWriter.writeLog("NO image written");


    }

    /**
     * read in the image and process and save raw image
     */
    BufferedImage processImage(GenericColorSpace decodeColorData,
                               byte[] data,String name,
                               int w,int h,int d,boolean imageMask,
                               PdfObject XObject, boolean saveRawData, int mode ) throws PdfException {

        //track its use
        cache.put(PdfObjectCache.ColorspacesUsed,decodeColorData.getID(),"x");

        int rawd=d;

        int sampling=1,newW,newH;

        float[] decodeArray=XObject.getFloatArray(PdfDictionary.Decode);

        if (LogWriter.debug && decodeArray!=null){
            String val="";
            for (float aDecodeArray : decodeArray) val = val + ' ' + aDecodeArray;
        }

        PdfArrayIterator Filters = XObject.getMixedArray(PdfDictionary.Filter);

        boolean isDCT=false,isJPX=false;
        //check not handled elsewhere
        int firstValue;
        if(Filters!=null && Filters.hasMoreTokens()){
            while(Filters.hasMoreTokens()){
                firstValue=Filters.getNextValueAsConstant(true);
                isDCT=firstValue==PdfFilteredReader.DCTDecode;
                isJPX=firstValue==PdfFilteredReader.JPXDecode;
            }

        }else
            Filters=null;

        boolean removed=false, isDownsampled=false;

        BufferedImage image = null;
        String type = "jpg";

        int colorspaceID=decodeColorData.getID();

        int compCount = decodeColorData.getColorSpace().getNumComponents();

        int pX=0,pY=0;

        /**setup any imageMask*/
        byte[] maskCol =new byte[4];
        if (imageMask)
            ImageCommands.getMaskColor(maskCol,gs);

        byte[] index=decodeColorData.getIndexedMap();

        /**setup sub-sampling*/
        if(renderPage && streamType!= ValueTypes.PATTERN && !current.avoidDownSamplingImage()){

            if(isPrinting && SamplingFactory.isPrintDownsampleEnabled && w<4000){
                pX=(int)(pageData.getCropBoxWidth(this.pageNum)*4);
                pY=(int)(pageData.getCropBoxHeight(this.pageNum)*4 );

            }else if(SamplingFactory.downsampleLevel== SamplingFactory.high || getSamplingOnly){// && w>500 && h>500){ // ignore small items

                //ensure all positive for comparison
                float[][] CTM=new float[3][3];
                for(int ii=0;ii<3;ii++){
                    for(int jj=0;jj<3;jj++){
                        if(gs.CTM[ii][jj]<0)
                            CTM[ii][jj]=-gs.CTM[ii][jj];
                        else
                            CTM[ii][jj]=gs.CTM[ii][jj];
                    }
                }

                if(CTM[0][0]==0 || CTM[0][0]<CTM[0][1])
                    pX=(int) (CTM[0][1]);
                else
                    pX=(int) (CTM[0][0]);

                if(CTM[1][1]==0 || CTM[1][1]<CTM[1][0])
                    pY=(int) (CTM[1][0]);
                else
                    pY=(int) (CTM[1][1]);


                //don't bother on small itemsS
                if(!getSamplingOnly &&(w<500 || (h<600 && (w<1000 || isJPX)))){ //change??


                    pX=0;//pageData.getCropBoxWidth(this.pageNum);
                    pY=0;//pageData.getCropBoxHeight(this.pageNum);
                }

            }else if(SamplingFactory.downsampleLevel==SamplingFactory.medium){
                pX=pageData.getCropBoxWidth(this.pageNum);
                pY=pageData.getCropBoxHeight(this.pageNum);
            }
        }

        /**
         * turn off all scaling and allow user to control
         */
        if(current.isScalingControlledByUser() || current.avoidDownSamplingImage()){
            pX = -1;
            pY=-1;
        }


        //needs to be factored in or images poor on hires modes
        if((isDCT || isJPX) && multiplyer>1){

            pX= (int) (pX* multiplyer);
            pY= (int) (pY* multiplyer);
        }

        PdfObject DecodeParms=XObject.getDictionary(PdfDictionary.DecodeParms), newMask, newSMask;

        newMask=XObject.getDictionary(PdfDictionary.Mask);
        newSMask=XObject.getDictionary(PdfDictionary.SMask);


        //avoid for scanned text
        if(d==1 && (newSMask!=null || XObject.getObjectType()!=PdfDictionary.Mask) &&
                decodeColorData.getID()== ColorSpaces.DeviceGray && h<300){

            //System.out.println("XObject="+XObject.getObjectType());
            //System.out.println("newSMask="+newSMask);
            pX=0;
            pY=0;
        }

        //flag masks
        if((newMask!=null || newSMask!=null) && LogWriter.isOutput())
            LogWriter.writeLog("newMask= "+ newMask + " newSMask="+ newSMask);

        //work out if inverted (assume true and disprove)
        //work this into saved data @mariusz so 125% works
        boolean arrayInverted=false;
        if(decodeArray!=null){

            arrayInverted=true;
            int count=decodeArray.length;
            for(int aa=0;aa<count;aa=aa+2){
                if(decodeArray[aa]==1f && decodeArray[aa+1]==0f){
                    //okay
                }else{
                    arrayInverted=false;
                    aa=count;
                }
            }
        }

        /**
         * also needs to be inverted in this case
         * see Customers3/Architectural_Specification.pdf page 31 onwards
         * 20100816 - no longer seems needed and removed by MS to fix abacus file (see Rog email 13th august 2010)
         */
        //if(!arrayInverted && decodeColorData.getID()==ColorSpaces.DeviceGray && index!=null){// && index[0]==-1 && index[1]==-1 && index[2]==-1){
        //    arrayInverted=true;
        //}

        /**
         * down-sample size if displaying (some cases excluded at present)
         */
        if(renderPage &&
                newMask==null &&
                decodeColorData.getID()!=ColorSpaces.ICC &&
                (arrayInverted || decodeArray==null || decodeArray.length==0)&&
                (d==1 || d==8)
                && pX>0 && pY>0 && (SamplingFactory.isPrintDownsampleEnabled || !isPrinting)){
            //      @speed - debug

            //see what we could reduce to and still be big enough for page
            newW=w;
            newH=h;

            //limit size (allow bigger grayscale
            if(multiplyer<=1 && !isPrinting){

                int maxAllowed=1000;
                if(decodeColorData.getID()==ColorSpaces.DeviceGray){
                    maxAllowed=4000;
                }
                if(pX>maxAllowed)
                    pX=maxAllowed;
                if(pY>maxAllowed)
                    pY=maxAllowed;
            }

            int smallestH=pY<<2; //double so comparison works
            int smallestW=pX<<2;

            //cannot be smaller than page
            while(newW>smallestW && newH>smallestH){
                sampling=sampling<<1;
                newW=newW>>1;
                newH=newH>>1;
            }

            int scaleX=w/pX;
            if(scaleX<1)
                scaleX=1;

            int scaleY=h/pY;
            if(scaleY<1)
                scaleY=1;

            //choose smaller value so at least size of page
            sampling=scaleX;
            if(sampling>scaleY)
                sampling=scaleY;

        }


        //get sampling and exit from this code as we don't need to go further
        if(getSamplingOnly){// && pX>0 && pY>0){

            float scaleX=(((float)w)/pX);
            float scaleY=(((float)h)/pY);

            if(scaleX<scaleY){
                samplingUsed=scaleX;
            }else{
                samplingUsed=scaleY;
            }
            //we may need to check mask as well

            boolean checkMask=false;
            if(newSMask!=null){

                /**read the stream*/
                byte[] objectData =currentPdfFile.readStream(newSMask,true,true,false, false,false, newSMask.getCacheName(currentPdfFile.getObjectReader()));

                if(objectData!=null){

                    if(DecodeParms==null)
                        DecodeParms=newSMask.getDictionary(PdfDictionary.DecodeParms);

                    int maskW=newSMask.getInt(PdfDictionary.Width);
                    int maskH=newSMask.getInt(PdfDictionary.Height);

                    //if all white image with mask, use mask
                    boolean isDownscaled=maskW/2>w && maskH/2>h;

                    boolean ignoreMask=isDownscaled &&
                            DecodeParms!=null &&
                            DecodeParms.getInt(PdfDictionary.Colors)!=-1 &&
                            DecodeParms.getInt(PdfDictionary.Predictor)!=15;


                    //ignoreMask is hack to fix odd Visuality files
                    if(!ignoreMask)
                        checkMask=true;
                }

            }

            if(!checkMask){

                //getSamplingOnly=false;

                return null;
            }
        }

        {


            if(sampling>1 && multiplyer>1){

                //samplingUsed= sampling;

                sampling = (int) (sampling/ multiplyer);

            }

            //switch to 8 bit and reduce bw image size by averaging
            if(sampling>1){

                isDownsampled=true;

                newW=w/sampling;
                newH=h/sampling;

                boolean saveData=false;
                //flatten out high res raw data in this case so we can store and resample (see deebug3/DOC002.PDF and DOC003.PDF
                if(imageMask && w>2000  && h>2000 && d==1 && decodeColorData.getID()==ColorSpaces.DeviceRGB && gs.CTM[0][0]>0 && gs.CTM[1][1]>0){
                    saveData=true;
                }

                if(d==1 && (decodeColorData.getID()!=ColorSpaces.DeviceRGB || index==null)){

                    //save raw 1 bit data
                    //code in DynamicVectorRenderer may need alignment if it changes
                    //20090929 - re-enabled by Mark with deviceGray limit for Abacus files
                    //breaks if form rotated so only use at top level
                    // (sample file breaks so we added this as hack for fattura 451-10 del 31.10.10.pdf in customers3)
                    if(formLevel<2 && (saveData ||(!imageMask && saveRawData && decodeColorData.getID()==ColorSpaces.DeviceGray ))){

                        //copy and turn upside down first
                        int count=data.length;

                        byte[] turnedData=new byte[count];
                        System.arraycopy(data,0,turnedData,0,count);

                        //            turnedData=ImageOps.invertImage(turnedData, w, h, d, 1, null);

                        boolean isInverted=!saveData && !doNotRotate && (renderDirectly || useHiResImageForDisplay) && RenderUtils.isInverted(gs.CTM);
                        boolean isRotated=!saveData && !doNotRotate && (renderDirectly || useHiResImageForDisplay) && RenderUtils.isRotated(gs.CTM);

                        if(renderDirectly){
                            isInverted=false;
                            isRotated=false;
                        }
                       
                        if(isRotated){ //rotate at byte level with copy New Code still some issues
                            turnedData= ImageOps.rotateImage(turnedData, w, h, d, 1, index);

                            //reset
                            int temp = h;
                            h=w;
                            w=temp;

                            temp = pX;
                            pX=pY;
                            pY=temp;

                        }

                        if(isInverted)//invert at byte level with copy
                            turnedData=ImageOps.invertImage(turnedData, w, h, d, 1, index);


                        //invert all the bits if needed before we store
                        if(arrayInverted){
                            for(int aa=0;aa<count;aa++)
                                turnedData[aa]= (byte) (turnedData[aa]^255);
                        }

                        //cache if binary image (not Mask)
                        if(decodeColorData.getID()==ColorSpaces.DeviceRGB && d==1 && maskCol!=null){  //avoid cases like Hand_test/DOC028.PDF

                        }else if(((w<4000 && h<4000) || decodeColorData.getID()==ColorSpaces.DeviceGray) && !(XObject instanceof MaskObject)){ //limit added after silly sizes on Customers3/1773_A2.pdf

                            //Integer pn = new Integer(this.pageNum);
                            //Integer iC = new Integer(imageCount);
                            String key = pageNum + String.valueOf(imageCount);

                          if(saveData){
                                current.getObjectStore().saveRawImageData(key,turnedData,w,h,pX,pY,maskCol,decodeColorData.getID());
                            }else{
                                current.getObjectStore().saveRawImageData(key,turnedData,w,h,pX,pY,null,decodeColorData.getID());
                            }
                        }

                        if(isRotated){
                            //reset
                            int temp = h;
                            h=w;
                            w=temp;

                            temp = pX;
                            pX=pY;
                            pY=temp;
                        }
                    }

                    //make 1 bit indexed flat
                    if(index!=null)
                        index=decodeColorData.convertIndexToRGB(index);

                    int size=newW*newH;

                    if(imageMask){
                        size=size*4;
                        maskCol[3]=(byte)255;
                    }else if(index!=null)
                        size=size*3;

                    byte[] newData=new byte[size];

                    final int[] flag={1,2,4,8,16,32,64,128};

                    int origLineLength= (w+7)>>3;

                    int bit;
                    byte currentByte = 0;

                    //scan all pixels and down-sample
                    for(int y=0;y<newH;y++){
                        for(int x=0;x<newW;x++){

                            int bytes=0,count=0;

                            //allow for edges in number of pixels left
                            int wCount=sampling,hCount=sampling;
                            int wGapLeft=w-x;
                            int hGapLeft=h-y;
                            if(wCount>wGapLeft)
                                wCount=wGapLeft;
                            if(hCount>hGapLeft)
                                hCount=hGapLeft;

                            //count pixels in sample we will make into a pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels)
                            int ptr=0;
                            for(int yy=0;yy<hCount;yy++){
                                for(int xx=0;xx<wCount;xx++){

                                    ptr=((yy+(y*sampling))*origLineLength)+(((x*sampling)+xx)>>3);

                                    if(ptr<data.length){
                                        currentByte=data[ptr];
                                    }else{
                                        currentByte=0;
                                    }

                                    if(imageMask && !arrayInverted)
                                        currentByte=(byte) (currentByte ^ 255);

                                    bit=currentByte & flag[7-(((x*sampling)+xx)& 7)];

                                    if(bit!=0)
                                        bytes++;
                                    count++;
                                }
                            }

                            //set value as white or average of pixels
                            int offset=x+(newW*y);

                            if(count>0){
                                if(imageMask){
                                    //System.out.println("xx");
                                    for(int ii=0;ii<4;ii++){
                                        if(arrayInverted)
                                            newData[(offset*4)+ii]=(byte)(255-(((maskCol[ii] & 255)*bytes)/count));
                                        else
                                            newData[(offset*4)+ii]=(byte)((((maskCol[ii] & 255)*bytes)/count));
                                        //System.out.println(newData[(offset*4)+ii]+" "+(byte)(((maskCol[ii] & 255)*bytes)/count);

                                    }
                                }else if(index!=null && decodeColorData.getID()==ColorSpaces.Separation){


                                    for(int ii=0;ii<3;ii++)
                                        if((bytes/count)>0.5f){
                                            newData[(offset*3)+ii]=(byte)(((index[3+ii] & 255)));
                                        }else{
                                            newData[(offset*3)+ii]=(byte)(((index[ii] & 255)));

                                        }

                                }else if(index!=null && d==1){
                                    int av;
                                   
                                  for(int ii=0;ii<3;ii++){
                                       
                                        //can be in either order so look at index
                                        if(index[0]==-1 && index[1]==-1 && index[2]==-1){
                                          av=(index[ii] & 255) +(index[ii+3] & 255);
                                            newData[(offset*3)+ii]=(byte)(255-((av *bytes)/count));
                                        }else{//  if(decodeColorData.getID()==ColorSpaces.DeviceCMYK){  //avoid color 'smoothing' - see CustomersJune2011/lead base paint.pdf
                                          float ratio=bytes/count;
                                          if(ratio>0.5)
                                              newData[(offset*3)+ii]= (byte) (index[ii+3]);
                                          else
                                              newData[(offset*3)+ii]= (byte) (index[ii]);

                                        }
                                    }
                                }else if(index!=null){
                                    for(int ii=0;ii<3;ii++)
                                        newData[(offset*3)+ii]=(byte)(((index[ii] & 255)*bytes)/count);
                                }else
                                    newData[offset]=(byte)((255*bytes)/count);
                            }else{

                                if(imageMask){
                                    for(int ii=0;ii<3;ii++)
                                        newData[(offset*4)+ii]=(byte)0;

                                }else if(index!=null){
                                    for(int ii=0;ii<3;ii++)
                                        newData[((offset)*3)+ii]=0;
                                }else
                                    newData[offset]=(byte) 255;
                            }
                        }
                    }

                    data=newData;

                    if(index!=null)
                        compCount=3;

                    h=newH;
                    w=newW;
                    decodeColorData.setIndex(null, 0);

                    //remap Separation as already converted here
                    if(decodeColorData.getID()==ColorSpaces.Separation){
                        decodeColorData=new DeviceRGBColorSpace();

                        //needs to have these settings if 1 bit not indexed
                        if(d==1 && index==null){
                            compCount=1;

                            int count=data.length;
                            for(int aa=0;aa<count;aa++)
                                data[aa]= (byte) (data[aa]^255);
                        }
                    }

                    d=8;

                    //imageMask=false;

                }else if(d==8 && (Filters==null || (!isDCT && !isJPX))){

                    boolean hasIndex=decodeColorData.getIndexedMap()!=null &&
                            (decodeColorData.getID()==ColorSpaces.DeviceRGB || decodeColorData.getID()==ColorSpaces.CalRGB ||
                            decodeColorData.getID()==ColorSpaces.DeviceCMYK || decodeColorData.getID()==ColorSpaces.ICC);

                    int oldSize=data.length;


                    int x=0,y=0,xx=0,yy=0,jj=0,comp=0,origLineLength=0,indexCount=1;
                    try{

                        if(hasIndex){ //convert to sRGB
                            comp=1;

                            compCount=3;
                            indexCount=3;
                            index=decodeColorData.convertIndexToRGB(index);

                            decodeColorData.setIndex(null,0);
                        }else{
                            comp=decodeColorData.getColorComponentCount();
                        }

                        //black and white
                        if(w*h==oldSize || decodeColorData.getID()==ColorSpaces.DeviceGray)
                            comp=1;


                        byte[] newData;
                        if(hasIndex){ //hard-coded to 3 values
                            newData=new byte[newW*newH*indexCount];
                            origLineLength= w;
                        }else{
                            newData=new byte[newW*newH*comp];
                            origLineLength= w*comp;
                        }
                        //System.err.println(w+" "+h+" "+data.length+" comp="+comp+" scaling="+sampling+" "+decodeColorData);

                        //System.err.println("size="+w*h*comp+" filter"+filter+" scaling="+sampling+" comp="+comp);
                        //System.err.println("w="+w+" h="+h+" data="+data.length+" origLineLength="+origLineLength+" sampling="+sampling);
                        //scan all pixels and down-sample
                        for(y=0;y<newH;y++){
                            for(x=0;x<newW;x++){

                                //allow for edges in number of pixels left
                                int wCount=sampling,hCount=sampling;
                                int wGapLeft=w-x;
                                int hGapLeft=h-y;
                                if(wCount>wGapLeft)
                                    wCount=wGapLeft;
                                if(hCount>hGapLeft)
                                    hCount=hGapLeft;

                                int[] indexAv;
                                for(jj=0;jj<comp;jj++){
                                    int byteTotal=0,count=0,ptr,newPtr;
                                    //noinspection ObjectAllocationInLoop
                                    indexAv=new int[indexCount];
                                    //count pixels in sample we will make into a pixel (ie 2x2 is 4 pixels , 4x4 is 16 pixels)
                                    for(yy=0;yy<hCount;yy++){
                                        for(xx=0;xx<wCount;xx++){

                                            ptr=((yy+(y*sampling))*origLineLength)+(((x*sampling*comp)+(xx*comp)+jj));
                                            if(ptr<oldSize){
                                                if(!hasIndex){
                                                    byteTotal=byteTotal+(data[ptr] & 255);
                                                }else{
                                                    for(int aa=0;aa<indexCount;aa++)
                                                        indexAv[aa]=indexAv[aa]+(index[(((data[ptr] & 255)*indexCount)+aa)] & 255);

                                                }

                                                count++;
                                            }
                                        }
                                    }

                                    //set value as white or average of pixels
                                    if(hasIndex){
                                        newPtr=jj+(x*indexCount)+(newW*y*indexCount);
                                        for(int aa=0;aa<indexCount;aa++)
                                            newData[newPtr+aa]=(byte)((indexAv[aa])/count);
                                    }else if(count>0){
                                        newPtr=jj+(x*comp)+(newW*y*comp);
                                        newData[newPtr]=(byte)((byteTotal)/count);
                                    }
                                }
                            }
                        }

                        data=newData;
                        h=newH;
                        w=newW;

                    }catch(Exception e){

                    }
                }else if(!isDCT && !isJPX && index==null){
                }
            }
        }

        /**handle any decode array*/
        if(decodeArray==null || decodeArray.length == 0){
        }else if(Filters!=null &&(isJPX||isDCT)){ //don't apply on jpegs
        }else{
            ImageCommands.applyDecodeArray(data, d, decodeArray,colorspaceID, XObject);
        }


        if (imageMask) {/** create an image from the raw data*/

            //allow for 1 x 1 pixel
            /**
             * allow for 1 x 1 pixels scaled up or fine lines
             */
            float ratio=((float)h)/(float)w;

            if((isPrinting && ratio<0.1f && w>4000 && h>1) || (ratio<0.001f && w>4000 && h>1|| (w==1 && h==1)){// && data[0]!=0){

                float ix=gs.CTM[2][0];
                float iy=gs.CTM[2][1];

                float ih=gs.CTM[1][1];
                if(ih==0)
                    ih=gs.CTM[1][0];
                if(ih<0){
                    iy=iy+ih;
                    ih=-ih;
                }

                float iw=gs.CTM[0][0];
                if(iw==0)
                    iw=gs.CTM[0][1];
                if(iw<0){
                    ix=ix+iw;
                    iw=-iw;
                }

                //factor in GS rotation and swap w and h
                if(gs.CTM[0][0]==0 && gs.CTM[0][1]>0 && gs.CTM[1][0]!=0 && gs.CTM[1][1]==0){
                    float tmp=ih;
                    ih=iw;
                    iw=tmp;
                }

                //allow for odd values less than 1 and ensure minimum width
                if(iw<1)
                    iw=1;
                if(ih<1)
                    ih=1;

                int lwidth=-1;

                //for thin lines, use line width to ensure appears
                if(ih<3){

                    lwidth=(int)ih;
                    ih=1;
                }else if(iw<3){
                    lwidth=(int)iw;
                    iw=1;
                }

                GeneralPath currentShape =new GeneralPath(GeneralPath.WIND_NON_ZERO);

                currentShape.moveTo(ix,iy);
                currentShape.lineTo(ix,iy+ih);
                currentShape.lineTo(ix+iw,iy+ih);
                currentShape.lineTo(ix+iw,iy);
                currentShape.closePath();

                //save for later
                if (renderPage && currentShape!=null){

                    float lastLineWidth=gs.getLineWidth();

                    if(lwidth>0)
                        gs.setLineWidth(lwidth);

                    gs.setNonstrokeColor(gs.nonstrokeColorSpace.getColor());
                    gs.setFillType(GraphicsState.FILL);

                    current.drawShape(currentShape,gs, Cmd.F) ;

                    //restore after draw
                    if(lwidth>0)
                        gs.setLineWidth(lastLineWidth);

                }
                return null;

            }
            else if(h==2 && d==1 && ImageCommands.isRepeatingLine(data, h)) {
                /* Takes account of ef1603e.pdf.  A thin horizontal dotted line is not scaled properly, therefore its converted to a shape. */
                /* Condition is only executed if line is uniform along vertical axis*/

                float ix = gs.CTM[2][0];
                float iy = gs.CTM[2][1];
                float ih=gs.CTM[1][1];
                float iw=gs.CTM[0][0];

                //factor in GS rotation and swap w and h
                if(gs.CTM[0][0]==0 && gs.CTM[0][1]>0 && gs.CTM[1][0]!=0 && gs.CTM[1][1]==0){
                    float tmp=ih;
                    ih=iw;
                    iw=tmp;
                }

                double byteWidth = iw / (data.length / h);
                double bitWidth = byteWidth / 8;

                for(int col = 0; col < data.length / h; col++) {
                    int currentByte = (int) data[col] & 0xff;
                    currentByte = ~currentByte & 0xff;

                    int bitCount = 8;
                    double endX = 0, startX;
                    boolean draw = false;

                    while(currentByte != 0 || draw) {
                        bitCount--;

                        if((currentByte & 0x1) == 1) {
                            if(!draw) {
                                endX = ((bitCount + 0.5) * bitWidth) + (col * byteWidth);
                                draw = true;
                            }
                        }
                        else if(draw) {
                            draw = false;
                            startX = ((bitCount + 0.5) * bitWidth) + (col * byteWidth);
                            @SuppressWarnings("ObjectAllocationInLoop")
                            GeneralPath currentShape = new GeneralPath(GeneralPath.WIND_NON_ZERO);

                            currentShape.moveTo((float) (ix + startX), iy);
                            currentShape.lineTo((float) (ix + startX), iy + ih);
                            currentShape.lineTo((float) (ix + endX), iy + ih);
                            currentShape.lineTo((float) (ix + endX),iy);
                            currentShape.closePath();

                            //save for later
                            if (renderPage && currentShape!=null){
                                gs.setNonstrokeColor(gs.nonstrokeColorSpace.getColor());
                                gs.setFillType(GraphicsState.FILL);

                                current.drawShape(currentShape,gs, Cmd.F) ;

                            }
                        }
                        currentByte = currentByte >>> 1;
                    }
                }
                return null;
            }
            else {

                //see if black and back object

                if(isDownsampled){
                    /** create an image from the raw data*/
                    DataBuffer db = new DataBufferByte(data, data.length);

                    int[] bands = {0,1,2,3};
                    image =new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB);
                    Raster raster =Raster.createInterleavedRaster(db,w,h,w * 4,4,bands,null);
                    image.setData(raster);

                }else{

                    //try to keep as binary if possible
                    boolean hasObjectBehind=true;

                    if(h<20)//added as found file with huge number of tiny tiles
                        hasObjectBehind=true;
                    else if(mode!=ImageCommands.ID) //not worth it for inline image
                        hasObjectBehind=current.hasObjectsBehind(gs.CTM);


                    //remove empty images in some files
                    boolean isBlank=false,keepNonTransparent=false;
                    if(imageMask && d==1 && decodeColorData.getID()==ColorSpaces.DeviceRGB && maskCol[0]==0 && maskCol[1]==0 && maskCol[2]==0){

                        //see if blank (assume true and disprove) and remove as totally see-through
                        isBlank=true;
                        for(int aa=0;aa<data.length;aa++){
                            if(data[aa]!=-1){
                                isBlank=false;
                                aa=data.length;
                            }
                        }
                       
                        if(isPrinting && (mode==ImageCommands.ID || isType3Font || d==1)){ //avoid transparency if possible
                            WritableRaster raster =Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, 1, null);
                            image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_BINARY);
                            image.setData(raster);
                            keepNonTransparent=true;
                        }else if(isBlank){
                            image=null;
                            removed=true;

                        }else{
                            byte[] newIndex={(maskCol[0]),(maskCol[1]), (maskCol[2]),(byte)255,(byte)255,(byte)255};
                            image = ColorSpaceConvertor.convertIndexedToFlat(d,w, h, data, newIndex,true,true);
                        }
                    }

                    if(isBlank){
                        //done above so ignore
                    }else if(!isPrinting && maskCol[0]==0 && maskCol[1]==0 && maskCol[2]==0 && !hasObjectBehind && !this.isType3Font && decodeColorData.getID()!=ColorSpaces.DeviceRGB){

                        if(d==1){
                            WritableRaster raster =Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, 1, null);
                            image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_BINARY);
                            image.setData(raster);

                        }else{ //down-sampled above //never called
                            final int[] bands = {0};

                            Raster raster =Raster.createInterleavedRaster(new DataBufferByte(data, data.length),w,h,w,1,bands,null);

                            image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_GRAY);
                            image.setData(raster);

                        }
                    }else if(!keepNonTransparent){

                        //if(hasObjectBehind){
                        //image=ColorSpaceConvertor.convertToARGB(image);
                        if(d==8 && isDownsampled){ //never called

                            byte[] newIndex={(maskCol[0]),(maskCol[1]), (maskCol[2]),(byte)255,(byte)255,(byte)255};
                            image = ColorSpaceConvertor.convertIndexedToFlat(d,w, h, data, newIndex, true,true);
                            // }else if(isType3Font){
                            //   WritableRaster raster =Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, 1, null);
                            // image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_BINARY);
                            //image.setData(raster);
                            // System.out.println(image.getType()+" "+image);
                        }else if((w<4000 && h<4000)|| hasObjectBehind){   //needed for hires
                            byte[] newIndex={maskCol[0],maskCol[1],maskCol[2],(byte)255,(byte)255,(byte)255};
                            image = ColorSpaceConvertor.convertIndexedToFlat(1,w, h, data, newIndex, true,false);
                            //}

                        }else{
                            //WritableRaster raster =Raster.createPackedRaster(new DataBufferByte(data, data.length), w, h, d, null);
                            //ismage = new BufferedImage(new IndexColorModel(d, 1, maskCol, 0, false), raster, false, null);
                            /**/
                        }
                    }
                }
            }
        }else if (Filters == null) { //handle no filters

            //save out image
            if(LogWriter.isOutput())
                LogWriter.writeLog("Image "+ name+ ' ' + w+ "W * "+ h+ "H with No Compression at BPC "+ d);

            image =makeImage(decodeColorData,w,h,d,data,compCount,XObject);

        } else if (isDCT) { //handle JPEGS

            if(LogWriter.isOutput())
                LogWriter.writeLog("JPeg Image "+ name+ ' ' + w+ "W * "+ h+ 'H'+" arrayInverted="+arrayInverted);

            /**
             * get image data,convert to BufferedImage from JPEG & save out
             */
            if(colorspaceID== ColorSpaces.DeviceCMYK && extractRawCMYK){

                if(LogWriter.isOutput())
                    LogWriter.writeLog("Raw CMYK image " + name + " saved.");

                if(!objectStoreStreamRef.saveRawCMYKImage(data, name))
                    errorTracker.addPageFailureMessage("Problem saving Raw CMYK image " + name);

            }

            /**
             try {
             java.io.FileOutputStream a =new java.io.FileOutputStream("/Users/markee/Desktop/"+ name + ".jpg");

             a.write(data);
             a.flush();
             a.close();

             } catch (Exception e) {
             LogWriter.writeLog("Unable to save jpeg " + name);

             }  /**/


            //if ICC with Alt RGB, use alternative first
            boolean decodedOnAltColorspace=false;
            if(decodeColorData.getID()==ColorSpaces.ICC){

                //try first and catch any error
                int alt=decodeColorData.getAlternateColorSpace();

                GenericColorSpace altDecodeColorData = null;

                if(alt==ColorSpaces.DeviceRGB)
                    altDecodeColorData=new DeviceRGBColorSpace();
                else if(alt==ColorSpaces.DeviceCMYK)
                    altDecodeColorData=new DeviceCMYKColorSpace();

                //try if any alt found
                if(altDecodeColorData!=null){

                    try{
                        image=altDecodeColorData.JPEGToRGBImage(data, w, h, decodeArray,pX , pY , arrayInverted);

                        //if it returns image it worked flag and switch over
                        if(image!=null){
                            decodedOnAltColorspace=true;
                            decodeColorData=altDecodeColorData;

                            //flag if YCCK
                            if(decodeColorData.isImageYCCK())
                                hasYCCKimages=true;
                        }
                    }catch(Exception e){
                        errorTracker.addPageFailureMessage("Unable to use alt colorspace with " + name + " to JPEG");
                        e.printStackTrace();
                        image.flush();
                        image=null;
                    }
                }
            }

            /**decode if not done above*/
            if(!decodedOnAltColorspace){
                //separation, renderer
                try{
                    image=decodeColorData.JPEGToRGBImage(data, w, h, decodeArray,pX , pY , arrayInverted);

                    //flag if YCCK
                    if(decodeColorData.isImageYCCK())
                        hasYCCKimages=true;

                    //image=simulateOP(image);
                }catch(Exception e){
                    errorTracker.addPageFailureMessage("Problem converting " + name + " to JPEG");
                    e.printStackTrace();
                    image.flush();
                    image=null;
                }/**catch(Error err){
                 addPageFailureMessage("Problem converting "+name+" to JPEG");
                 //e.printStackTrace();
                 image=null;
                 }/**/
            }
           
            type = "jpg";

            //set in makeImage so not set for JPEGS - we do it explicitly here
            setRotationOptionsOnJPEGImage();

           
        }else if(isJPX){ //needs imageio library

            if(LogWriter.isOutput())
                LogWriter.writeLog("JPeg 2000 Image "+ name+ ' ' + w+ "W * "+ h+ 'H');

            /**
             try {
             java.io.FileOutputStream a =new java.io.FileOutputStream("/Users/markee/Desktop/"+ name + ".jpg");

             a.write(data);
             a.flush();
             a.close();

             } catch (Exception e) {
             LogWriter.writeLog("Unable to save jpeg " + name);

             }  /**/

            if(JAIHelper.isJAIused()){

                image = decodeColorData.JPEG2000ToRGBImage(data,w,h,decodeArray,pX,pY);

                type = "jpg";
            }else{
                if(System.getProperty("org.jpedal.jai")!=null && System.getProperty("org.jpedal.jai").toLowerCase().equals("true")){
                    if(!ImageCommands.JAImessageShow){
                        ImageCommands.JAImessageShow=true;
                        System.err.println("JPeg 2000 Images need both JAI and imageio.jar on classpath");
                    }
                    throw new RuntimeException("JPeg 2000 Images need both JAI and imageio.jar on classpath");
                }else{
                    System.err.println("JPeg 2000 Images needs the VM parameter -Dorg.jpedal.jai=true switch turned on");
                    throw new RuntimeException("JPeg 2000 Images needs the VM parameter -Dorg.jpedal.jai=true switch turned on");
                }
            }

            //set in makeImage so not set for JPEGS - we do it explicitly here
            setRotationOptionsOnJPEGImage();

        } else { //handle other types

            if(LogWriter.isOutput())
                LogWriter.writeLog(name+ ' ' + w+ "W * "+ h+ "H BPC="+d+ ' '+decodeColorData);


            image =makeImage(decodeColorData,w,h,d,data,compCount,XObject);

            //choose type on basis of size and avoid ICC as they seem to crash the Java class
            if (d == 8 || gs.nonstrokeColorSpace.getID()== ColorSpaces.DeviceRGB || gs.nonstrokeColorSpace.getID()== ColorSpaces.ICC)
                type = "jpg";
        }

        if (image != null) {

            if(newSMask!=null && DecodeParms==null){
                DecodeParms=newSMask.getDictionary(PdfDictionary.DecodeParms);

                /**
                 * handle tiny gray dot used with larger mask for images in MSword  (Customers customers-dec2011/January_Good_News2.pdf)
                 */
                if(decodeColorData.getID()==ColorSpaces.DeviceGray && w<newSMask.getInt(PdfDictionary.Width) && h<newSMask.getInt(PdfDictionary.Height)){

                    //check data is all black
                    int bytes=data.length;
                    boolean isEmpty=true;
                    for(int aa=0;aa<bytes;aa++){
                        if(data[aa]!=0){
                            isEmpty=false;
                            aa=bytes;
                        }
                    }
               
                    if(isEmpty){
                        image=new BufferedImage(newSMask.getInt(PdfDictionary.Width),newSMask.getInt(PdfDictionary.Height), BufferedImage.TYPE_BYTE_GRAY);
                    }
                }
            }

            /**handle any soft mask*/
            if(newSMask!=null)
                image = addSMaskObject(decodeColorData, data, name, w, h,XObject, isDCT, isJPX, image, DecodeParms, newSMask);
            else if(newMask!=null)
                image = ImageCommands.addMaskObject(decodeColorData, d, isDCT, isJPX, image,colorspaceID, index, newMask, optionsApplied, currentPdfFile);

            if(image!=null)
                image = ImageCommands.simulateOverprint(decodeColorData, data, isDCT, isJPX, image, colorspaceID, newMask, newSMask, current, gs);

            if(image==null)
                return null;

            if(!renderDirectly)
                saveImage(name, createScaledVersion, image, type);

        }

        if(image == null && !removed){
            imagesProcessedFully=false;
        }

        //apply any transfer function
        PdfObject TR=gs.getTR();
        if(TR!=null) //array of values
            image=ImageCommands.applyTR(image, TR,currentPdfFile);

        //try to simulate some of blend by removing white if not bottom image
        if(DecodeParms!=null  && DecodeParms.getInt(PdfDictionary.Blend)!=PdfDictionary.Unknown && current.hasObjectsBehind(gs.CTM) && image!=null && image.getType()!=2 && (!isDCT || DecodeParms.getInt(PdfDictionary.QFactor)==0))
            image= ImageCommands.makeBlackandWhiteTransparent(image);

        //sharpen 1 bit
        if(pX>0 && pY>0 && rawd==1 && ImageCommands.sharpenDownsampledImages && (decodeColorData.getID()==ColorSpaces.DeviceGray || decodeColorData.getID()==ColorSpaces.DeviceRGB)){

            Kernel kernel = new Kernel(3, 3,
                    new float[] {
                            -1, -1, -1,
                            -1, 9, -1,
                            -1, -1, -1});
            BufferedImageOp op = new ConvolveOp(kernel);
            image = op.filter(image, null);

        }

        //number of images used for caching
        imageCount++;

        /**
         * transparency slows down printing so try to reduce if possible in printing
         */
       if(mode==ImageCommands.ID && isPrinting && image!=null && d==1 && maskCol!=null && maskCol[0]==0 && maskCol[1]==0 && maskCol[2]==0 && maskCol[3]==0){

            int iw=image.getWidth();
            int ih=image.getHeight();
            BufferedImage newImage=new BufferedImage(iw,ih,BufferedImage.TYPE_BYTE_GRAY);

            newImage.getGraphics().setColor(Color.WHITE);
            newImage.getGraphics().fillRect(0,0,iw,ih);
            newImage.getGraphics().drawImage(image, 0, 0, null);
            image=newImage;
       }

        return image;
    }

    /**
     * needs to be explicitly set for JPEG images if getting image from object
     */
    private void setRotationOptionsOnJPEGImage() {


        if(imageStatus>0 && gs.CTM[0][0]>0 && gs.CTM[0][1]>0 && gs.CTM[1][1]>0 && gs.CTM[1][0]<0){
/**/
            //we need a different op for Image and viewer as we handle in diff ways
            if(imageStatus==IMAGE_getImageFromPdfObject){
                //optionsApplied=optionsApplied+ PDFImageProcessing.IMAGE_INVERTED;
                gs.CTM[0][1]=-gs.CTM[0][1];
                //gs.CTM[1][0]=gs.CTM[1][0];
                gs.CTM[1][1]=-gs.CTM[1][1];
                gs.CTM[2][1]=gs.CTM[2][1]-gs.CTM[1][1];

            }else if(imageStatus==SCREEN_getImageFromPdfObject)
                optionsApplied=optionsApplied+ PDFImageProcessing.IMAGE_INVERTED;
            /**/

        }else if(optionsApplied>0 && gs.CTM[0][0]<0 && gs.CTM[1][1]<0 && gs.CTM[0][1]==0 && gs.CTM[1][0]==0){ //fix for elfo.pdf

            //optionsApplied=optionsApplied+ PDFImageProcessing.IMAGE_INVERTED;
            //gs.CTM[0][1]=-gs.CTM[0][1];
            //gs.CTM[1][0]=gs.CTM[1][0];
            gs.CTM[1][1]=-gs.CTM[1][1];
            gs.CTM[2][1]=gs.CTM[2][1]-gs.CTM[1][1]+gs.CTM[1][0];
            //gs.CTM[2][0]=gs.CTM[2][0]+30;

            gs.CTM[2][0]=gs.CTM[2][0]-gs.CTM[0][1];
        }
    }


    private void saveImage(String name, boolean createScaledVersion,
                           BufferedImage image, String type) {
        if (image!=null && image.getSampleModel().getNumBands() == 1)
            type = "tif";

        if(isPageContent &&(renderImages || finalImagesExtracted || clippedImagesExtracted || rawImagesExtracted)){

            //save the raw image or blank if demo or encryption enabled
            if (ImageCommands.isExtractionAllowed(currentPdfFile)){

                objectStoreStreamRef.saveStoredImage(name,ImageCommands.addBackgroundToMask(image, isMask),false,createScaledVersion,type);

            }else{

                /**create copy and scale if required*/
                if(PdfDecoder.dpi!=72){

                    int imageType=image.getType();
                    if(imageType==0)
                        imageType=BufferedImage.TYPE_INT_RGB;
                    BufferedImage newImage=new BufferedImage(image.getWidth(),image.getHeight(),imageType);
                    newImage.createGraphics().drawImage(image,null,null);
                    float s=((float)PdfDecoder.dpi)/72;
                    AffineTransform scale = new AffineTransform();
                    scale.scale(s, s);
                    AffineTransformOp scalingOp =new AffineTransformOp(scale, ColorSpaces.hints);
                    newImage =scalingOp.filter(newImage, null);
                    objectStoreStreamRef.saveStoredImage(name,ImageCommands.addBackgroundToMask(newImage, isMask),false,createScaledVersion,type);

                }else{
                    objectStoreStreamRef.saveStoredImage(name,ImageCommands.addBackgroundToMask(image, isMask),false,createScaledVersion,type);
                }
            }
        }
    }

    /**
     * turn raw data into a BufferedImage
     */
    private BufferedImage makeImage(GenericColorSpace decodeColorData,int w,int h,int d,
                                    byte[] data, int comp, PdfObject XObject) {


        //ensure correct size
        if(decodeColorData.getID()== ColorSpaces.DeviceGray){
            if(d==1){
                int requiredSize=((w+7)>>3)*h;
                int oldSize=data.length;
                if(oldSize<requiredSize){
                    byte[] oldData=data;
                    data=new byte[requiredSize];
                    System.arraycopy(oldData,0,data,0,oldSize);

                    //and fill rest with 255 for white
                    for(int aa=oldSize;aa<requiredSize;aa++)
                        data[aa]=(byte)255;
                }

            }else if(d==8){
                int requiredSize=w*h;
                int oldSize=data.length;
                if(oldSize<requiredSize){
                    byte[] oldData=data;
                    data=new byte[requiredSize];
                    System.arraycopy(oldData,0,data,0,oldSize);
                }
            }
        }

        /**
         * put data into separate array. If we keep in PdfData then on pages where same image reused
         * such as adobe/Capabilities and precisons, its flipped each time as its an object :-(
         */
        //int byteCount=rawData.length;
        //byte[] data=new byte[byteCount];
        //System.arraycopy(rawData, 0, data, 0, byteCount);


        ColorSpace cs=decodeColorData.getColorSpace();
        int ID=decodeColorData.getID();

        BufferedImage image = null;
        byte[] index =decodeColorData.getIndexedMap();

        optionsApplied=PDFImageProcessing.NOTHING;


        /**fast op on data to speed up image manipulation*/
        //optimse rotations here as MUCH faster and flag we have done this
        //something odd happens if CTM[2][1] is negative so factor ignore this case
        boolean isInverted=!doNotRotate && useHiResImageForDisplay && RenderUtils.isInverted(gs.CTM);
        boolean isRotated=!doNotRotate && useHiResImageForDisplay && RenderUtils.isRotated(gs.CTM);

        //do not apply to mask
        //last condition is for /Users/markee/PDFdata/baseline_screens/debug2/StampsProblems.pdf
        if(XObject.getGeneralType(PdfDictionary.Type)==PdfDictionary.Mask && streamType!=ValueTypes.FORM){
            isInverted=false;
            isRotated=false;
        }

        //This fix was masking miscalculation.
        //fix for image wrong on Customers3/ImageQualitySample_v0.2.docx.pdf
//        if(isInverted && gs.CTM[1][1]>0 && gs.lastCTM[1][1]<0)
//            isInverted=false;
//
//        if(renderDirectly && ! this.isType3Font){
//            isInverted=false;
//            isRotated=false;
//        }

        //I optimised the code slightly - you were setting booleans are they had been
        //used - I removed so it keeps code shorter

        if(isInverted){//invert at byte level with copy

            //needs to be 1 for sep
            int count=comp;
            if(ID==ColorSpaces.Separation)
                count=1;
            else if(ID==ColorSpaces.DeviceN)
                count=decodeColorData.getColorComponentCount();

            byte[] processedData= ImageOps.invertImage(data, w, h, d, count, index);

            if(processedData!=null){

                data=processedData;
                optionsApplied=optionsApplied+PDFImageProcessing.IMAGE_INVERTED;

            }
        }



        if(isRotated){ //rotate at byte level with copy New Code still some issues
            byte[] processedData=ImageOps.rotateImage(data, w, h, d, comp, index);

            if(processedData!=null){
                data=processedData;

                optionsApplied=optionsApplied+PDFImageProcessing.IMAGE_ROTATED;

                //reset
                int temp = h;
                h=w;
                w=temp;
            }
        }

        //data=ColorSpaceConvertor.convertIndexedToFlat(d,w, h, data, index, 255);

        //System.out.println("index="+index);

        if (index != null) { //indexed images

            if(LogWriter.isOutput())
                LogWriter.writeLog("Indexed "+w+ ' ' +h);

            /**convert index to rgb if CMYK or ICC*/
            if(!decodeColorData.isIndexConverted()){
                index=decodeColorData.convertIndexToRGB(index);
            }

            //workout size and check in range
            //int size =decodeColorData.getIndexSize()+1;

            //pick out draft setting of totally empty iamge and ignore
            if(d==8 && decodeColorData.getIndexSize()==0 && decodeColorData.getID()==ColorSpaces.DeviceRGB){

                boolean hasPixels=false;

                int indexCount=index.length;
                for(int ii=0;ii<indexCount;ii++){
                    if(index[ii]!=0){
                        hasPixels=true;
                        ii=indexCount;
                    }
                }

                if(!hasPixels){
                    int pixelCount=data.length;

                    for(int ii=0;ii<pixelCount;ii++){
                        if(data[ii]!=0){
                            hasPixels=true;
                            ii=pixelCount;
                        }
                    }
                }
                if(!hasPixels){
                    return new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB);
                }
            }
            //allow for half bytes (ie bootitng.pdf)
            //if(d==4 && size>16)
            //    size=16;

            //      WritableRaster raster =Raster.createPackedRaster(db, w, h, d, null);

            //      ColorModel cm=new IndexColorModel(d, size, index, 0, false);
            //      image = new BufferedImage(cm, raster, false, null);

            //if(debugColor)
            //System.out.println("xx d="+d+" w="+w+" data="+data.length+" index="+index.length+" size="+size);

            try{
                if(d==1 && index.length==6 && index[0]== index[3] && index[1]== index[4] && index[2]== index[5]){
                    image=null;//remove image in Itext which is white on white
                }else
                    image = ColorSpaceConvertor.convertIndexedToFlat(d,w, h, data, index,false,false);
            }catch(Exception e){
                e.printStackTrace();
            }

        } else if (d == 1) { //bitmaps next

            image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_BINARY);
            /** create an image from the raw data*/
            DataBuffer db = new DataBufferByte(data, data.length);

            WritableRaster raster =Raster.createPackedRaster(db, w, h, d, null);
            image.setData(raster);

        }else if(ID==ColorSpaces.Separation || ID==ColorSpaces.DeviceN){
            if(LogWriter.isOutput())
                LogWriter.writeLog("Converting Separation/DeviceN colorspace to sRGB ");

            image=decodeColorData.dataToRGB(data,w,h);

            //direct images
        } else if (comp == 4) { //handle CMYK or ICC

            if(LogWriter.isOutput())
                LogWriter.writeLog("Converting ICC/CMYK colorspace to sRGB ");

            image =ColorSpaceConvertor.convertFromICCCMYK(w,h,data);

            //ShowGUIMessage.showGUIMessage("y",image,"y");
        } else if (comp == 3) {

            if(LogWriter.isOutput())
                LogWriter.writeLog("Converting 3 comp colorspace to sRGB index="+index);

            //work out from size what sort of image data we have
            if (w * h == data.length) {
                if (d == 8 && index!=null){
                    image = ColorSpaceConvertor.convertIndexedToFlat(d,w, h, data, index, false,false);
                }else{

                    /** create an image from the raw data*/
                    DataBuffer db = new DataBufferByte(data, data.length);

                    int[] bands = {0};

                    image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_GRAY);
                    Raster raster =Raster.createInterleavedRaster(db,w,h,w,1,bands,null);
                    image.setData(raster);

                }
            } else{

                if(LogWriter.isOutput())
                    LogWriter.writeLog("Converting data to sRGB ");

                //expand out 4 bit raster as does not appear to be easy way
                if(d==4){
                    int origSize=data.length;
                    int newSize=w*h*3;

                    byte[] newData=new byte[newSize];
                    byte rawByte;
                    int ptr=0,currentLine=0;
                    for(int ii=0;ii<origSize;ii++){
                        rawByte=data[ii];

                        currentLine=currentLine+2;
                        newData[ptr]=(byte) (rawByte & 240);
                        if(newData[ptr]==-16)   //fix for white
                            newData[ptr]=(byte)255;
                        ptr++;


                        newData[ptr]=(byte) ((rawByte & 15) <<4);
                        if(newData[ptr]==-16//fix for white
                            newData[ptr]=(byte)255;

                        ptr++;

                        if(ptr==newSize)
                            ii=origSize;
                    }
                    data=newData;

                }

                image =new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);

                data=ImageOps.checkSize(data,w,h,3);

                Raster raster = ColorSpaceConvertor.createInterleavedRaster(data, w, h);
                image.setData(raster);


            }

        } else if (comp == 1) {

            if(LogWriter.isOutput())
                LogWriter.writeLog("comp=1 and d= "+d);

            if(d!=8){

                int newSize=w*h;

                byte[] newData=new byte[newSize];

                //Java needs 8 bit so expand out
                switch(d){

                    case 2:
                        ColorSpaceConvertor.flatten2bpc(w, data, null, false, newSize, newData);

                        break;

                    case 4:
                        ColorSpaceConvertor.flatten4bpc(w, data,newSize, newData);
                        break;

                    default:

                        if(LogWriter.isOutput())
                            LogWriter.writeLog("unknown comp= "+d);
                }

                data=newData;

            }

            /** create an image from the raw data*/
            DataBuffer db = new DataBufferByte(data, data.length);

            int[] bands ={0};
            image =new BufferedImage(w,h,BufferedImage.TYPE_BYTE_GRAY);
            Raster raster =Raster.createInterleavedRaster(db,w,h,w,1,bands,null);
            image.setData(raster);

        } else if(LogWriter.isOutput())
            LogWriter.writeLog("Image "+ cs.getType()+ " not currently supported with components "+ comp);

        return image;
    }

    private BufferedImage addSMaskObject(GenericColorSpace decodeColorData,
                                         byte[] data, String name, int w, int h, PdfObject XObject,
                                         boolean isDCT, boolean isJPX, BufferedImage image,
                                         PdfObject DecodeParms, PdfObject newSMask) throws PdfException {
        {

            BufferedImage smaskImage;

            /**read the stream*/
            byte[] objectData =currentPdfFile.readStream(newSMask,true,true,false, false,false, newSMask.getCacheName(currentPdfFile.getObjectReader()));

            if(objectData!=null){

                boolean ignoreMask=DecodeParms!=null &&  DecodeParms.getInt(PdfDictionary.Colors)!=-1
                        &&  DecodeParms.getInt(PdfDictionary.Predictor)!=15 && decodeColorData.getID()!=ColorSpaces.ICC;

                //special case
                PdfObject maskColorSpace=newSMask.getDictionary(PdfDictionary.ColorSpace);
                if(ignoreMask && (decodeColorData.getID()==ColorSpaces.DeviceRGB || decodeColorData.getID()==ColorSpaces.DeviceCMYK) && maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace)==ColorSpaces.DeviceGray)
                    ignoreMask=false;

                //ignore in this case of blank Smask on jpeg with grayscale
                if(isDCT && maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace)==ColorSpaces.DeviceGray){
                    int len=objectData.length;
                    ignoreMask=true;
                    for(int aa=0;aa<len;aa++){
                        if(objectData[aa]!=-1){
                            ignoreMask=false;
                            aa=len;
                        }
                    }
                }

                //ignoreMask is hack to fix odd Visuality files
                if(!ignoreMask){

                    int rawOptions=optionsApplied;

                    if(optionsApplied==PDFImageProcessing.NOTHING)
                        doNotRotate=true;

                    int maskW=newSMask.getInt(PdfDictionary.Width);
                    int maskH=newSMask.getInt(PdfDictionary.Height);

                    boolean isWhiteAndDownscaled=false;


                    boolean isIndexed=false;
                    if(isWhiteAndDownscaled){

                        PdfObject XObjectColorSpace=XObject.getDictionary(PdfDictionary.ColorSpace);
                        //PdfObject maskColorSpace=newSMask.getDictionary(PdfDictionary.ColorSpace);

                        PdfArrayIterator maskFilters = newSMask.getMixedArray(PdfDictionary.Filter);

                        boolean isJBIG2=false;

                        //only needed for this case
                        if(XObjectColorSpace.getParameterConstant(PdfDictionary.ColorSpace)== ColorSpaces.DeviceRGB){
                            int maskFirstValue;
                            if(maskFilters!=null && maskFilters.hasMoreTokens()){
                                while(maskFilters.hasMoreTokens()){
                                    maskFirstValue=maskFilters.getNextValueAsConstant(true);
                                    isJBIG2=maskFirstValue== PdfFilteredReader.JBIG2Decode;
                                }
                            }
                        }

                        //special case customers3/si_test.pdf
                        isIndexed=data.length==2 && XObjectColorSpace.getParameterConstant(PdfDictionary.ColorSpace)== ColorSpaces.Indexed;

                        isWhiteAndDownscaled=XObjectColorSpace!=null &&
                                ((XObjectColorSpace.getParameterConstant(PdfDictionary.ColorSpace)== ColorSpaces.DeviceRGB && (!isDCT || isJBIG2)) ||
                                        (isIndexed && XObjectColorSpace.getDictionary(PdfDictionary.Indexed).getParameterConstant(PdfDictionary.ColorSpace)== ColorSpaces.DeviceRGB)) &&
                                maskColorSpace.getParameterConstant(PdfDictionary.ColorSpace)== ColorSpaces.DeviceGray;

                    }

                    if((isWhiteAndDownscaled  && (isDCT || isJPX || isIndexed))){

                        //invert and get image
                        int c=objectData.length;
                        for(int ii=0;ii<c;ii++)
                            objectData[ii]= (byte) (((byte)255)-objectData[ii]);

                        image = processImageXObject(newSMask,name, objectData,true,null);

                    }else{


                            smaskImage = processImageXObject(newSMask,name, objectData,true,null);

                            //restore
                            doNotRotate=false;
                            optionsApplied=rawOptions;

                            //apply mask
                            if(smaskImage!=null){
                                image=ImageCommands.applySmask(image, smaskImage, newSMask, false, decodeColorData.getID() == ColorSpaces.DeviceRGB, newSMask.getDictionary(PdfDictionary.ColorSpace),XObject,gs);
                                smaskImage.flush();
                            }

                    }
                }
            }
        }
        return image;
    }
}
TOP

Related Classes of org.jpedal.parser.ImageDecoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.