Package org.jpedal.parser

Source Code of org.jpedal.parser.PdfStreamDecoder

/**
* ===========================================
* Java Pdf Extraction Decoding Access Library
* ===========================================
*
* Project Info:  http://www.jpedal.org
* (C) Copyright 1997-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


  *
  * ---------------
  * PdfStreamDecoder.java
  * ---------------
*/
package org.jpedal.parser;

import org.jpedal.PdfDecoder;
import org.jpedal.color.*;
import org.jpedal.constants.PDFImageProcessing;
import org.jpedal.constants.PageInfo;
import org.jpedal.exception.PdfException;
import org.jpedal.external.*;
import org.jpedal.fonts.PdfFont;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.fonts.glyph.T3Size;
import org.jpedal.images.SamplingFactory;
import org.jpedal.io.*;
import org.jpedal.objects.*;
import org.jpedal.objects.layers.PdfLayerList;
import org.jpedal.objects.raw.*;

import org.jpedal.render.*;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.Matrix;
import org.jpedal.utils.StringUtils;
import org.jpedal.utils.repositories.Vector_Int;
import org.jpedal.utils.repositories.Vector_Object;
import org.jpedal.utils.repositories.Vector_Rectangle;

import java.awt.*;
import java.awt.geom.Area;
import java.awt.image.*;
import java.util.*;

/**
* Contains the code which 'parses' the commands in
* the stream and extracts the data (images and text).
* Users should not need to call it.
*/
public class PdfStreamDecoder extends BaseDecoder{

    protected GraphicsState newGS=null;

    protected byte[] pageStream=null;

    PdfLayerList layers;

    protected boolean getSamplingOnly=false;

    TextState currentTextState = new TextState();

    private Map shadingColorspacesObjects=new HashMap(50);

    /**flag to show if stack setup*/
    private boolean isStackInitialised=false;

    /**stack for graphics states*/
    private Vector_Object graphicsStateStack;

    /**stack for graphics states*/
    private Vector_Object strokeColorStateStack;

    /**stack for graphics states*/
    private Vector_Object nonstrokeColorStateStack;

    /**stack for graphics states*/
    private Vector_Object textStateStack;

    private boolean isTTHintingRequired=false;

    private Vector_Int textDirections=new Vector_Int();

    private Vector_Rectangle textAreas=new Vector_Rectangle();

    /**shows if t3 glyph uses internal colour or current colour*/
    public boolean ignoreColors=false;

    //trap for recursive loop of xform calling itself
    int lastDataPointer=-1;
   
    private T3Decoder t3Decoder=null;

    /**flag to show if we REMOVE shapes*/
    private boolean removeRenderImages=false;

    /**flags to show we need colour data as well*/
    private boolean textColorExtracted=false,colorExtracted=false;

    /**flag to show text is being extracted*/
    private boolean textExtracted=true;

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

    /**
     * if forms flattened, different calculation needed
     */
    private boolean isFlattenedForm=false;
    private float flattenX=0, flattenY=0;

    /**list of images used for display*/
    private String imagesInFile=null;

    //set threshold - value indicates several possible values
    public static float currentThreshold=0.595f;

    private boolean flattenXFormToImage=false;

    private boolean requestTimeout =false;

    private int timeoutInterval=-1;

    protected ImageHandler customImageHandler=null;

    private PdfFontFactory pdfFontFactory;

    private boolean isXMLExtraction=false;


    /**interactive display*/
    private StatusBar statusBar=null;

    private boolean markedContentExtracted=false;

    /**store text data and can be passed out to other classes*/
    private PdfData pdfData = new PdfData();

    /**store image data extracted from pdf*/
    private PdfImageData pdfImages=new PdfImageData();

    /**used to debug*/
    protected static String indent="";

    /**show if possible error in stream data*/
    protected boolean isDataValid=true;

    /**used to store font information from pdf and font functionality*/
    private PdfFont currentFontData;

    /**flag to show we use hi-res images to draw onscreen*/
    protected boolean useHiResImageForDisplay=false;

    protected ObjectStore objectStoreStreamRef;

    private String formName="";

    protected boolean isType3Font;

    public static boolean useTextPrintingForNonEmbeddedFonts=false;

    static{
        SamplingFactory.setDownsampleMode(null);
    }

    public PdfStreamDecoder(PdfObjectReader currentPdfFile){

        init(currentPdfFile);

    }

    /**
     * create new StreamDecoder to create screen display with hires images
     */
    public PdfStreamDecoder(PdfObjectReader currentPdfFile, boolean useHiResImageForDisplay, PdfLayerList layers) {

        if(layers!=null)
            this.layers=layers;


        init(currentPdfFile);
    }

    private void init(PdfObjectReader currentPdfFile) {

        cache=new PdfObjectCache();
        gs=new GraphicsState();
        layerDecoder=new LayerDecoder();
        errorTracker=new ErrorTracker();
        pageData = new PdfPageData();

        StandardFonts.checkLoaded(StandardFonts.STD);

        this.currentPdfFile=currentPdfFile;
        pdfFontFactory =new PdfFontFactory(currentPdfFile);

    }


    /**
     *
     *  objects off the page, stitch into a stream and
     * decode and put into our data object. Could be altered
     * if you just want to read the stream
     * @param pageStream
     * @throws PdfException
     */
    public final T3Size decodePageContent(GraphicsState newGS, byte[] pageStream) throws PdfException{

        this.newGS=newGS;
        this.pageStream=pageStream;

        return decodePageContent(null);
    }

    /**
     *
     *  objects off the page, stitch into a stream and
     * decode and put into our data object. Could be altered
     * if you just want to read the stream
     * @param pdfObject
     * @throws PdfException
     */
    public final T3Size decodePageContent(PdfObject pdfObject) throws PdfException{

        try{

            //check switched off
            imagesProcessedFully=true;

            //reset count
            imageCount=0;

            isTimeout=false;

            layerDecoder.setPdfLayerList(this.layers);

            //reset count
            imagesInFile=null; //also reset here as good point as syncs with font code

            if(!this.renderDirectly && statusBar!=null)
                statusBar.percentageDone=0;

            if(newGS!=null)
                gs = newGS;
            else
                gs = new GraphicsState(0,0);

            //save for later
            if (renderPage){

                /**
                 * check setup and throw exception if null
                 */
                if(current==null)
                    throw new PdfException("DynamicVectorRenderer not setup PdfStreamDecoder setStore(...) should be called");

                current.drawClip(gs,defaultClip, false) ;
            }

            //get the binary data from the file
            byte[] b_data;

            byte[][] pageContents= null;
            if(pdfObject !=null){
                pageContents= pdfObject.getKeyArray(PdfDictionary.Contents);
                isDataValid= pdfObject.streamMayBeCorrupt();
            }

            //get any page grouping obj
            if(pdfObject==null)
                cache.pageGroupingObj=null;
            else
                cache.pageGroupingObj=pdfObject.getDictionary(PdfDictionary.Group);

            if(pdfObject !=null && pageContents==null)
                b_data =currentPdfFile.readStream(pdfObject,true,true,false, false,false, pdfObject.getCacheName(currentPdfFile.getObjectReader()));
            else if(pageStream!=null)
                b_data =pageStream;
            else
                b_data =currentPdfFile.getObjectReader().readPageIntoStream(pdfObject);

            //trap for recursive loop of xform calling itself
            lastDataPointer=-1;

            //if page data found, turn it into a set of commands
            //and decode the stream of commands
            if (b_data!=null && b_data.length > 0)
                decodeStreamIntoObjects(b_data, false);

            //flush fonts
            if(!isType3Font)
                cache.resetFonts();

            T3Size t3=new T3Size();
            if(t3Decoder!=null){
              t3.x = t3Decoder.T3maxWidth;
              t3.y = t3Decoder.T3maxHeight;
              ignoreColors=t3Decoder.ignoreColors;
              t3Decoder=null;
            }

            return t3;


        }catch(Error err){


            if (ExternalHandlers.throwMissingCIDError && err.getMessage().contains("kochi"))
                throw err;

            errorTracker.addPageFailureMessage("Problem decoding page " + err);

        }

        return null;
    }

    /**
     * routine to decode an XForm stream
     */
    public void drawFlattenedForm(PdfObject form) throws  PdfException {

        isFlattenedForm=true;

        //check if this form should be displayed
        boolean[] characteristic = ((FormObject)form).getCharacteristics();
        if (characteristic[0] || characteristic[1] || characteristic[5] ||
                (form.getBoolean(PdfDictionary.Open)==false &&
                        form.getParameterConstant(PdfDictionary.Subtype)==PdfDictionary.Popup)){
            //this form should be hidden
            return;
        }

        PdfObject imgObj = null;
        PdfObject APobjN = form.getDictionary(PdfDictionary.AP).getDictionary(PdfDictionary.N);
        String defaultState = form.getName(PdfDictionary.AS);
        if (defaultState != null && defaultState.equals(((FormObject)form).getNormalOnState())) {
            //use the selected appearance stream
            if(APobjN.getDictionary(PdfDictionary.On) !=null){
                imgObj = APobjN.getDictionary(PdfDictionary.On);
            }else {
                Map otherValues=APobjN.getOtherDictionaries();
                if(otherValues!=null && !otherValues.isEmpty()){
                    Iterator keys=otherValues.keySet().iterator();
                    PdfObject val;
                    String key;
                    while(keys.hasNext()){
                        key=(String)keys.next();
                        val=(PdfObject)otherValues.get(key);
                        imgObj = val;
                    }
                }
            }
        }else {
            //use the normal appearance Stream
            if(APobjN!=null || form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.I) !=null){

                //if we have a root stream then it is the off value
                //check in order of N Off, MK I, then N
                //as N Off overrides others and MK I is in preference to N
                if(APobjN!=null && APobjN.getDictionary(PdfDictionary.Off) !=null){
                    imgObj = APobjN.getDictionary(PdfDictionary.Off);
                }else if(form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.I) !=null
                        && form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.IF)==null){
                    //look here for MK IF
                    //if we have an IF inside the MK then use the MK I as some files shown that this value is there
                    //only when the MK I value is not as important as the AP N.
                    imgObj = form.getDictionary(PdfDictionary.MK).getDictionary(PdfDictionary.I);

                }else if(APobjN!=null && APobjN.getDecodedStream()!=null){
                    imgObj = APobjN;
                }
            }
        }

        if(imgObj==null)
            return;

        currentPdfFile.checkResolved(imgObj);
        byte[] formData=imgObj.getDecodedStream(); //get from the AP
        //debug code for mark, for the flattern case 10295
//        System.out.println("ref="+form.getObjectRefAsString()+" stream="+new String(formData));

        //might be needed to pick up fonts
        PdfObject resources = imgObj.getDictionary(PdfDictionary.Resources);
        readResources(resources,false);

        /**
         * see if bounding box and set
         */
        float[] BBox=form.getFloatArray(PdfDictionary.Rect);

        //if we flatten form objects with XForms, we need to use diff calculation
        if(isFlattenedForm){
            flattenX=BBox[0];
            flattenY=BBox[1];
        }

        //please dont delete through merge this fixes most of the flatten form positionsing.
        float[] matrix=imgObj.getFloatArray(PdfDictionary.Matrix);

        //we need to factor in this to calculations
        int pageRotation=pageData.getRotation(pageNum);

        //System.out.println("pageRot="+pageRotation);
        //System.out.println("BBox="+BBox[0]+" "+BBox[1]+" "+BBox[2]+" "+BBox[3]);
        //System.out.println("matrix="+matrix[0]+" "+matrix[1]+" "+matrix[2]+" "+matrix[3]);
        float x = BBox[0],y = BBox[1];
        Area newClip=null;

        //check for null and then recalculate insets
        if(matrix!=null){
            switch(pageRotation){
                case 90:
                    x = BBox[2];
                    //newClip=new Area(new Rectangle((int)BBox[2],(int)BBox[1],(int)BBox[0],(int)BBox[3]));
                    break;
                default:
                    x = BBox[0]+matrix[4];
                    newClip=new Area(new Rectangle((int)BBox[0],(int)BBox[1],(int)BBox[2],(int)BBox[3]));
                    break;
            }
            y = BBox[1]+matrix[5];

            //set gs.CTM to form coords (probably {1,0,0}{0,1,0}{x,y,1} at a guess
            gs.CTM = new float[][]{{matrix[0],matrix[1],0},{matrix[2],matrix[3],0},{x,y,1}};

        }else{
            gs.CTM = new float[][]{{1,0,0},{0,1,0},{x,y,1}};
            newClip=new Area(new Rectangle((int)BBox[0],(int)BBox[1],(int)BBox[2],(int)BBox[3]));
        }

        //set clip to match bounds on form
        if(newClip!=null)
            gs.updateClip(new Area(newClip));

        current.drawClip(gs, defaultClip,false) ;

        /**decode the stream*/
        setBooleanValue(IsFlattenedForm,isFlattenedForm);
        decodeStreamIntoObjects(formData, false);

        /**
         * we need to reset clip otherwise items drawn afterwards
         * like forms data in image or print will not appear.
         */
        gs.updateClip(null);
        current.drawClip(gs, null,true) ;

    }

    public void setObjectValue(int key, Object  obj){

        switch(key){

            case ValueTypes.Name:
                setName((String) obj);
                break;

            case ValueTypes.DynamicVectorRenderer:
                current=(DynamicVectorRenderer)obj;
                //flag OCR used
                boolean isOCR=(renderMode & PdfDecoder.OCR_PDF)==PdfDecoder.OCR_PDF;
                if(isOCR && current!=null)
                    current.setOCR(true);
                break;

            case ValueTypes.PDFPageData:
                pageData=(PdfPageData)obj;
                //flag if colour info being extracted
                if(textColorExtracted)
                    pdfData.enableTextColorDataExtraction();

                break;

            /**
             * pass in status bar object
             *
             */
            case ValueTypes.StatusBar:
                this.statusBar=(StatusBar)obj;
                break;

            case ValueTypes.PdfLayerList:
                this.layers=(PdfLayerList) obj;
                break;


            case ValueTypes.ImageHandler:
                this.customImageHandler = (ImageHandler)obj;
                if(customImageHandler!=null && current!=null)
                    current.setCustomImageHandler(this.customImageHandler);
                break;

            /**
             * setup stream decoder to render directly to g2
             * (used by image extraction)
             */
            case ValueTypes.DirectRendering:

                this.renderDirectly=true;
                Graphics2D g2 = (Graphics2D)obj;
                this.defaultClip=g2.getClip();

                break;

            /** should be called after constructor or other methods may not work
             * <p>Also initialises DynamicVectorRenderer*/
            case ValueTypes.ObjectStore:
                objectStoreStreamRef = (ObjectStore)obj;

                current=new SwingDisplay(this.pageNum,objectStoreStreamRef,false);
                current.setHiResImageForDisplayMode(useHiResImageForDisplay);

                if(customImageHandler!=null && current!=null)
                    current.setCustomImageHandler(customImageHandler);

                break;

            default:
                super.setObjectValue(key,obj);

        }
    }

    /**
     * flag to show interrupted by user
     */
    private boolean isTimeout=false;

    boolean isPrinting=false;

    /**
     * NOT PART OF API
     * tells software to generate glyph when first rendered not when decoded.
     * Should not need to be called in general usage
     */
    public void setBooleanValue(int key,boolean value) {

        switch(key){


            case IsPrinting:
                isPrinting=value;
                break;

            case ValueTypes.XFormFlattening:
                flattenXFormToImage=value;
                break;

            default:
                super.setBooleanValue(key,value);
        }
    }

    /**/

    /**used internally to allow for colored streams*/
    public void setDefaultColors(PdfPaint strokeCol, PdfPaint nonstrokeCol) {

        gs.strokeColorSpace.setColor(strokeCol);
        gs.nonstrokeColorSpace.setColor(nonstrokeCol);
        gs.setStrokeColor(strokeCol);
        gs.setNonstrokeColor(nonstrokeCol);
    }

    /**return the data*/
    public Object getObjectValue(int key){

        switch(key){
            case ValueTypes.PDFData:
                if (PdfDecoder.embedWidthData)
                    pdfData.widthIsEmbedded();

                // store page width/height so we can translate 270
                // rotation co-ords
                pdfData.maxX = pageData.getMediaBoxWidth(pageNum);
                pdfData.maxY = pageData.getMediaBoxHeight(pageNum);

                return  pdfData;

            case ValueTypes.PDFImages:
                return  pdfImages;

            case ValueTypes.TextAreas:
                return textAreas;

            case ValueTypes.TextDirections:
                return textDirections;

            case ValueTypes.DynamicVectorRenderer:
                return current;

            case PdfDictionary.Font:
                return  pdfFontFactory.getFontsInFile();

            case PdfDictionary.Image:
                return imagesInFile;

            case DecodeStatus.NonEmbeddedCIDFonts:
                return pdfFontFactory.getnonEmbeddedCIDFonts();

            case PageInfo.COLORSPACES:
                return cache.iterator(PdfObjectCache.ColorspacesUsed);

            default:
                if(LogWriter.isOutput())
                    LogWriter.writeLog("Unknown parameter "+key);


        }

        return null;
    }

    /**
     * read page header and extract page metadata
     * @throws PdfException
     */
    public final void readResources(PdfObject Resources,boolean resetList) throws PdfException {

        if(resetList)
            pdfFontFactory.resetfontsInFile();

        cache.readResources(Resources, resetList,currentPdfFile);

    }


    /**
     * decode the actual 'Postscript' stream into text and images by extracting
     * commands and decoding each.
     */
    public String decodeStreamIntoObjects(byte[] stream,boolean returnText) {

        if(stream.length==0)
            return null;

        //start of Dictionary on Inline image
        int startInlineStream=0;

        long startTime=System.currentTimeMillis();

        CommandParser parser=new CommandParser(stream);
        this.parser=parser;

        int streamSize=stream.length,dataPointer = 0,startCommand=0;

        //used in CS to avoid miscaching
        String csInUse="",CSInUse="";

        PdfShape currentDrawShape=new PdfShape();

        //setup textDecoder
        TextDecoder textDecoder;
        if(markedContentExtracted)
            textDecoder= new TextDecoder(layerDecoder);
        else{
            textDecoder= new TextDecoder(pdfData,isXMLExtraction,layerDecoder);
            textDecoder.setReturnText(returnText);
        }

        if(errorTracker!=null)
            textDecoder.setHandlerValue(Options.ErrorTracker, errorTracker);
        textDecoder.setParameters(isPageContent,renderPage, renderMode, extractionMode, isPrinting);
        textDecoder.setFileHandler(currentPdfFile);
        textDecoder.setIntValue(FormLevel, formLevel);
        textDecoder.setIntValue(TextPrint, textPrint);
        textDecoder.setBooleanValue(RenderDirectly, renderDirectly);
        textDecoder.setBooleanValue(GenerateGlyphOnRender, generateGlyphOnRender);
        textDecoder.setRenderer(current);

        if(!renderDirectly && statusBar!=null){
            statusBar.percentageDone=0;
            statusBar.resetStatus("stream");
        }

        /**
         * loop to read stream and decode
         */
        while (true) {

            //allow user to request exit and fail page
            if(requestTimeout || (timeoutInterval!=-1 && System.currentTimeMillis()-startTime >timeoutInterval)){
                requestTimeout =false;
                timeoutInterval=-1;
                isTimeout=true;

                break;
            }

            if(!renderDirectly && statusBar!=null)
                statusBar.percentageDone=(90*dataPointer)/streamSize;

            dataPointer=parser.getCommandValues(dataPointer,streamSize,tokenNumber);
            int commandID=parser.getCommandID();

            //use negative flag to show commands found
            if(dataPointer<0){

                dataPointer=-dataPointer;
                try{

                    /**
                     * call method to handle commands
                     */
                    int commandType=Cmd.getCommandType(commandID);

                    /**text commands first and all other
                     * commands if not found in first
                     **/
                    switch(commandType){

                        case Cmd.TEXT_COMMAND:

                            if((commandID ==Cmd.EMC || layerDecoder.isLayerVisible()) && !getSamplingOnly &&(renderText || textExtracted)){

                                textDecoder.setCommands(parser);
                                textDecoder.setGS(gs);
                                textDecoder.setTextState(currentTextState);
                                textDecoder.setIntValue(TokenNumber, tokenNumber);

                                if(renderPage && commandID ==Cmd.BT){
                                    //save for later and set TR
                                    current.drawClip(gs,defaultClip,true) ;
                                    current.drawTR(GraphicsState.FILL);

                                    //flag text block started
                                    current.flagCommand(Cmd.BT,tokenNumber);
                                }

                                if(commandID ==Cmd.Tj || commandID ==Cmd.TJ || commandID ==Cmd.quote || commandID ==Cmd.doubleQuote){

                                    //flag which TJ command we are on
                                    current.flagCommand(Cmd.Tj,tokenNumber);

                                    if(currentTextState.hasFontChanged()){

                                        //switch to correct font
                                        String fontID=currentTextState.getFontID();
                                        PdfFont restoredFont = resolveFont(fontID);
                                        if(restoredFont!=null){
                                            currentFontData=restoredFont;
                                            current.drawFontBounds(currentFontData.getBoundingBox());
                                        }
                                    }

                                    if(currentFontData==null)
                                        currentFontData=new PdfFont(currentPdfFile);

                                    if(currentTextState.hasFontChanged()){
                                        currentTextState.setFontChanged(false);
                                    }

                                    textDecoder.setFont(currentFontData);
                                }

                                dataPointer =textDecoder.processToken(currentTextState, commandID, startCommand, dataPointer);

                            }
                            break;

                        case Cmd.SHAPE_COMMAND:

                            if(!getSamplingOnly){
                                switch(commandID){

                                    case Cmd.B :
                                        if(!removeRenderImages){
                                            Shape currentShape= ShapeCommands.B(false, false, gs, formLevel, currentDrawShape, layerDecoder, renderPage, current);
                                        }
                                        break;

                                    case Cmd.b :
                                        if(!removeRenderImages){
                                            Shape currentShape= ShapeCommands.B(false, true, gs, formLevel, currentDrawShape, layerDecoder, renderPage, current);
                                        }
                                        break;

                                    case Cmd.bstar :
                                        if(!removeRenderImages){
                                            Shape currentShape= ShapeCommands.B(true, true, gs, formLevel, currentDrawShape, layerDecoder, renderPage, current);
                                        }
                                        break;

                                    case Cmd.Bstar :
                                        if(!removeRenderImages){
                                            Shape currentShape= ShapeCommands.B(true, false, gs, formLevel, currentDrawShape, layerDecoder, renderPage, current);
                                        }
                                        break;

                                    case Cmd.c :
                                        float x3 =parser.parseFloat(1);
                                        float y3 = parser.parseFloat(0);
                                        float x2 =parser.parseFloat(3);
                                        float y2 = parser.parseFloat(2);
                                        float x = parser.parseFloat(5);
                                        float y = parser.parseFloat(4);
                                        currentDrawShape.addBezierCurveC(x, y, x2, y2, x3, y3);
                                        break;

                                    case Cmd.d :
                                        ShapeCommands.D(parser, gs);
                                        break;

                                    case Cmd.F :
                                        if(!removeRenderImages)
                                            F(false, formLevel, currentDrawShape);
                                        break;

                                    case Cmd.f :
                                        if(!removeRenderImages)
                                            F(false,formLevel, currentDrawShape);
                                        break;

                                    case Cmd.Fstar :
                                        if(!removeRenderImages)
                                            F(true,formLevel, currentDrawShape);
                                        break;

                                    case Cmd.fstar :
                                        if(!removeRenderImages)
                                            F(true,formLevel, currentDrawShape);
                                        break;

                                    case Cmd.h :
                                        currentDrawShape.closeShape();
                                        break;

                                    //case Cmd.i:
                                    //  I();
                                    //break;

                                    case Cmd.J :
                                        ShapeCommands.J(false, parser.parseInt(0), gs);
                                        break;

                                    case Cmd.j :
                                        ShapeCommands.J(true, parser.parseInt(0), gs);
                                        break;

                                    case Cmd.l :
                                        currentDrawShape.lineTo(parser.parseFloat(1),parser.parseFloat(0));
                                        break;

                                    case Cmd.M:
                                        gs.setMitreLimit((int) (parser.parseFloat(0)));
                                        break;

                                    case Cmd.m :
                                        currentDrawShape.moveTo(parser.parseFloat(1),parser.parseFloat(0));
                                        break;

                                    case Cmd.n :
                                        ShapeCommands.N(currentDrawShape, gs, formLevel, defaultClip, renderPage, current, pageData, pageNum);
                                        break;

                                    case Cmd.re :
                                        currentDrawShape.appendRectangle(parser.parseFloat(3),parser.parseFloat(2),parser.parseFloat(1),parser.parseFloat(0));
                                        break;

                                    case Cmd.S :
                                        if(!removeRenderImages){
                                            Shape currentShape= ShapeCommands.S(false, layerDecoder, gs, currentDrawShape, current, renderPage);
                                        }
                                        break;
                                    case Cmd.s :
                                        if(!removeRenderImages){
                                            Shape currentShape= ShapeCommands.S(true, layerDecoder, gs, currentDrawShape, current, renderPage);
                                        }
                                        break;

                                    case Cmd.v :
                                        currentDrawShape.addBezierCurveV(parser.parseFloat(3),parser.parseFloat(2),parser.parseFloat(1),parser.parseFloat(0));
                                        break;

                                    case Cmd.w:
                                        gs.setLineWidth(parser.parseFloat(0));
                                        break;

                                    case Cmd.Wstar : //set Winding rule
                                        currentDrawShape.setEVENODDWindingRule();
                                        currentDrawShape.setClip(true);
                                        break;

                                    case Cmd.W :
                                        currentDrawShape.setNONZEROWindingRule();
                                        currentDrawShape.setClip(true);
                                        break;

                                    case Cmd.y :
                                        currentDrawShape.addBezierCurveY(parser.parseFloat(3),parser.parseFloat(2),parser.parseFloat(1),parser.parseFloat(0));
                                        break;
                                }
                            }

                            break;

                        case Cmd.SHADING_COMMAND:

                            if(!getSamplingOnly && (renderPage || textColorExtracted || colorExtracted)){

                                if (renderPage){
                                    ShadingCommands.sh(parser.generateOpAsString(0, true), cache, gs,
                                            isPrinting, shadingColorspacesObjects, pageNum, currentPdfFile,
                                            pageData, current);
                                }
                            }

                            break;

                        case Cmd.COLOR_COMMAND:

                            if(!getSamplingOnly && (renderPage || textColorExtracted || colorExtracted)){
                                if(commandID!=Cmd.SCN && commandID!=Cmd.scn && commandID!=Cmd.SC && commandID!=Cmd.sc)
                                    current.resetOnColorspaceChange();

                                switch(commandID){

                                    case Cmd.cs :
                                    {
                                        String colorspaceObject=parser.generateOpAsString(0, true);
                                        boolean isLowerCase=true;
                                        //ensure if used for both Cs and cs simultaneously we only cache one version and do not overwrite
                                        boolean alreadyUsed=(!isLowerCase && colorspaceObject.equals(csInUse))||(isLowerCase && colorspaceObject.equals(CSInUse));

                                        if(isLowerCase)
                                            csInUse=colorspaceObject;
                                        else
                                            CSInUse=colorspaceObject;

                                        ColorCommands.CS(isLowerCase, colorspaceObject, gs, cache, currentPdfFile, isPrinting, pageNum, pageData, alreadyUsed);
                                        break;
                                    }
                                    case Cmd.CS :

                                        String colorspaceObject=parser.generateOpAsString(0, true);
                                        boolean isLowerCase=false;
                                        //ensure if used for both Cs and cs simultaneously we only cache one version and do not overwrite
                                        boolean alreadyUsed=(!isLowerCase && colorspaceObject.equals(csInUse))||(isLowerCase && colorspaceObject.equals(CSInUse));

                                        if(isLowerCase)
                                            csInUse=colorspaceObject;
                                        else
                                            CSInUse=colorspaceObject;

                                        ColorCommands.CS(isLowerCase, colorspaceObject, gs, cache, currentPdfFile, isPrinting, pageNum, pageData, alreadyUsed);
                                        break;

                                    case Cmd.rg :
                                        ColorCommands.RG(true, gs, parser, cache);
                                        break;

                                    case Cmd.RG :
                                        ColorCommands.RG(false, gs, parser, cache);
                                        break;

                                    case Cmd.SCN :
                                        ColorCommands.SCN(false, gs, parser, cache);
                                        break;

                                    case Cmd.scn :
                                        ColorCommands.SCN(true, gs, parser, cache);
                                        break;

                                    case Cmd.SC :
                                        ColorCommands.SCN(false, gs, parser, cache);
                                        break;

                                    case Cmd.sc :
                                        ColorCommands.SCN(true, gs, parser, cache);
                                        break;

                                    case Cmd.g :
                                        ColorCommands.G(true, gs, parser, cache);
                                        break;

                                    case Cmd.G :
                                        ColorCommands.G(false, gs, parser, cache);
                                        break;

                                    case Cmd.k :
                                        ColorCommands.K(true, gs, parser, cache);
                                        break;

                                    case Cmd.K :
                                        ColorCommands.K(false, gs, parser, cache);
                                        break;

                                }

                            }

                            break;

                        case Cmd.GS_COMMAND:

                            switch(commandID){

                                case Cmd.cm :

                                    CM(gs,parser);
                                    break;

                                case Cmd.q :
                                    gs = Q(gs,true);
                                    break;

                                case Cmd.Q :
                                    gs = Q(gs, false);
                                    break;

                                case Cmd.gs :
                                    if(!getSamplingOnly){
                                        gs.setMode((PdfObject) cache.GraphicsStates.get(parser.generateOpAsString(0, true)));

                                        current.setGraphicsState(GraphicsState.FILL,gs.getAlpha(GraphicsState.FILL));
                                        current.setGraphicsState(GraphicsState.STROKE,gs.getAlpha(GraphicsState.STROKE));
                                    }

                                    break;

                            }

                            //may have changed so read back and reset
                            gs.setTextState(currentTextState);
                            if(commandID ==Cmd.cm && textDecoder!=null)
                                textDecoder.reset();

                            break;

                        case Cmd.IMAGE_COMMAND:

                            if(commandID==Cmd.BI){
                                startInlineStream= dataPointer;
                            }else{

                                ImageDecoder imageDecoder;
                                PdfObject XObject=null;
                                int subtype=1;
                                if(commandID==Cmd.Do){

                                    String name=parser.generateOpAsString(0, true);
                                    byte[] rawData=null;

                                    XObject = cache.getXObjects(name);
                                    if (XObject != null){

                                        rawData=XObject.getUnresolvedData();

                                        currentPdfFile.checkResolved(XObject);

                                       subtype = XObject.getParameterConstant(PdfDictionary.Subtype);
                                    }

                                    if (subtype == PdfDictionary.Form){

                                      if(formLevel>10 &&  dataPointer==lastDataPointer){
                                        //catch for odd files like customers-June2011/results.pdf
                                      }else{
                                          lastDataPointer=dataPointer; 
                                         
                                            processXForm(dataPointer, XObject, defaultClip,parser);

                                            //if lots of objects in play turn back to ref to save memory
                                            if(rawData!=null && cache.getXObjectCount()>30){
                                                String ref=XObject.getObjectRefAsString();

                                                cache.resetXObject(name,ref,rawData);
                                                XObject=null;

                                            }
                                        }
                                    }
                                }

                                if (subtype != PdfDictionary.Form){

                                    imageDecoder= new ImageDecoder(customImageHandler,objectStoreStreamRef, renderDirectly,pdfImages, formLevel,pageData,imagesInFile,formName);

                                    imageDecoder.setIntValue(PageNumber, pageNum);
                                    imageDecoder.setIntValue(FormLevel, formLevel);
                                    imageDecoder.setHandlerValue(Options.ErrorTracker, errorTracker);
                                    imageDecoder.setRes(cache);
                                    imageDecoder.setGS(gs);
                                    imageDecoder.setSamplingOnly(getSamplingOnly);
                                    imageDecoder.setIntValue(ValueTypes.StreamType, streamType);
                                    imageDecoder.setName(fileName);
                                    imageDecoder.setFloatValue(Multiplier, multiplyer);
                                    imageDecoder.setFloatValue(SamplingUsed, samplingUsed);
                                    imageDecoder.setFileHandler(currentPdfFile);
                                    imageDecoder.setRenderer(current);
                                    imageDecoder.setIntValue(IsImage,imageStatus);

                                    imageDecoder.setParameters(isPageContent,renderPage, renderMode, extractionMode, isPrinting,isType3Font,useHiResImageForDisplay);

                                    imageDecoder.setIntValue(ImageCount,imageCount);

                                    if(commandID==Cmd.Do){
                                        if(XObject==null || !layerDecoder.isLayerVisible() || (layers!=null && !layers.isVisible(XObject)))
                                            ;//ignore
                                        else
                                        dataPointer =imageDecoder.processDOImage(parser.generateOpAsString(0, true), dataPointer,XObject);
                                    }else if(layerDecoder.isLayerVisible())
                                        dataPointer =imageDecoder.processIDImage(dataPointer,startInlineStream,parser.getStream(),tokenNumber);

                                    samplingUsed=imageDecoder.getFloatValue(SamplingUsed);

                                    imageCount=imageDecoder.getIntValue(ImageCount);

                                    imagesInFile=imageDecoder.getImagesInFile();

                                    if(imageDecoder.getBooleanValue(HasYCCKimages))
                                        hasYCCKimages=true;

                                    if(imageDecoder.getBooleanValue(ImagesProcessedFully))
                                        imagesProcessedFully=true;
                                }
                            }
                            break;

                        case Cmd.T3_COMMAND:

                            if(!getSamplingOnly &&(renderText || textExtracted)) {

                              if(t3Decoder==null)
                                t3Decoder=new T3Decoder();

                                t3Decoder.setCommands(parser);
                                t3Decoder.setCommands(parser);
                              t3Decoder.processToken(commandID);
                              
                            }
                            break;
                    }
                } catch (Exception e) {


                    if(LogWriter.isOutput())
                        LogWriter.writeLog("[PDF] "+ e+" Processing token >" + Cmd.getCommandAsString(commandID)+ "<>" + fileName+" <"+pageNum);

                    //only exit if no issue with stream
                    if(isDataValid){
                    }else
                        dataPointer=streamSize;

                } catch (OutOfMemoryError ee) {
                    errorTracker.addPageFailureMessage("Memory error decoding token stream");

                    if(LogWriter.isOutput())
                        LogWriter.writeLog("[MEMORY] Memory error - trying to recover");
                }

                //save for next command
                startCommand=dataPointer;

                //reset array of trailing values
                parser.reset();

                //increase pointer
                tokenNumber++;
            }

            //break at end
            if (streamSize <= dataPointer)
                break;
        }

        if(!renderDirectly && statusBar!=null)
            statusBar.percentageDone=100;

        //pick up TextDecoder values
        isTTHintingRequired=textDecoder.isTTHintingRequired();
        textAreas= (Vector_Rectangle) textDecoder.getObjectValue(ValueTypes.TextAreas);
        textDirections= (Vector_Int) textDecoder.getObjectValue(ValueTypes.TextDirections);

            return "";
    }

    /**
     * decode or get font
     * @param fontID
     */
    private PdfFont resolveFont(String fontID) {

        PdfFont restoredFont=(PdfFont) cache.resolvedFonts.get(fontID);

        //check it was decoded
        if(restoredFont==null){

            String ref=(String)cache.unresolvedFonts.get(fontID);
            PdfObject newFont;

            if(ref!=null){

                //remove from list
                cache.unresolvedFonts.remove(fontID);

                newFont=new FontObject(ref);
                newFont.setStatus(PdfObject.UNDECODED_REF);
                newFont.setUnresolvedData(StringUtils.toBytes(ref), PdfDictionary.Font);
                currentPdfFile.checkResolved(newFont);

                //currentPdfFile.readObject(newFont);
            }else{
                newFont=(PdfObject)cache.directFonts.get(fontID);

                //remove from list
                if(newFont!=null)
                    cache.directFonts.remove(fontID);
            }

            if(newFont!=null){
                try {
                    restoredFont = pdfFontFactory.createFont(newFont, fontID,  objectStoreStreamRef, renderPage, errorTracker,isPrinting);

                    //<start-pro>
                    /**
                    //<end-pro>
                   
                    //<start-std>

                    String fontName=restoredFont.getFontName();

                    int mode=current.getValue(org.jpedal.render.output.html.HTMLDisplay.FontMode);
                    if(current.getType()== org.jpedal.render.output.html.HTMLDisplay.CREATE_HTML &&
                            (mode== org.jpedal.examples.html.HTMLFontMapper.EMBED_ALL ||
                            (mode== org.jpedal.examples.html.HTMLFontMapper.EMBED_ALL_EXCEPT_BASE_FAMILIES &&
                                    !StandardFonts.isStandardFont(restoredFont.getFontName(),true) &&
                                    !fontName.contains("Arial")))){ //check for base fonts (explict Arial test for ArialMT)

                        PdfObject pdfFontDescriptor=newFont.getDictionary(PdfDictionary.FontDescriptor);

                        //if null check to see if it is a CIF font and get data from DescendantFonts obj
                        if (pdfFontDescriptor== null ) {
                            PdfObject Descendent=newFont.getDictionary(PdfDictionary.DescendantFonts);
                            if(Descendent!=null)
                                pdfFontDescriptor=Descendent.getDictionary(PdfDictionary.FontDescriptor);
                        }

                        //write out any embedded font file data

                        if (pdfFontDescriptor!= null ) {

                            byte[] stream;
                            PdfObject FontFile2=pdfFontDescriptor.getDictionary(PdfDictionary.FontFile2);

                            if(FontFile2!=null){ //truetype fonts
                                stream=currentPdfFile.readStream(FontFile2,true,true,false, false,false, FontFile2.getCacheName(currentPdfFile.getObjectReader()));
                                current.writeCustom(org.jpedal.render.output.html.HTMLDisplay.SAVE_EMBEDDED_FONT, new Object[]{restoredFont,stream,"ttf",fontID});
                            }else{
                                PdfObject FontFile3=pdfFontDescriptor.getDictionary(PdfDictionary.FontFile3);
                                if(FontFile3!=null){ //type1 fonts
                                    restoredFont.getGlyphData().setRenderer(current);
                                    stream=currentPdfFile.readStream(FontFile3,true,true,false, false,false, FontFile3.getCacheName(currentPdfFile.getObjectReader()));
                                    current.writeCustom(org.jpedal.render.output.html.HTMLDisplay.SAVE_EMBEDDED_FONT, new Object[]{restoredFont,stream,"cff",fontID});
                                }else{

                                    PdfObject FontFile=pdfFontDescriptor.getDictionary(PdfDictionary.FontFile);
                                    if(FontFile!=null){ //type1 fonts
                                        restoredFont.getGlyphData().setRenderer(current);
                                        stream=currentPdfFile.readStream(FontFile,true,true,false, false,false, FontFile.getCacheName(currentPdfFile.getObjectReader()));
                                        current.writeCustom(org.jpedal.render.output.html.HTMLDisplay.SAVE_EMBEDDED_FONT, new Object[]{restoredFont,stream,"t1",fontID});
                                    }
                                }
                            }
                        }
                    }
                   
                    //<end-std>
                    /**/
                } catch (PdfException e) {
                    e.printStackTrace();
                }
            }

            //store
            if(restoredFont!=null)
                cache.resolvedFonts.put(fontID,restoredFont);

        }

        return restoredFont;
    }

    /**
     return boolean flags with appropriate ket
     */
    public boolean getBooleanValue(int key) {

        switch(key){

            case ValueTypes.EmbeddedFonts:
                return pdfFontFactory.hasEmbeddedFonts();


            case DecodeStatus.PageDecodingSuccessful:
                return errorTracker.pageSuccessful;

            case DecodeStatus.NonEmbeddedCIDFonts:
                return pdfFontFactory.hasNonEmbeddedCIDFonts();

            case DecodeStatus.ImagesProcessed:
                return imagesProcessedFully;

            case DecodeStatus.YCCKImages:
                return hasYCCKimages;

            case DecodeStatus.Timeout:
                return isTimeout;

            case DecodeStatus.TTHintingRequired:
                return isTTHintingRequired;

            default:
                throw new RuntimeException("Unknown value "+key);
        }
    }

    public void dispose() {

        if(pdfData!=null)
            this.pdfData.dispose();

        //this.pageLines=null;

    }
    /**
     private class TestShapeTracker implements ShapeTracker {
     public void addShape(int tokenNumber, int type, Shape currentShape, PdfPaint nonstrokecolor, PdfPaint strokecolor) {

     //use this to see type
     //Cmd.getCommandAsString(type);

     //print out details
     if(type==Cmd.S || type==Cmd.s){ //use stroke color to draw line
     System.out.println("-------Stroke-------PDF cmd="+Cmd.getCommandAsString(type));
     System.out.println("tokenNumber="+tokenNumber+" "+currentShape.getBounds()+" stroke color="+strokecolor);

     }else if(type==Cmd.F || type==Cmd.f || type==Cmd.Fstar || type==Cmd.fstar){ //uses fill color to fill shape
     System.out.println("-------Fill-------PDF cmd="+Cmd.getCommandAsString(type));
     System.out.println("tokenNumber="+tokenNumber+" "+currentShape.getBounds()+" fill color="+nonstrokecolor);

     }else{ //not yet implemented (probably B which is S and F combo)
     System.out.println("Not yet added");
     System.out.println("tokenNumber="+tokenNumber+" "+currentShape.getBounds()+" type="+type+" "+Cmd.getCommandAsString(type));
     }
     }
     }

     /**
     * request exit from main loop
     */
    public void reqestTimeout(Object value) {

        if(value==null)
            requestTimeout =true;
        else if(value instanceof Integer){
            timeoutInterval= (Integer) value;
        }
    }

    public void setIntValue(int key, int value) {

        switch(key){

            /**
             * currentPage number
             */
            case ValueTypes.PageNum:
                this.pageNum=value;
                break;

            /**
             * tells program to try and use Java's font printing if possible
             * as work around for issue with PCL printing
             */
            case TextPrint:
                this.textPrint = value;
                break;

            default:
                super.setIntValue(key,value);
        }
    }

    public void setXMLExtraction( boolean isXMLExtraction){
        this.isXMLExtraction=isXMLExtraction;
    }

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

        super.setParameters(isPageContent,renderPage, renderMode, extractionMode);

        /**
         * flags
         */

        renderText=renderPage &&(renderMode & PdfDecoder.RENDERTEXT) == PdfDecoder.RENDERTEXT;

        textExtracted=(extractionMode & PdfDecoder.TEXT)==PdfDecoder.TEXT;

        textColorExtracted=(extractionMode & PdfDecoder.TEXTCOLOR) == PdfDecoder.TEXTCOLOR;

        colorExtracted=(extractionMode & PdfDecoder.COLOR) == PdfDecoder.COLOR;

        removeRenderImages=renderPage &&(renderMode & PdfDecoder.REMOVE_RENDERSHAPES )== PdfDecoder.REMOVE_RENDERSHAPES;

    }

    /**
     * recursive subroutine so in actual body of PdfStreamDecoder so it can recall decodeStream
     * @param dataPointer
     */
    private void processXForm(int dataPointer, PdfObject XObject, Shape defaultClip, CommandParser parser) throws PdfException {

        final boolean debug=false;

        if(debug)
            System.out.println("processImage "+dataPointer+ ' ' +XObject.getObjectRefAsString()+ ' ' +defaultClip);

        if(!layerDecoder.isLayerVisible() || (layers!=null && !layers.isVisible(XObject)) || XObject==null)
            return;

        String oldFormName=formName;

        String name=parser.generateOpAsString(0, true);

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

        //string to hold image details
        String details=name;

        try {

            if(ImageCommands.trackImages){
                //add details to string so we can pass back
                if(imagesInFile==null)
                    imagesInFile=details+" Form";
                else
                    imagesInFile=details+" Form\n"+ imagesInFile;
            }

            //reset operand
            parser.reset();

            //read stream for image
            byte[] objectData = currentPdfFile.readStream(XObject, true, true, false, false, false, XObject.getCacheName(currentPdfFile.getObjectReader()));
            if (objectData != null) {

                String oldIndent= PdfStreamDecoder.indent;
                PdfStreamDecoder.indent= PdfStreamDecoder.indent+"   ";

                //set value and see if Transform matrix
                float[] transformMatrix=new float[6];
                float[] matrix= XObject.getFloatArray(PdfDictionary.Matrix);
                boolean isIdentity=matrix==null || isIdentity(matrix);
                if(matrix!=null)
                    transformMatrix=matrix;

                float[][] CTM, oldCTM=null;

                //allow for stroke line width being altered by scaling
                float lineWidthInForm=-1; //negative values not used

                if(matrix!=null && !isIdentity) {

                    //save current
                    float[][] currentCTM=new float[3][3];
                    for(int i=0;i<3;i++)
                        System.arraycopy(gs.CTM[i], 0, currentCTM[i], 0, 3);

                    oldCTM = currentCTM;

                    CTM= gs.CTM;

                    float[][] scaleFactor={{transformMatrix[0],transformMatrix[1],0},
                            {transformMatrix[2],transformMatrix[3],0},
                            {transformMatrix[4],transformMatrix[5],1}};

                    scaleFactor= Matrix.multiply(scaleFactor, CTM);
                    gs.CTM=scaleFactor;

                    //work out line width
                    lineWidthInForm=transformMatrix[0]* gs.getLineWidth();

                    if (lineWidthInForm == 0)
                        lineWidthInForm=transformMatrix[1]* gs.getLineWidth();

                    if(lineWidthInForm<0)
                        lineWidthInForm=-lineWidthInForm;

                    if(debug)
                        System.out.println("setMatrix "+gs.CTM[0][0]+ ' ' +gs.CTM[0][1]+ ' ' +gs.CTM[1][0]+ ' ' +gs.CTM[1][1]+ ' ' +gs.CTM[2][0]+ ' ' +gs.CTM[2][1]);
                }

                //track depth
                formLevel++;

                //track name so we can make unique key for image name
                if(formLevel==1)
                    formName= name;
                else
                    formName= formName+'_'+ name;

                //preserve colorspaces
                GenericColorSpace mainStrokeColorData=(GenericColorSpace) gs.strokeColorSpace.clone();
                GenericColorSpace mainnonStrokeColorData=(GenericColorSpace) gs.nonstrokeColorSpace.clone();

                //set form line width if appropriate
                if(lineWidthInForm>0)
                    gs.setLineWidth(lineWidthInForm);

                //set gs max to current so child gs values can not exceed
                float maxStrokeValue= gs.getAlphaMax(GraphicsState.STROKE);
                float maxFillValue= gs.getAlphaMax(GraphicsState.FILL);
                gs.setMaxAlpha(GraphicsState.STROKE, gs.getAlpha(GraphicsState.STROKE));

                if(formLevel==1)
                    gs.setMaxAlpha(GraphicsState.FILL, gs.getAlpha(GraphicsState.FILL));

                //make a copy s owe can restore to original state
                //we need to pass in and then undo any changes at end
                PdfObjectCache mainCache = cache.copy();   //setup cache
                cache.reset(mainCache);   //copy in data

                /**read any resources*/
                PdfObject Resources= XObject.getDictionary(PdfDictionary.Resources);
                readResources(Resources, false);

                /**read any resources*/
                cache.groupObj= XObject.getDictionary(PdfDictionary.Group);
                currentPdfFile.checkResolved(cache.groupObj);

                /**
                 * see if bounding box and set
                 */
                float[] BBox= XObject.getFloatArray(PdfDictionary.BBox);
                Area clip=null;
                boolean clipChanged=false;

               
                //this code breaks customers-june2011/169351.pdf so added as possible fix
                if(BBox!=null && BBox[2]>1 && BBox[3]>1 && gs.getClippingShape()==null && gs.CTM[0][1]==0 && gs.CTM[1][0]==0 && gs.CTM[2][1]!=0 && gs.CTM[2][0]<0){
                    if(debug)
                        System.out.println("setClip");

                    clip = setClip(defaultClip, BBox);
                    clipChanged=true;
                   
                   
                    //System.out.println(BBox[0]+" "+BBox[1]+" "+BBox[2]+" "+BBox[3]);
                    //Matrix.show(gs.CTM);
                }
                else if(BBox!=null && BBox[0]==0 && BBox[1]==0 && BBox[2]>1 && BBox[3]>1   && (gs.CTM[0][0]>0.99 || gs.CTM[2][1]<-1) && (gs.CTM[2][0]<-1 || gs.CTM[2][0]>1) && gs.CTM[2][1]!=0 ){//)  && BBox[2]>1 && BBox[3]>1 ){//if(BBox!=null && matrix==null && BBox[0]==0 && BBox[1]==0){

                  if(debug)
                        System.out.println("setClip");

                    clip = setClip(defaultClip, BBox);
                    clipChanged=true;
              }
              
                /**decode the stream*/
                if(objectData.length>0){

                    PdfObject newSMask = getSMask(BBox); //check for soft mask we need to apply
                    int firstValue = getFirstValue(gs.getBM()); //see if multiply transparency

                    //isTransparent sees if this case happens (randomHouse/9781580082778_DistX.pdf)
                    //added formLevel to try and fix customer issue on customers3/Auktionsauftrag_45.9.33620.pdf (including printing)
                    //if(!isTransparent(cache.groupObj) &&
                   //         (flattenXFormToImage || (isPrinting && (gs.CTM[2][0]==0) && gs.CTM[2][1]==0 && newSMask==null && firstValue!=PdfDictionary.Multiply && gs.getAlpha(GraphicsState.FILL)==1) ||
                   //         ((gs.getAlpha(GraphicsState.FILL)==1 || gs.SMask!=null || layerDecoder.layerLevel>0 || (formLevel==1 && gs.getAlpha(GraphicsState.FILL)<0.1f))))){ //use if looks like marked text block
                     if(!isTransparent(cache.groupObj) && (flattenXFormToImage || (isPrinting && (gs.CTM[2][0]==0) && gs.CTM[2][1]==0 && newSMask==null && firstValue!=PdfDictionary.Multiply && gs.getAlpha(GraphicsState.FILL)==1) ||
                                ((gs.getAlpha(GraphicsState.FILL)==1 || layerDecoder.layerLevel>0 || (formLevel==1 && gs.getAlpha(GraphicsState.FILL)<0.1f)) && newSMask==null && firstValue!=PdfDictionary.Multiply))){ //use if looks like marked text block

                        if(debug)
                            System.out.println("decode");

                        decodeStreamIntoObjects(objectData, false);

                    }else if(newSMask!=null || firstValue==PdfDictionary.Multiply){ //if an smask render to image and apply Smask to it - then write out as image

                        if(debug)
                            System.out.println("createMaskForm");

                        createMaskForm(XObject, name, newSMask, firstValue);

                    }else{

                        if(debug)
                            System.out.println("other");

                        //save renderer
                        DynamicVectorRenderer oldCurrent=current;
                        current=new SwingDisplay(pageNum, objectStoreStreamRef,false);
                        current.setHiResImageForDisplayMode(useHiResImageForDisplay);

                        boolean oldRenderDirectly= renderDirectly;

                        //to draw image we need to use local 1
                        float strokeAlpha= gs.getAlpha(GraphicsState.STROKE);
                        float maxStroke= gs.getAlphaMax(GraphicsState.STROKE);
                        float fillAlpha= gs.getAlpha(GraphicsState.FILL);
                        float maxFill= gs.getAlphaMax(GraphicsState.FILL);

                        currentPdfFile.checkResolved(cache.pageGroupingObj);

                        if(cache.pageGroupingObj!=null && renderDirectly){
                            gs.setMaxAlpha(GraphicsState.STROKE,1);


                            //needed for /PDFdata/baseline_screens/customers1/Milkshake BusyTime DistX.pdf
                           // if(cache.pageGroupingObj.getDictionary(PdfDictionary.ColorSpace).getParameterConstant(PdfDictionary.ColorSpace)!=ColorSpaces.DeviceCMYK){
                            //   gs.setMaxAlpha(GraphicsState.FILL,1);
                           // }
                        }else{
                            gs.setMaxAlpha(GraphicsState.STROKE,strokeAlpha);
                            gs.setMaxAlpha(GraphicsState.FILL,fillAlpha);
                        }

                        if(renderDirectly && (cache.pageGroupingObj==null ||
                                (cache.pageGroupingObj!=null && cache.groupObj!=null &&
                                        (cache.groupObj.getDictionary(PdfDictionary.ColorSpace).getParameterConstant(PdfDictionary.ColorSpace)==ColorSpaces.ICC ||
                                        cache.groupObj.getDictionary(PdfDictionary.ColorSpace).getParameterConstant(PdfDictionary.ColorSpace)!=cache.pageGroupingObj.getDictionary(PdfDictionary.ColorSpace).getParameterConstant(PdfDictionary.ColorSpace)))))
                            gs.setMaxAlpha(GraphicsState.FILL,1);

                        if(!renderDirectly){
                            gs.setAlpha(GraphicsState.STROKE,1);
                            gs.setAlpha(GraphicsState.FILL,1);
                        }

                        renderDirectly=false;

                        //ensure drawn
                        oldCurrent.setGraphicsState(GraphicsState.STROKE,1f);
                        oldCurrent.setGraphicsState(GraphicsState.FILL,1f);


                        decodeStreamIntoObjects(objectData, false);

                        gs.setMaxAlpha(GraphicsState.STROKE,maxStroke);
                        gs.setMaxAlpha(GraphicsState.FILL,maxFill);

                        if(!renderDirectly){
                            gs.setAlpha(GraphicsState.STROKE,strokeAlpha);
                            gs.setAlpha(GraphicsState.FILL,fillAlpha);
                        }

                        oldCurrent.drawXForm(current, gs);

                        //restore
                        current=oldCurrent;
                        current.setGraphicsState(GraphicsState.STROKE,strokeAlpha);
                        current.setGraphicsState(GraphicsState.FILL,fillAlpha);

                        renderDirectly=oldRenderDirectly;
                    }
                }
                //restore clip if changed
                if(clipChanged){
                    gs.setClippingShape(clip);
                    current.drawClip(gs,clip,false) ;
                }

                //restore settings
                formLevel--;

                //restore old matrix
                if(oldCTM!=null)
                    gs.CTM=oldCTM;

                /**restore old colorspace and fonts*/
                gs.strokeColorSpace=mainStrokeColorData;
                gs.nonstrokeColorSpace=mainnonStrokeColorData;

                //put back original state
                cache.restore(mainCache);

                //restore gs max to current so child gs values can not exceed
                gs.setMaxAlpha(GraphicsState.STROKE, maxStrokeValue);
                gs.setMaxAlpha(GraphicsState.FILL, maxFillValue);

                PdfStreamDecoder.indent=oldIndent;
            }

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

            if (ExternalHandlers.throwMissingCIDError && e.getMessage().contains("kochi"))
                throw e;
        } catch (Exception e) {

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

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

        formName=oldFormName;

    }

    private PdfObject getSMask(float[] BBox) {
        PdfObject newSMask=null;

        //ignore if none
        if(gs.SMask!=null && gs.SMask.getGeneralType(PdfDictionary.SMask)==PdfDictionary.None){

            return null;
        }

        if(gs.SMask!=null && BBox!=null && BBox[2]>0 ){ //see if SMask to cache to image & stop negative cases such as Milkshake StckBook Activity disX.pdf
            if(gs.SMask.getParameterConstant(PdfDictionary.Type)!=PdfDictionary.Mask || gs.SMask.getFloatArray(PdfDictionary.BC)!=null){ //fix for waves file
                newSMask= gs.SMask.getDictionary(PdfDictionary.G);
                currentPdfFile.checkResolved(newSMask);
            }
        }
        return newSMask;
    }

    private static int getFirstValue(PdfArrayIterator BMvalue) {

        int firstValue= PdfDictionary.Unknown;
        if(BMvalue !=null && BMvalue.hasMoreTokens()) {
            firstValue= BMvalue.getNextValueAsConstant(false);
        }
        return firstValue;
    }

    private void createMaskForm(PdfObject XObject, String name, PdfObject newSMask, int firstValue) throws PdfException {
        float[] BBox;//size
        BBox= XObject.getFloatArray(PdfDictionary.BBox);

        /**get form as an image*/
        int fx=(int)BBox[0];
        int fy=(int)BBox[1];
        int fw=(int)BBox[2];
        int fh=(int)(BBox[3]);

        //check x,y offsets and factor in
        if(fx<0)
            fx=0;

        //get the form
        BufferedImage image=null;

        // get smask if present and create as image for later
        if(newSMask!=null){

            image = getImageFromPdfObject(XObject, fx, fw, fy, fh);
            BufferedImage smaskImage = getImageFromPdfObject(newSMask, fx, fw, fy, fh);

            /**
             * get Mask colourspace as we need to process mask differently depending on value
             */
            PdfObject ColorSpace=null;
            PdfObject group=newSMask.getDictionary(PdfDictionary.Group);
            if(group!=null){
                currentPdfFile.checkResolved(group);
                ColorSpace=group.getDictionary(PdfDictionary.ColorSpace);
            }

            //apply SMask to image
            image= ImageCommands.applySmask(image, smaskImage, newSMask, true,true, ColorSpace, XObject, gs);

            smaskImage.flush();
        }

        GraphicsState gs1 =new GraphicsState();
        gs1.CTM=new float[][]{{image.getWidth(),0,1},{0,image.getHeight(),1},{0,0,0}};

        //different formula needed if flattening forms
        if(isFlattenedForm){
            gs1.x= flattenX;
            gs1.y= flattenY;
        }else{
            gs1.x=fx;
            gs1.y=fy-image.getHeight();
        }

        //draw as image
        gs1.CTM[2][0]= gs1.x;
        gs1.CTM[2][1]= gs1.y;
        current.drawImage(pageNum,image, gs1,false, name, PDFImageProcessing.IMAGE_INVERTED, -1);
    }

    private BufferedImage createTransparentForm(PdfObject XObject, int fx, int fy, int fw, int fh) {
        BufferedImage image;
        byte[] objectData1 = currentPdfFile.readStream(XObject,true,true,false, false,false, XObject.getCacheName(currentPdfFile.getObjectReader()));

        ObjectStore localStore = new ObjectStore();
        DynamicVectorRenderer glyphDisplay=new SwingDisplay(0,false,20,localStore);
        glyphDisplay.setHiResImageForDisplayMode(useHiResImageForDisplay);

        PdfStreamDecoder glyphDecoder=new PdfStreamDecoder(currentPdfFile, useHiResImageForDisplay,null); //switch to hires as well
        glyphDecoder.setParameters(isPageContent, renderPage, renderMode, extractionMode);
        glyphDecoder.setObjectValue(ValueTypes.ObjectStore, localStore);
        glyphDecoder.setIntValue(FormLevel,formLevel);
        glyphDecoder.setFloatValue(Multiplier, multiplyer);
        glyphDecoder.setFloatValue(SamplingUsed, samplingUsed);

        glyphDecoder.setObjectValue(ValueTypes.DynamicVectorRenderer,glyphDisplay);

        /**read any resources*/
        try{

            PdfObject SMaskResources = XObject.getDictionary(PdfDictionary.Resources);
            if (SMaskResources != null)
                glyphDecoder.readResources(SMaskResources,false);

        }catch(Exception e){
            e.printStackTrace();
        }

        /**decode the stream*/
        if(objectData1 !=null)
            glyphDecoder.decodeStreamIntoObjects(objectData1,false);

        int hh= fh;
        if(fy > fh)
            hh= fy - fh;

        //get bit underneath and merge in
        image=new BufferedImage(fw,hh, BufferedImage.TYPE_INT_ARGB);

        Graphics2D formG2=image.createGraphics();

        if(!isFlattenedForm) //already allowed for in form flattening
            formG2.translate(-fx,-fh);

        //current.paint(formG2,null,null,null,false,true);

        formG2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));

        glyphDisplay.setG2(formG2);
        glyphDisplay.paint(null,null,null);

        localStore.flush();

        return image;
    }

    private static boolean isTransparent(PdfObject groupObj) {

        boolean isTransparentRGB=false;
        if(groupObj!=null){
            String S= groupObj.getName(PdfDictionary.S);
            PdfObject colspace= groupObj.getDictionary(PdfDictionary.ColorSpace);

            isTransparentRGB= S!=null && S.equals("Transparency") && colspace!= null &&
                    colspace.getParameterConstant(PdfDictionary.ColorSpace)== ColorSpaces.DeviceRGB;
        }

        return isTransparentRGB;
    }

    private Area setClip(Shape defaultClip, float[] BBox) {
        Area clip;
        float scalingW= gs.CTM[0][0];
        if(scalingW==0)
            scalingW= gs.CTM[0][1];

        float scalingH=gs.CTM[1][1];
        if(scalingH==0)
            scalingH=gs.CTM[1][0];
         
        int x,y,w,h;

        if(gs.CTM[0][1]>0 && gs.CTM[1][0]<0){

            x=(int)(gs.CTM[2][0]-(BBox[3]));
            y=(int)(gs.CTM[2][1]+BBox[0]);
            w=(int)((BBox[3]-BBox[1])*scalingW);
            h=(int)((BBox[2]-BBox[0])*scalingH);

        }else if(gs.CTM[0][1]<0 && gs.CTM[1][0]>0){

            x=(int)(gs.CTM[2][0]+BBox[1]);
            y=(int)(gs.CTM[2][1]-BBox[2]);
            w=(int)((BBox[3]-BBox[1])*-scalingW);
            h=(int)((BBox[2]-BBox[0])*-scalingH);

        }else//note we adjust size using CTM to factor in scaling
            x=(int)((gs.CTM[2][0]+BBox[0]));
            y=(int)((gs.CTM[2][1]+BBox[1]));
            w=(int)(1+(BBox[2]-BBox[0])*scalingW);           
            h=(int)(1+(BBox[3]-BBox[1])*scalingH);
           
            //allow for inverted
            if(gs.CTM[1][1]<0){
                y=y-h;
            }
        }

        if(gs.getClippingShape()==null)
            clip=null;
        else
            clip= (Area) gs.getClippingShape().clone();

        Area newClip=new Area(new Rectangle(x,y,w,h));

        gs.updateClip(new Area(newClip));
        current.drawClip(gs, defaultClip,false) ;

        return clip;
    }

    final private static float[] matches={1f,0f,0f,1f,0f,0f};

    private static boolean isIdentity(float[] matrix) {

        boolean isIdentity=true;// assume right and try to disprove

        if(matrix!=null){

            //see if it matches if not set flag and exit
            for(int ii=0;ii<6;ii++){
                if(matrix[ii]!=matches[ii]){
                    isIdentity=false;
                    break;
                }
            }
        }

        return isIdentity;
    }

    private BufferedImage getImageFromPdfObject(PdfObject newSMask, int fx, int fw, int fy, int fh) throws PdfException {

        BufferedImage smaskImage;
        Graphics2D formG2;
        byte[] objectData =currentPdfFile.readStream(newSMask,true,true,false, false,false, newSMask.getCacheName(currentPdfFile.getObjectReader()));

        ObjectStore localStore = new ObjectStore();

        DynamicVectorRenderer glyphDisplay=new SwingDisplay(0,false,20,localStore);

        PdfStreamDecoder glyphDecoder=new PdfStreamDecoder(currentPdfFile,useHiResImageForDisplay,null); //switch to hires as well
        glyphDecoder.setParameters(isPageContent,renderPage, renderMode, extractionMode);
        glyphDecoder.setObjectValue(ValueTypes.ObjectStore, localStore);
        glyphDisplay.setHiResImageForDisplayMode(useHiResImageForDisplay);
        glyphDecoder.setObjectValue(ValueTypes.DynamicVectorRenderer,glyphDisplay);
        glyphDecoder.setFloatValue(Multiplier, multiplyer);
        glyphDecoder.setFloatValue(SamplingUsed, samplingUsed);
        glyphDecoder.setBooleanValue(IsFlattenedForm,isFlattenedForm);
        glyphDecoder.setIntValue(FormLevel,formLevel);

        //flag to image decoder that called form here and whether screen or image
        if(renderDirectly)
            glyphDecoder.setIntValue(IsImage,IMAGE_getImageFromPdfObject);
        else
            glyphDecoder.setIntValue(IsImage,SCREEN_getImageFromPdfObject);

        /**read any resources*/
        try{

            PdfObject SMaskResources =newSMask.getDictionary(PdfDictionary.Resources);
            if (SMaskResources != null)
                glyphDecoder.readResources(SMaskResources,false);

        }catch(Exception e){
            e.printStackTrace();
        }

        /**decode the stream*/
        if(objectData!=null)
            glyphDecoder.decodeStreamIntoObjects(objectData,false);

        glyphDecoder.dispose();

        int hh=fh;
        if(fy>fh)
            hh=fy-fh;

        if(fw==0)
            fw=1;
        smaskImage=new  BufferedImage(fw,hh,BufferedImage.TYPE_INT_ARGB);

        formG2=smaskImage.createGraphics();

        formG2.translate(-fx,-fh);
        glyphDisplay.setG2(formG2);
        glyphDisplay.paint(null,null,null);

        localStore.flush();
        return smaskImage;
    }

    /**
     * put item in graphics stack
     */
    private void pushGraphicsState(GraphicsState gs) {

        if(!isStackInitialised){
            isStackInitialised=true;

            graphicsStateStack = new Vector_Object(10);
            textStateStack = new Vector_Object(10);
            strokeColorStateStack= new Vector_Object(20);
            nonstrokeColorStateStack= new Vector_Object(20);
            //clipStack=new Vector_Object(20);
        }

        //store
        graphicsStateStack.push(gs.clone());

        //store clip
        //    Area currentClip=gs.getClippingShape();
        //    if(currentClip==null)
        //      clipStack.push(null);
        //    else{
        //      clipStack.push(currentClip.clone());
        //    }
        //store text state (technically part of gs)
        textStateStack.push(currentTextState.clone());

        //save colorspaces
        nonstrokeColorStateStack.push(gs.nonstrokeColorSpace.clone());
        strokeColorStateStack.push(gs.strokeColorSpace.clone());

        current.resetOnColorspaceChange();

    }

    /**
     * restore GraphicsState status from graphics stack
     */
    private GraphicsState restoreGraphicsState(GraphicsState gs) {

        boolean hasClipChanged=false;

        if(!isStackInitialised){

            if(LogWriter.isOutput())
                LogWriter.writeLog("No GraphicsState saved to retrieve");

            //reset to defaults
            gs=new GraphicsState();
            currentTextState = new TextState();

        }else{

            //see if clip changed
            hasClipChanged=gs.hasClipChanged();

            gs = (GraphicsState) graphicsStateStack.pull();
            currentTextState = (TextState) textStateStack.pull();

            //@remove all caching?
            gs.strokeColorSpace=(GenericColorSpace) strokeColorStateStack.pull();
            gs.nonstrokeColorSpace=(GenericColorSpace) nonstrokeColorStateStack.pull();

            if(gs.strokeColorSpace.getID()== ColorSpaces.Separation)
                gs.strokeColorSpace.restoreColorStatus();

            if(gs.nonstrokeColorSpace.getID()==ColorSpaces.Separation)
                gs.nonstrokeColorSpace.restoreColorStatus();
        }
        //20101122 removed by MS as not apparently needed
        //Object currentClip=clipStack.pull();


        /**
         if(hasClipChanged){
         //if(!renderDirectly && hasClipChanged){
         if(currentClip==null){

         if(gs.current_clipping_shape!=null){
         System.out.println("1shape="+gs.current_clipping_shape);
         throw new RuntimeException();
         }
         gs.setClippingShape(null);
         }else{

         if(!gs.current_clipping_shape.equals((Area)currentClip)){
         System.out.println("2shape="+gs.current_clipping_shape);
         //  throw new RuntimeException();
         }
         gs.setClippingShape((Area)currentClip);
         }
         }
         /**/
        ////////////////////////////////////

        //copy last CM
        for(int i=0;i<3;i++)
            System.arraycopy(gs.CTM, 0, gs.lastCTM, 0, 3);

        //save for later
        if (renderPage){

            if(hasClipChanged){

                current.drawClip(gs,defaultClip,true) ;
            }
            current.resetOnColorspaceChange();

            current.drawFillColor(gs.getNonstrokeColor());
            current.drawStrokeColor(gs.getStrokeColor());

            /**
             * align display
             */
            current.setGraphicsState(GraphicsState.FILL,gs.getAlpha(GraphicsState.FILL));
            current.setGraphicsState(GraphicsState.STROKE,gs.getAlpha(GraphicsState.STROKE));

            //current.drawTR(currentGraphicsState.getTextRenderType()); //reset TR value

        }

        return gs;
    }


    private GraphicsState Q(GraphicsState gs, boolean isLowerCase) {

        //save or retrieve
        if (isLowerCase)
            pushGraphicsState(gs);
        else{
            gs = restoreGraphicsState(gs);

            //flag font has changed
            currentTextState.setFontChanged(true);

        }

        return gs;
    }


    private static void CM(GraphicsState gs, CommandParser parser) {

        //create temp Trm matrix to update Tm
        float[][] Trm = new float[3][3];

        //set Tm matrix
        Trm[0][0] = parser.parseFloat(5);
        Trm[0][1] = parser.parseFloat(4);
        Trm[0][2] = 0;
        Trm[1][0] = parser.parseFloat(3);
        Trm[1][1] = parser.parseFloat(2);
        Trm[1][2] = 0;
        Trm[2][0] = parser.parseFloat(1);
        Trm[2][1] = parser.parseFloat(0);
        Trm[2][2] = 1;


        //copy last CM
        for(int i=0;i<3;i++)
            System.arraycopy(gs.CTM, 0, gs.lastCTM, 0, 3);

        //multiply to get new CTM
        gs.CTM = Matrix.multiply(Trm, gs.CTM);

        //remove slight sheer
        if(gs.CTM[0][0]>0 && gs.CTM[1][1]>0 && gs.CTM[1][0]>0 && gs.CTM[1][0]<0.01 && gs.CTM[0][1]<0){
            gs.CTM[0][1]=0;
            gs.CTM[1][0]=0;
        }
    }



    private void F(boolean isStar, int formLevel, PdfShape currentDrawShape) {

        //ignore transparent white if group set
        if(formLevel>0 && cache.groupObj!=null && !cache.groupObj.getBoolean(PdfDictionary.K) && gs.getAlphaMax(GraphicsState.FILL)>0.84f && (gs.nonstrokeColorSpace.getID() == ColorSpaces.DeviceCMYK)){

            PdfArrayIterator BMvalue = gs.getBM();

            //check not handled elsewhere
            int firstValue=PdfDictionary.Unknown;
            if(BMvalue !=null && BMvalue.hasMoreTokens()) {
                firstValue= BMvalue.getNextValueAsConstant(false);
            }

            if(gs.nonstrokeColorSpace.getColor().getRGB()==-1 || (firstValue==PdfDictionary.Multiply && gs.getAlpha(GraphicsState.FILL)==1f))
                return;
        }

        /**
         * if SMask with this color, we need to ignore
         *  (only case of white with BC of 1,1,1 at present for customers-june2011/12.pdf)
         */
        if(gs.SMask!=null && gs.nonstrokeColorSpace.getID() == ColorSpaces.DeviceCMYK){

            float[] BC=gs.SMask.getFloatArray(PdfDictionary.BC);
            if(gs.nonstrokeColorSpace.getColor().getRGB()==-16777216 && BC!=null && BC[0]==1.0f)
                return;
        }

        /**
         * if SMask with this color, we need to ignore
         *  (only case of white with BC of 1,1,1 at present for customers-june2011/4.pdf)
         */
        if(gs.SMask!=null && gs.nonstrokeColorSpace.getID() == ColorSpaces.ICC){

            float[] BC=gs.SMask.getFloatArray(PdfDictionary.BC);
            if(gs.nonstrokeColorSpace.getColor().getRGB()==-16777216 && BC!=null && BC[0]==0.0f)
                return;
        }

        //replace F with image if soft mask set (see randomHouse/9781609050917_DistX.pdf)
        if(gs.SMask!=null && gs.SMask.getDictionary(PdfDictionary.G)!=null && gs.nonstrokeColorSpace.getID()==ColorSpaces.DeviceRGB){


            if(gs.nonstrokeColorSpace.getColor().getRGB()==-1 && gs.getOPM()==1.0f)
                return;

            float[] BC=gs.SMask.getFloatArray(PdfDictionary.BC);

            if(gs.nonstrokeColorSpace.getColor().getRGB()==-16777216 && BC!=null && BC[0]==1.0f && BC[1]==1.0f && BC[2]==1.0f)
                return;

            try {
                createSMaskFill();
            } catch (PdfException e) {
                e.printStackTrace();
            }
            return;
        }

        // (see randomHouse/9781609050917_DistX.pdf)
//if(gs.SMask!=null && (gs.SMask.getGeneralType(PdfDictionary.SMask)==PdfDictionary.None || gs.SMask.getGeneralType(PdfDictionary.SMask)==PdfDictionary.Multiply) && gs.nonstrokeColorSpace.getID() == ColorSpaces.DeviceRGB && gs.getOPM()==1.0f && gs.nonstrokeColorSpace.getColor().getRGB()==-16777216){

        if(gs.SMask!=null && gs.SMask.getGeneralType(PdfDictionary.SMask)!=PdfDictionary.None && gs.nonstrokeColorSpace.getID() == ColorSpaces.DeviceRGB && gs.getOPM()==1.0f && gs.nonstrokeColorSpace.getColor().getRGB()==-16777216){
            return;
        }

        if(layerDecoder.isLayerVisible()){

            //set Winding rule
            if (isStar){
                currentDrawShape.setEVENODDWindingRule();
            }else
                currentDrawShape.setNONZEROWindingRule();

            currentDrawShape.closeShape();

            //generate shape and stroke and status
            Shape currentShape =currentDrawShape.generateShapeFromPath(gs.CTM,gs.getLineWidth(),Cmd.F);


            //simulate overPrint - may need changing to draw at back of stack
            if(gs.nonstrokeColorSpace.getID()==ColorSpaces.DeviceCMYK && gs.getOPM()==1.0f){

                PdfArrayIterator BMvalue = gs.getBM();

                //check not handled elsewhere
                int firstValue=PdfDictionary.Unknown;
                if(BMvalue !=null && BMvalue.hasMoreTokens()) {
                    firstValue= BMvalue.getNextValueAsConstant(false);
                }

                if(firstValue==PdfDictionary.Multiply){

                    float[] rawData=gs.nonstrokeColorSpace.getRawValues();

                    if(rawData!=null && rawData[3]==1){

                        //try to keep as binary if possible
                        //boolean hasObjectBehind=current.hasObjectsBehind(gs.CTM);
                        //if(hasObjectBehind){
                        currentShape=null;
                        //}
                    }
                }
            }


            if(currentShape!=null && gs.nonstrokeColorSpace.getID()==ColorSpaces.ICC && gs.getOPM()==1.0f){

                PdfArrayIterator BMvalue = gs.getBM();

                //check not handled elsewhere
                int firstValue=PdfDictionary.Unknown;
                if(BMvalue !=null && BMvalue.hasMoreTokens()) {
                    firstValue= BMvalue.getNextValueAsConstant(false);
                }

                if(firstValue==PdfDictionary.Multiply){

                    float[] rawData=gs.nonstrokeColorSpace.getRawValues();

                    /**if(rawData!=null && rawData[2]==1){

                     //try to keep as binary if possible
                     boolean hasObjectBehind=current.hasObjectsBehind(gs.CTM);
                     if(hasObjectBehind)
                     currentShape=null;
                     }else*/{ //if zero just remove
                        int elements=rawData.length;
                        boolean isZero=true;
                        for (float aRawData : rawData)
                            if (aRawData != 0)
                                isZero = false;

                        if(isZero)
                            currentShape=null;
                    }
                }
            }


            //do not paint white CMYK in overpaint mode
            if(currentShape!=null && gs.getAlpha(GraphicsState.FILL)<1 &&
                    gs.nonstrokeColorSpace.getID()==ColorSpaces.DeviceN && gs.getOPM()==1.0f &&
                    gs.nonstrokeColorSpace.getColor().getRGB()==-16777216 ){

                //System.out.println(gs.getNonStrokeAlpha());
                //System.out.println(nonstrokeColorSpace.getAlternateColorSpace()+" "+nonstrokeColorSpace.getColorComponentCount()+" "+nonstrokeColorSpace.pantoneName);
                boolean ignoreTransparent =true; //assume true and disprove
                float[] raw=gs.nonstrokeColorSpace.getRawValues();

                if(raw!=   null){
                    int count=raw.length;
                    for(int ii=0;ii<count;ii++){

                        //System.out.println(ii+"="+raw[ii]+" "+count);

                        if(raw[ii]>0){
                            ignoreTransparent =false;
                            ii=count;
                        }
                    }
                }

                if(ignoreTransparent){
                    currentShape=null;
                }
            }

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

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

                current.drawShape( currentShape,gs, Cmd.F);
            }
        }
        //always reset flag
        currentDrawShape.setClip(false);
        currentDrawShape.resetPath(); // flush all path ops stored

    }

    /**
     * make image from SMask and colour in with fill colour to simulate effect
     */
    private void createSMaskFill() throws PdfException {

        PdfObject maskObj=gs.SMask.getDictionary(PdfDictionary.G);
        currentPdfFile.checkResolved(maskObj);
        float[] BBox;//size
        BBox= maskObj.getFloatArray(PdfDictionary.BBox);

        /**get dimensions as an image*/
        int fx=(int)BBox[0];
        int fy=(int)BBox[1];
        int fw=(int)BBox[2];
        int fh=(int)(BBox[3]);

        //check x,y offsets and factor in
        if(fx<0)
            fx=0;

        /**
         * get the SMAsk
         */
        BufferedImage smaskImage = getImageFromPdfObject(maskObj, fx, fw, fy, fh);

        WritableRaster ras=smaskImage.getRaster();

        int w=ras.getWidth();
        int h=ras.getHeight();

        /**
         * and colour in
         */
        boolean transparent;
        int[] values=new int[4];

        //get fill colour
        int fillColor=gs.nonstrokeColorSpace.getColor().getRGB();
        values[0]=(byte) ((fillColor>>16) & 0xFF);
        values[1]=(byte) ((fillColor>>8) & 0xFF);
        values[2]=(byte) ((fillColor) & 0xFF);

        int[] transparentPixel={0,0,0,0};
        for(int y=0;y<h;y++){
            for(int x=0;x<w;x++){

                //get raw color data
                ras.getPixels(x,y,1,1,values);

                //see if transparent
                transparent=(values[0]==0 && values[1]==0 && values[2]==0 && values[3]==255);

                //if it matched replace and move on
                if(transparent) {
                    ras.setPixels(x, y, 1, 1, transparentPixel);
                }else{

                    int[] newPixel=new int[4];

                    newPixel[3]= (int) (255*0.75f);
                    newPixel[0]=values[0];
                    newPixel[1]=values[1];
                    newPixel[2]=values[2];

                    ras.setPixels(x,y,1,1,newPixel);
                }
            }
        }

        /**
         * draw the shape as image
         */
        GraphicsState gs1 =new GraphicsState();
        gs1.CTM=new float[][]{{smaskImage.getWidth(),0,1},{0,smaskImage.getHeight(),1},{0,0,0}};

        gs1.x=fx;
        gs1.y=fy-smaskImage.getHeight();

        //add as image
        gs1.CTM[2][0]= gs1.x;
        gs1.CTM[2][1]= gs1.y;
        current.drawImage(pageNum,smaskImage, gs1,false, "F"+tokenNumber, PDFImageProcessing.IMAGE_INVERTED, -1);

        smaskImage.flush();

    }

}
TOP

Related Classes of org.jpedal.parser.PdfStreamDecoder

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.