All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.render.SwingDisplay Maven / Gradle / Ivy

/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/support/
 *
 * (C) Copyright 1997-2016 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
     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


 *
 * ---------------
 * SwingDisplay.java
 * ---------------
 */
package org.jpedal.render;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.JOptionPane;
import org.jpedal.color.DeviceRGBColorSpace;
import org.jpedal.color.GenericColorSpace;
import org.jpedal.color.PdfColor;
import org.jpedal.color.PdfPaint;
import org.jpedal.exception.PdfException;
import org.jpedal.external.JPedalCustomDrawObject;
import org.jpedal.fonts.PdfFont;
import org.jpedal.fonts.glyph.*;
import org.jpedal.io.ObjectStore;
import org.jpedal.objects.GraphicsState;
import org.jpedal.objects.PdfShape;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.parser.ParserOptions;
import org.jpedal.parser.image.ImageDataToJavaImage;
import org.jpedal.parser.image.data.ImageData;
import org.jpedal.parser.image.downsample.DownSampler;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.Messages;
import org.jpedal.utils.repositories.*;
import org.jpedal.utils.repositories.generic.Vector_Rectangle_Int;

 public class SwingDisplay extends GUIDisplay{
    
    
     //debug flag for testing new image rescaling
    public static final boolean testSampling=false;
    
    
    //Flag to prevent drawing highlights too often.
    boolean ignoreHighlight;
    
    float lastStrokeOpacity=-1;
    float lastFillOpacity=-1;
    
    //stop screen being cleared on next repaint
    private boolean noRepaint;
    
    //track items painted to reduce unnecessary calls
    private int lastItemPainted=-1;

    //tell renderer to optimise calls if possible
    private boolean optimsePainting;
    
    private int pageX1=9999, pageX2=-9999, pageY1=-9999, pageY2=9999;
    
    //used to cache single image
    private BufferedImage singleImage;
    
    private int imageCount;
    
    //hint for conversion ops
    private static final RenderingHints hints;
    
    private final Map cachedWidths=new HashMap(10);
    
    private final Map cachedHeights=new HashMap(10);
    
    private Map fonts=new HashMap(50);
    
    private Set fontsUsed=new HashSet(50);
    
    protected GlyphFactory factory;
    
    private PdfGlyphs glyphs;
    
    private Map imageID=new HashMap(10);
    
    private Map storedImageValues=new HashMap(10);
    
    //text highlights if needed
    private int[] textHighlightsX;
    
    //allow user to diable g2 setting
    boolean stopG2setting;
    
    float[] x_coord,y_coord;

    private Vector_Object text_color;
    private Vector_Object stroke_color;
    private Vector_Object fill_color;
    
    private Vector_Object stroke;
    
    Vector_Int shapeType;
    
    private Vector_Double af1;
    private Vector_Double af2;
    private Vector_Double af3;
    private Vector_Double af4;
    
    //TR for text
    private Vector_Int TRvalues;
    
    //font sizes for text
    private Vector_Int fs;
    
    //line widths if not 0
    private Vector_Int lw;
    
    //holds rectangular outline to test in redraw*/
    private Vector_Shape clips;
    
    //holds object type
    private Vector_Object javaObjects;
    
    //holds fill type
    private Vector_Int textFillType;
    
    //holds object type
    private Vector_Float opacity;
    
    //holds blends
    private Vector_Int BMvalues;
    
    //used to track col changes
    int lastFillTextCol,lastFillCol,lastStrokeCol;
    
    //used to track strokes
    Stroke lastStroke;
    
    //trakc affine transform changes
    private double[] lastAf=new double[4];
    
    //used to minimise TR and font changes by ignoring duplicates
    private int lastTR=2,lastFS=-1,lastLW=-1;
    
    //ensure colors reset if text
    boolean resetTextColors=true;
    
    boolean fillSet,strokeSet;
    
    //If highlgihts are not null and no highlgihts are drawn
    //then it is likely a scanned page. Treat differently.
    private boolean needsHighlights = true;
    
    private int paintThreadCount;
    private int paintThreadID;
    
    //For IDR internal use only
    private boolean[] drawnHighlights;
    
    static {
        hints =new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        hints.put(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    }
    
    public SwingDisplay() {
    	currentItem = 0;
        type =DynamicVectorRenderer.DISPLAY_SCREEN;
    }
    
    /**
     * @param defaultSize
     */
    void setupArrays(final int defaultSize) {
        
        x_coord=new float[defaultSize];
        y_coord=new float[defaultSize];
        text_color=new Vector_Object(defaultSize);
        textFillType=new Vector_Int(defaultSize);
        stroke_color=new Vector_Object(defaultSize);
        fill_color=new Vector_Object(defaultSize);
        stroke=new Vector_Object(defaultSize);
        pageObjects=new Vector_Object(defaultSize);
        javaObjects=new Vector_Object(defaultSize);
        shapeType=new Vector_Int(defaultSize);
        areas=new Vector_Rectangle_Int(defaultSize);
        af1=new Vector_Double(defaultSize);
        af2=new Vector_Double(defaultSize);
        af3=new Vector_Double(defaultSize);
        af4=new Vector_Double(defaultSize);
        
        clips=new Vector_Shape(defaultSize);
        objectType=new Vector_Int(defaultSize);
        
        opacity=new Vector_Float(defaultSize);
        
        currentItem = 0;
    }
    
    public SwingDisplay(final int pageNumber, final boolean addBackground, final int defaultSize, final ObjectStore newObjectRef) {
        
        this.rawPageNumber =pageNumber;
        this.objectStoreRef = newObjectRef;
        this.addBackground=addBackground;
        
        setupArrays(defaultSize);
        type =DynamicVectorRenderer.DISPLAY_SCREEN;
    }
    
    
    public SwingDisplay(final int pageNumber, final ObjectStore newObjectRef, final boolean isPrinting) {
        
        this.rawPageNumber =pageNumber;
        this.objectStoreRef = newObjectRef;
        this.isPrinting=isPrinting;
        
        setupArrays(defaultSize);
        type =DynamicVectorRenderer.DISPLAY_SCREEN;
        
    }
    
    private void renderHighlight(final Rectangle highlight, final Graphics2D g2){
        
        if(highlight!=null && !ignoreHighlight){
            //Backup current g2 paint and composite
            final Composite comp = g2.getComposite();
            final Paint p = g2.getPaint();
            
            //Set new values for highlight
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,DecoderOptions.highlightComposite));
            
            if(invertHighlight){
                g2.setColor(Color.WHITE);
                g2.setXORMode(Color.BLACK);
            }else{
                
                g2.setPaint(DecoderOptions.highlightColor);
            }
            //Draw highlight
            g2.fill(highlight);
            
            //Reset to starting values
            g2.setComposite(comp);
            g2.setPaint(p);
            
            needsHighlights = false;
        }
    }
    
   public void stopG2HintSetting(final boolean isSet){
        stopG2setting=isSet;
        
    }
    
    /* remove all page objects and flush queue */
    private void flush() {
        
        singleImage=null;
        
        imageCount=0;
        
        lastFS = -1;
        
        objectAreas = null;
        
        if(shapeType!=null){
            
            shapeType.clear();
            pageObjects.clear();
            objectType.clear();
            javaObjects.clear();
            areas.clear();
            clips.clear();
            x_coord=new float[defaultSize];
            y_coord=new float[defaultSize];
            textFillType.clear();
            text_color.clear();
            fill_color.clear();
            stroke_color.clear();
            stroke.clear();
            
            if(TRvalues!=null) {
                TRvalues = null;
            }
            
            if(fs!=null) {
                fs = null;
            }
            
            if(lw!=null) {
                lw = null;
            }
            
            af1.clear();
            af2.clear();
            af3.clear();
            af4.clear();
            
            if(opacity!=null) {
                opacity.clear();
            }
            
            if(BMvalues!=null) {
                BMvalues.clear();
            }
            
            lastStrokeOpacity=-1;
            lastFillOpacity=-1;

            endItem=-1;
        }
        
        //pointer we use to flag color change
        lastFillTextCol=0;
        lastFillCol=0;
        lastStrokeCol=0;
        
        lastClip=null;
        hasClips=false;
        
        //track strokes
        lastStroke=null;
        
        lastAf=new double[4];
        
        currentItem=0;
        
        fillSet=false;
        strokeSet=false;
        
        fonts.clear();
        fontsUsed.clear();
        
        imageID.clear();
        
        pageX1=9999;
        pageX2=-9999;
        pageY1=-9999;
        pageY2=9999;
        
        lastScaling=0;
        
    }
    
    private boolean renderFailed;
    
    //optional frame for user to pass in - if present, error warning will be displayed
    private Container frame;
    
    //make sure user only gets 1 error message a session
    private static boolean userAlerted;
    
    
    
    /*renders all the objects onto the g2 surface*/
    @Override
    @SuppressWarnings("OverlyLongMethod")
    public void paint(final Rectangle[] highlights, final AffineTransform viewScaling, final Rectangle userAnnot){
        
        //take a lock
        final int currentThreadID=++paintThreadID;
        paintThreadCount++;
        
        //Keep track of drawn highlights so we don't draw multiple times
        if(highlights!=null){
            drawnHighlights = new boolean[highlights.length];
            for(int i=0; i!=drawnHighlights.length; i++) {
                drawnHighlights[i] = false;
            }
        }
        
        //ensure all other threads dead or this one killed  (screen only)
        if(paintThreadCount>1){
            try {
                Thread.sleep(50);
            } catch (final InterruptedException e) {
                //tell user and log
                LogWriter.writeLog("Exception: " + e.getMessage());
            }
            
            if(currentThreadID!=paintThreadID){
                paintThreadCount--;
                return;
            }
        }
        
        final boolean debug=false;
        
        String fontUsed;
        float a=0,b = 0,c=0,d=0;
        
        Rectangle dirtyRegion=null;
        
        //local copies
        final int[] objectTypes=objectType.get();
        final int[] textFill=textFillType.get();
        
        //currentItem to make the code work - can you let me know what you think
        final int count=currentItem; //DO nOT CHANGE
        final Area[] pageClips=clips.get();
        final double[] afValues1=af1.get();
        int[] fsValues=null;
        if(fs!=null) {
            fsValues = fs.get();
        }

        int[] lwValues=null;
        if(lw!=null) {
            lwValues = lw.get();
        }
        final double[] afValues2=af2.get();
        final double[] afValues3=af3.get();
        final double[] afValues4=af4.get();
        final Object[] text_color=this.text_color.get();
        final Object[] fill_color=this.fill_color.get();
        
        final Object[] stroke_color=this.stroke_color.get();
        final Object[] pageObjects=this.pageObjects.get();
        
        final Object[] javaObjects=this.javaObjects.get();
        final Object[] stroke=this.stroke.get();
        final int[] fillType=this.shapeType.get();
        
        float[] opacity = null;
        if(this.opacity!=null) {
            opacity = this.opacity.get();
        }
        
        int[] BMvalues=null;
        if(this.BMvalues!=null) {
            BMvalues = this.BMvalues.get();
        }
        
        int[] TRvalues = null;
        if(this.TRvalues!=null) {
            TRvalues = this.TRvalues.get();
        }
        
        int[][] areas=null;
        
        if(this.areas!=null) {
            areas = this.areas.get();
        }
        
        boolean isInitialised=false;
        
        Shape defaultClip=null;
        
        if(g2!=null){
            final Shape rawClip=g2.getClip();
            if(rawClip!=null) {
                dirtyRegion = rawClip.getBounds();
            }
            
            defaultClip=g2.getClip();
        }
        
        //used to optimise clipping
        Area clipToUse=null;
        boolean newClip=false;
        
        if(noRepaint) {
            noRepaint = false;
        } else if(lastItemPainted==-1){
            paintBackground(dirtyRegion);
        }
        
        //save raw scaling and apply any viewport
        AffineTransform rawScaling=null;
        
        if(g2!=null){
            rawScaling=g2.getTransform();
            if(viewScaling!=null){
                g2.transform(viewScaling);
                defaultClip=g2.getClip(); //not valid if viewport so disable
            }
        }
        
        Object currentObject;
        int type,textFillType,currentTR=GraphicsState.FILL;
        int lineWidth=0;
        float fillOpacity=1.0f;
        float strokeOpacity=1.0f;
        float x,y ;
        int iCount=0,cCount=0,sCount=0,fsCount=-1,lwCount=0,afCount=-1,tCount=0,stCount=0,
                fillCount=0,strokeCount=0,trCount=0,opCount=0,BMCount=0,
                stringCount=0;//note af is 1 behind!
        PdfPaint textStrokeCol=null,textFillCol=null,fillCol=null,strokeCol = null;
        Stroke currentStroke=null;
        
        //if we reuse image this is pointer to live image
        int imageUsed;
        
        //use preset colours for T3 glyph
        if(colorsLocked){
            strokeCol=this.strokeCol;
            fillCol=this.fillCol;
        }
        
        //now draw all objects
        for(int i=0; i4800)
            //break;
            
        	//Set item we are currently rendering
        	itemToRender = i;
        	
            boolean ignoreItem;
            
            type=objectTypes[i];
            
            Rectangle currentArea=null;
            
            //exit if later paint recall
            if(currentThreadID!=paintThreadID){
                paintThreadCount--;
                
                return;
            }
            
            //generate glyph for text
            if(type<0){
                
                //lazy initialisation on factory
                if(factory==null) {
                    factory = new T1GlyphFactory(false);
                }
                
                final UnrendererGlyph glyph=(UnrendererGlyph)pageObjects[i];
                final Object newGlyph;
                
                //generate glyph now if needed
                final float[][] trm={{a,b},{c,d},{glyph.x,glyph.y}};
                
                final int raw=glyph.rawInt;
                final Integer key= raw;
                final String disp=glyphs.getDisplayValue(key);
                final String charGlyph = glyphs.getCharGlyph(key);
                final String emb = glyphs.getEmbeddedEnc(key);
                final float width=glyph.currentWidth;
                
                if(type==-DynamicVectorRenderer.TEXT){
                    
                    final boolean isSTD=DecoderOptions.isRunningOnMac ||(org.jpedal.fonts.StandardFonts.isStandardFont(glyphs.getBaseFontName(),false));
                    final Area transformedGlyph2= glyphs.getStandardGlyph(trm, raw, disp, width,isSTD);
                    
                    //if its already generated we just need to move it
                    final AffineTransform at2 =AffineTransform.getTranslateInstance(glyph.x,glyph.y);
                    transformedGlyph2.transform(at2);
                    
                    currentArea=RenderUtils.getAreaForGlyph(trm);
                    
                    newGlyph= transformedGlyph2;
                    
                }else{
                    newGlyph= glyphs.getEmbeddedGlyph(factory,charGlyph ,trm, raw, disp, width,emb);
                }
                
                //reset values to generated values
                type=-type;
                objectTypes[i]=type;
                pageObjects[i]=newGlyph;
                
            }
            
            if(type>0){
                
                x=x_coord[i];
                y=y_coord[i];
                
                currentObject=pageObjects[i];
                
                //swap in replacement image
                if(type==DynamicVectorRenderer.REUSED_IMAGE){
                    type=DynamicVectorRenderer.IMAGE;
                    imageUsed= (Integer) currentObject;
                    currentObject=pageObjects[imageUsed];
                }else {
                    imageUsed = -1;
                }
                
                //workout area occupied by glyf
                if(currentArea==null) {
                    currentArea = getObjectArea(afValues1, fsValues, afValues2, afValues3, afValues4, pageObjects, areas, type, x, y, fsCount, afCount, i);
                }
                
                ignoreItem=false;
                
                //see if we need to draw
                if (currentArea != null && userAnnot != null && type < 7) {
                    ignoreItem = testIfAnnotVisible(currentArea, userAnnot, ignoreItem);
                }
                
                if(ignoreItem || (lastItemPainted!=-1 && i-1){
            
            //Use on page coords to make sure the glyph needs highlighting
            currentArea=RenderUtils.getAreaForGlyph(new float[][]{{(float)afValues1[afCount],(float)afValues2[afCount],0},
                {(float)afValues3[afCount],(float)afValues4[afCount],0},{x,y,1}});
            
        }else if(fsCount!=-1 && afValues1!=null){// && afCount>-1){
            
            final int realSize=fsValues[fsCount];
            if(realSize<0) //ignore sign which is used as flag elsewhere
            {
                currentArea = new Rectangle((int) x + realSize, (int) y, -realSize, -realSize);
            } else {
                currentArea = new Rectangle((int) x, (int) y, realSize, realSize);
            }
        }
        return currentArea;
    }
    
    private void renderImage(final double[] afValues1, final double[] afValues2,
            final double[] afValues3, final double[] afValues4, final Object[] pageObjects,
             Object currentObject, final float fillOpacity,
            final float x, final float y, final int iCount, final int afCount, final int imageUsed, final int i){
        
        
        //generate unique value to every image on given page (no more overighting stuff in the hashmap)
        final String key = Integer.toString(this.rawPageNumber) + Integer.toString(iCount);
        
        if(!isType3Font && objectStoreRef.isRawImageDataSaved(key)){
            currentObject=getResampledImage(key, pageObjects, i, currentObject);
        }
        
        //get image and reload if needed
        BufferedImage img=null;
        if(currentObject!=null) {
            img = (BufferedImage) currentObject;
        }else{
            img = reloadCachedImage(imageUsed, i, img);
        }

        if(img!=null) {
            final AffineTransform imageAf=new AffineTransform(afValues1[afCount],afValues2[afCount],afValues3[afCount],afValues4[afCount],x,y);

            renderImage(imageAf, img, fillOpacity, null, x, y);
        }
    }
    
    private Object getResampledImage(final String key, final Object[] pageObjects1, final int i, Object currentObject) {
        
        int sampling=1,w1,pY;
        float  scalingToUse=scaling;
        //fix for rescaling on Enkelt-Scanning_-_Bank-10.10.115.166_-_12-12-2007_-_15-27-57jpg50-300.pdf
        if(scaling<1) {
            scalingToUse = 1f;
        }
        final int defaultX= (Integer) objectStoreRef.getRawImageDataParameter(key, ObjectStore.IMAGE_pX);
        final int pX=(int)(defaultX*scalingToUse);
        final int defaultY= (Integer) objectStoreRef.getRawImageDataParameter(key, ObjectStore.IMAGE_pY);
        pY=(int)(defaultY*scalingToUse);
        w1= (Integer) objectStoreRef.getRawImageDataParameter(key, ObjectStore.IMAGE_WIDTH);
        final int h1= (Integer) objectStoreRef.getRawImageDataParameter(key, ObjectStore.IMAGE_HEIGHT);
        final int bpc= (Integer) objectStoreRef.getRawImageDataParameter(key, ObjectStore.IMAGE_DEPTH);
        final byte[] maskCol=(byte[]) objectStoreRef.getRawImageDataParameter(key,ObjectStore.IMAGE_MASKCOL);
        //final int colorspaceID= (Integer) objectStoreRef.getRawImageDataParameter(key, ObjectStore.IMAGE_COLORSPACE);
        
        BufferedImage image=null;
        //down-sample size if displaying
        if(pX>0){
            
            //see what we could reduce to and still be big enough for page
            int newW=w1,newH=h1;
            
            final int smallestH=pY<<2; //double so comparison works
            final int smallestW=pX<<2;
            
            //cannot be smaller than page
            while(newW>smallestW && newH>smallestH){
                sampling <<= 1;
                newW >>= 1;
                newH >>= 1;
            }
            
            //System.out.println("sampling="+sampling+" w,h="+w1+" "+h1+" newW,H"+newW+" "+newH+" pX="+pX+" pY="+pY);
            
            int scaleX=w1/pX;
            if(scaleX<1) {
                scaleX = 1;
            }
            
            int scaleY=h1/pY;
            if(scaleY<1) {
                scaleY = 1;
            }
            
            //choose smaller value so at least size of page
            sampling=scaleX;
            if(sampling>scaleY) {
                sampling = scaleY;
            }
            
            //see what we could reduce to and still be big enough for page
            int defnewW=w1,defnewH=h1;
            
            final int defsmallestH=pY<<2; //double so comparison works
            final int defsmallestW=pX<<2;
            
            //cannot be smaller than page
            while(defnewW>defsmallestW && defnewH>defsmallestH){
                defnewW >>= 1;
                defnewH >>= 1;
            }
            
            //rescan all pixels and down-sample image
            if((scaling>1f || lastScaling>1f)&& sampling>=1 && (lastScaling!=scaling)){
                try{
                    image=resampleImageData(sampling, w1, h1, bpc,maskCol,key);
                }catch(Exception e){
                    //tell user and log
                    LogWriter.writeLog("Exception rescaling image: " + e.getMessage());
                }
            }
        }
        
        //reset image stored by renderer
        if (image!=null) {
            //reset our track if only graphics
            if(singleImage!=null) {
                singleImage = image;
            }
            pageObjects1[i] = image;
            currentObject=image;
        }
        return currentObject;
    }
    
    private BufferedImage resampleImageData(final int sampling, final int w1, final int h1, int bpc,final byte[] maskCol, final String key) {
        
        //get data
        final byte[] data= objectStoreRef.getRawImageData(key);
        
        ImageData imageData=new ImageData(data);
        imageData.setWidth(w1);
        imageData.setHeight(h1);
        imageData.setCompCount(4);
        imageData.setDepth(bpc);
        
        if(testSampling){
            System.out.println("resampleImageData bytes="+data.length+" "+w1+" "+h1+" "+sampling+" bytes="+imageData.getObjectData().length);
        }
        
        GenericColorSpace decodeColorData=new DeviceRGBColorSpace();
        
        BufferedImage image;
        
        if(sampling>1){
            decodeColorData = DownSampler.downSampleImage(decodeColorData, imageData, maskCol, sampling);
        }
        
        if (maskCol!=null) {
            image = ImageDataToJavaImage.makeMaskImage(new ParserOptions(), null, null,imageData, decodeColorData, maskCol);           
        } else { //handle other types

            LogWriter.writeLog( imageData.getWidth() + "W * " + imageData.getHeight() + "H BPC=" + imageData.getDepth() + ' ' + decodeColorData);

            image =ImageDataToJavaImage.makeImage(decodeColorData,imageData);

        }
        
        if(testSampling){
            System.out.println("image now="+image.getWidth()+" "+image.getHeight());
        }
        
        return image;
    }
    
    private BufferedImage reloadCachedImage(final int imageUsed, final int i,BufferedImage img) {

        Object currentObject=null;
        try{
            
            //cache single images in memory for speed
            if(singleImage!=null){
                currentObject=singleImage.getSubimage(0,0,singleImage.getWidth(),singleImage.getHeight());
            }
            
            if(currentObject==null){
                
                int keyID=i;
                if(imageUsed!=-1) {
                    keyID = imageUsed;
                }
                
                if(rawKey==null) {
                    currentObject = objectStoreRef.loadStoredImage(rawPageNumber + "_HIRES_" + keyID);
                } else {
                    currentObject = objectStoreRef.loadStoredImage(rawPageNumber + "_HIRES_" + keyID + '_' + rawKey);
                }
                
                //flag if problem
                if(currentObject==null) {
                    renderFailed = true;
                }
            }
            
            img=(BufferedImage)currentObject;
            
        }catch(final Exception e){
            LogWriter.writeLog("Exception: " + e.getMessage());
        }
        return img;
    }
    
    /**
     * allow user to set component for waring message in renderer to appear -
     * if unset no message will appear
     * @param frame
     */
    public void setMessageFrame(final Container frame){
        this.frame=frame;
    }
    
    /**
     * highlight a glyph by reversing the display. For white text, use black
     */
    private Rectangle setHighlightForGlyph(final Rectangle area, final Rectangle[] highlights) {
        
        
        if (highlights == null || textHighlightsX == null) {
            return null;
        }
        
        ignoreHighlight = false;
        for(int j=0; j!= highlights.length; j++){
            if(highlights[j]!=null && area!=null && (highlights[j].intersects(area))){
                
                //Get intersection of the two areas
                final Rectangle intersection = highlights[j].intersection(area);
                
                //Intersection area between highlight and text area
                final float iArea = intersection.width*intersection.height;
                
                //25% of text area
                final float tArea = (area.width*area.height)/ 4f;
                
                //Only highlight if (x.y) is with highlight and more than 25% intersects
                //or intersect is greater than 60%
                if((highlights[j].contains(area.x, area.y) && iArea>tArea) ||
                        iArea>(area.width*area.height)/ 1.667f){
                    if(!drawnHighlights[j]){
                        ignoreHighlight = false;
                        drawnHighlights[j]=true;
                        return highlights[j];
                    }else{
                        ignoreHighlight = true;
                        return highlights[j];
                    }
                }
            }
        }
        
        //old code not used
        return null;
        
    }
    
    /* saves text object with attributes for rendering*/
    @Override
    public void drawText(final float[][] Trm, final String text, final GraphicsState currentGraphicsState, final float x, final float y, final Font javaFont) {
        
        //set color first
        PdfPaint currentCol;
        
        if(Trm!=null){
            final double[] nextAf= {Trm[0][0],Trm[0][1],Trm[1][0],Trm[1][1],Trm[2][0],Trm[2][1]};
            
            if((lastAf[0]==nextAf[0])&&(lastAf[1]==nextAf[1])&&
                    (lastAf[2]==nextAf[2])&&(lastAf[3]==nextAf[3])){
            }else{
                this.drawAffine(nextAf);
                lastAf[0]=nextAf[0];
                lastAf[1]=nextAf[1];
                lastAf[2]=nextAf[2];
                lastAf[3]=nextAf[3];
            }
        }
        
        final int text_fill_type = currentGraphicsState.getTextRenderType();
        
        //for a fill
        if ((text_fill_type & GraphicsState.FILL) == GraphicsState.FILL) {
            currentCol=currentGraphicsState.getNonstrokeColor();
            
            if(currentCol.isPattern()){
                drawColor(currentCol,GraphicsState.FILL);
                resetTextColors=true;
            }else{
                
                final int newCol=(currentCol).getRGB();
                if((resetTextColors)||((lastFillTextCol!=newCol))){
                    lastFillTextCol=newCol;
                    drawColor(currentCol,GraphicsState.FILL);
                }
            }
        }
        
        //and/or do a stroke
        if ((text_fill_type & GraphicsState.STROKE) == GraphicsState.STROKE){
            currentCol=currentGraphicsState.getStrokeColor();
            
            if(currentCol.isPattern()){
                drawColor(currentCol,GraphicsState.STROKE);
                resetTextColors=true;
            }else{
                
                final int newCol=currentCol.getRGB();
                if((resetTextColors)||(lastStrokeCol!=newCol)){
                    lastStrokeCol=newCol;
                    drawColor(currentCol,GraphicsState.STROKE);
                }
            }
        }
        
        pageObjects.addElement(text);
        javaObjects.addElement(javaFont);
        
        objectType.addElement(DynamicVectorRenderer.STRING);
        
        //add to check for transparency if large
        final int fontSize=javaFont.getSize();
        final int[] rectParams = {(int)x,(int)y,fontSize,fontSize};
        if(fontSize>100) {
            areas.addElement(rectParams);
        } else {
            areas.addElement(null);
        }
        
        x_coord=RenderUtils.checkSize(x_coord,currentItem);
        y_coord=RenderUtils.checkSize(y_coord,currentItem);
        x_coord[currentItem]=x;
        y_coord[currentItem]=y;
        
        currentItem++;
        
        resetTextColors=false;
        
    }

    /* save image in array to draw */
    @Override
    public int drawImage(final int pageNumber,BufferedImage image,
    final GraphicsState currentGraphicsState,
    final boolean alreadyCached, final String name, final int previousUse) {
        
        if(previousUse!=-1) {
            return redrawImage(pageNumber, currentGraphicsState, name, previousUse);
        }
        
        this.rawPageNumber =pageNumber;
        float CTM[][]=currentGraphicsState.CTM;
        
        final float x=currentGraphicsState.x;
        float y=currentGraphicsState.y;
        
        final double[] nextAf=new double[6];
        
        final boolean cacheInMemory=(image.getWidth()<100 && image.getHeight()<100) || image.getHeight()==1;
        
        String key;
        if(rawKey==null) {
            key = pageNumber + "_" + (currentItem + 1);
        } else {
            key = rawKey + '_' + (currentItem + 1);
        }
        
        final AffineTransform upside_down=new AffineTransform(CTM[0][0],CTM[0][1],CTM[1][0],CTM[1][1],0,0);
            
        upside_down.getMatrix(nextAf);
        
        this.drawAffine(nextAf);
        lastAf[0]=nextAf[0];
        lastAf[1]=nextAf[1];
        lastAf[2]=nextAf[2];
        lastAf[3]=nextAf[3];
            
        int w,h;

        if(!alreadyCached || cachedWidths.get(key)==null){
            w = image.getWidth();
            h = image.getHeight();
        }else{
            w= cachedWidths.get(key);
            h= cachedHeights.get(key);
        }
            
        if(!alreadyCached && !cacheInMemory){

            if(!isPrinting){

                //cache PDF with single image for speed
                if(imageCount==0){
                    singleImage=image.getSubimage(0,0,image.getWidth(),image.getHeight());

                    imageCount++;
                }else {
                    singleImage = null;
                }
            }
            if(rawKey==null){
                objectStoreRef.saveStoredImageAsBytes(pageNumber+"_HIRES_"+currentItem,image,false);
                imageIDtoName.put(currentItem,pageNumber+"_HIRES_"+currentItem);
            }else{
                objectStoreRef.saveStoredImageAsBytes(pageNumber+"_HIRES_"+currentItem+ '_' +rawKey,image,false);
                imageIDtoName.put(currentItem,pageNumber+"_HIRES_"+currentItem+ '_' +rawKey);
            }

            if(rawKey==null) {
                key = pageNumber + "_" + currentItem;
            } else {
                key = rawKey + '_' + currentItem;
            }

            cachedWidths.put(key, w);
            cachedHeights.put(key, h);
        }
        
        x_coord=RenderUtils.checkSize(x_coord,currentItem);
        y_coord=RenderUtils.checkSize(y_coord,currentItem);
        x_coord[currentItem]=x;
        y_coord[currentItem]=y;
        
        objectType.addElement(DynamicVectorRenderer.IMAGE);
        float WidthModifier = 1;
        float HeightModifier = 1;
        
        //ignore in this case /PDFdata/baseline_screens/customers3/1773_A2.pdf
        if(CTM[0][0]>0 && CTM[0][0]<0.05 && CTM[0][1]!=0 && CTM[1][0]!=0 && CTM[1][1]!=0){
            areas.addElement(null);
        }else{
            w=(int)(CTM[0][0]*WidthModifier);
            if(w==0) {
                w = (int) (CTM[0][1] * WidthModifier);
            }
            h=(int)(CTM[1][1]*HeightModifier);
            if(h==0) {
                h = (int) (CTM[1][0] * HeightModifier);
            }
           
            //fix negative height on Ghostscript image in printing
            final int x1=(int)currentGraphicsState.x;
            int y1=(int)currentGraphicsState.y;
            final int w1=w;
            int h1=h;
            if(h1<0){
                y1 += h1;
                h1=-h1;
            }
            
            if(h1==0) {
                h1 = 1;
            }
            
            final int[] rectParams = {x1,y1,w1,h1};
            
            areas.addElement(rectParams);
            
            checkWidth(rectParams);
        }
        
        if(!cacheInMemory){
            pageObjects.addElement(null);
        }else {
            pageObjects.addElement(image);
        }
        
        //store id so we can get as low res image
        imageID.put(name, currentItem);
        
        //nore minus one as affine not yet done
        storedImageValues.put("imageAff-"+currentItem,nextAf);
        
        currentItem++;
        
        return currentItem-1;
    }
    
    /* save image in array to draw */
    private int redrawImage(final int pageNumber, final GraphicsState currentGraphicsState, final String name, final int previousUse) {
        
        this.rawPageNumber =pageNumber;
        
        final float x=currentGraphicsState.x;
        final float y=currentGraphicsState.y;
        
        final double[] nextAf= storedImageValues.get("imageAff-"+previousUse);
        this.drawAffine(nextAf);

        lastAf[0]=nextAf[0];
        lastAf[1]=nextAf[1];
        lastAf[2]=nextAf[2];
        lastAf[3]=nextAf[3];

        if(rawKey==null && imageIDtoName.containsKey(previousUse)){
            imageIDtoName.put(currentItem,pageNumber+"_HIRES_"+previousUse);
        }else{
            imageIDtoName.put(currentItem,pageNumber+"_HIRES_"+previousUse+ '_' +rawKey);
        }
        
        x_coord=RenderUtils.checkSize(x_coord,currentItem);
        y_coord=RenderUtils.checkSize(y_coord,currentItem);
        x_coord[currentItem]=x;
        y_coord[currentItem]=y;
        
        objectType.addElement(DynamicVectorRenderer.REUSED_IMAGE);
        
        final int[] previousRectangle=areas.elementAt(previousUse);
        
        int[] newRect=null;
        
        if(previousRectangle!=null){
            newRect = new int[]{(int)x,(int)y,previousRectangle[2],previousRectangle[3]};
        }
        
        areas.addElement(newRect);
        
        if(previousRectangle!=null) {
            checkWidth(newRect);
        }
        
        pageObjects.addElement(previousUse);
        
        //store id so we can get as low res image
        imageID.put(name, previousUse);
        
        currentItem++;
        
        return currentItem-1;
    }
    
    /**
     * track actual size of shape
     */
    private void checkWidth(final int[] rect) {
        
        final int x1=rect[0];
        final int y2=rect[1];
        final int y1=y2+rect[3];
        final int x2=x1+rect[2];
        
        if(x1pageX2) {
            pageX2 = x2;
        }
        
        if(y1>pageY1) {
            pageY1 = y1;
        }
        if(y2 fonts){
        
        // we use Cannoo to turn our stream back into a DynamicVectorRenderer
        try{
            this.fonts = fonts;
            
            final ByteArrayInputStream bis=new ByteArrayInputStream(stream);
            
            //read version and throw error is not correct version
            final int version=bis.read();
            if(version!=1) {
                throw new PdfException("Unknown version in serialised object " + version);
            }
            
            bis.read(); //0=no,1=yes old hires flag
            
            rawPageNumber =bis.read();
            
            x_coord=(float[]) RenderUtils.restoreFromStream(bis);
            y_coord=(float[]) RenderUtils.restoreFromStream(bis);
            
            //read in arrays - opposite of serializeToByteArray();
            //we may need to throw an exception to allow for errors
            
            text_color = (Vector_Object) RenderUtils.restoreFromStream(bis);
            
            textFillType = (Vector_Int) RenderUtils.restoreFromStream(bis);
            
            stroke_color = new Vector_Object();
            stroke_color.restoreFromStream(bis);
            
            fill_color = new Vector_Object();
            fill_color.restoreFromStream(bis);
            
            stroke = new Vector_Object();
            stroke.restoreFromStream(bis);
            
            pageObjects = new Vector_Object();
            pageObjects.restoreFromStream(bis);
            
            javaObjects=(Vector_Object) RenderUtils.restoreFromStream(bis);
            
            shapeType = (Vector_Int) RenderUtils.restoreFromStream(bis);
            
            af1 = (Vector_Double) RenderUtils.restoreFromStream(bis);
            
            af2 = (Vector_Double) RenderUtils.restoreFromStream(bis);
            
            af3 = (Vector_Double) RenderUtils.restoreFromStream(bis);
            
            af4 = (Vector_Double) RenderUtils.restoreFromStream(bis);
            
            clips = new Vector_Shape();
            clips.restoreFromStream(bis);
            
            objectType = (Vector_Int) RenderUtils.restoreFromStream(bis);

            opacity=(Vector_Float) RenderUtils.restoreFromStream(bis);
            
            BMvalues=(Vector_Int) RenderUtils.restoreFromStream(bis);
            
            TRvalues = (Vector_Int) RenderUtils.restoreFromStream(bis);
            
            fs = (Vector_Int) RenderUtils.restoreFromStream(bis);
            lw = (Vector_Int) RenderUtils.restoreFromStream(bis);
            
            final int fontCount= (Integer) RenderUtils.restoreFromStream(bis);
            for(int ii=0;ii) RenderUtils.restoreFromStream(bis));
                updatedFont.setCharGlyphs((Map) RenderUtils.restoreFromStream(bis));
                updatedFont.setEmbeddedEncs((Map) RenderUtils.restoreFromStream(bis));
                
            }
            
            bis.close();
            
        }catch(final Exception e){
            LogWriter.writeLog("Exception: " + e.getMessage());
        }
        
        type =DynamicVectorRenderer.DISPLAY_SCREEN;
        //used in loop to draw so needs to be set
        currentItem=pageObjects.get().length;
        
    }
    
    /**stop screen being cleared on repaint - used by Canoo code
     *
     * NOT PART OF API and subject to change (DO NOT USE)
     **/
    public void stopClearOnNextRepaint(final boolean flag) {
        noRepaint=flag;
    }
    
    /**
     * turn object into byte[] so we can move across
     * this way should be much faster than the stadard Java serialise.
     *
     * NOT PART OF API and subject to change (DO NOT USE)
     *
     * @throws IOException
     */
    @Override
    public byte[] serializeToByteArray(final Set fontsAlreadyOnClient) throws IOException{
        
        final ByteArrayOutputStream bos=new ByteArrayOutputStream();
        
        //add a version so we can flag later changes
        bos.write(1);
        
        //flag hires
        //0=no,1=yes
        bos.write(1);
        
        //save page
        bos.write(rawPageNumber);
        
        text_color.trim();
        stroke_color.trim();
        fill_color.trim();
        stroke.trim();
        pageObjects.trim();
        javaObjects.trim();
        stroke.trim();
        pageObjects.trim();
        javaObjects.trim();
        shapeType.trim();
        af1.trim();
        af2.trim();
        af3.trim();
        af4.trim();
        
        clips.trim();
        objectType.trim();
        if(opacity!=null) {
            opacity.trim();
        }
        if(BMvalues!=null) {
            BMvalues.trim();
        }
        
        if(TRvalues!=null) {
            TRvalues.trim();
        }
        
        if(fs!=null) {
            fs.trim();
        }
        
        if(lw!=null) {
            lw.trim();
        }
        
        RenderUtils.writeToStream(bos,x_coord);
        RenderUtils.writeToStream(bos,y_coord);
        RenderUtils.writeToStream(bos,text_color);
        RenderUtils.writeToStream(bos,textFillType);
        stroke_color.writeToStream(bos);
        fill_color.writeToStream(bos);
        
        stroke.writeToStream(bos);
        
        pageObjects.writeToStream(bos);
        
        RenderUtils.writeToStream(bos,javaObjects);
        RenderUtils.writeToStream(bos,shapeType);
        
        RenderUtils.writeToStream(bos,af1);
        RenderUtils.writeToStream(bos,af2);
        RenderUtils.writeToStream(bos,af3);
        RenderUtils.writeToStream(bos,af4);
        
        clips.writeToStream(bos);
        
        RenderUtils.writeToStream(bos,objectType);
        RenderUtils.writeToStream(bos,opacity);
        RenderUtils.writeToStream(bos,BMvalues);
        RenderUtils.writeToStream(bos,TRvalues);
        
        RenderUtils.writeToStream(bos,fs);
        RenderUtils.writeToStream(bos,lw);
        
        int fontCount=0,updateCount=0;
        final Set fontsAlreadySent=new HashSet(10);
        final Set newFontsToSend=new HashSet(10);
        
        for (final String fontUsed : fontsUsed) {
            if (!fontsAlreadyOnClient.contains(fontUsed)) {
                fontCount++;
                newFontsToSend.add(fontUsed);
            } else {
                updateCount++;
                fontsAlreadySent.add(fontUsed);
            }
        }
        
        //new fonts
        RenderUtils.writeToStream(bos, fontCount);

        for (String key : newFontsToSend) {
            
            RenderUtils.writeToStream(bos,key);
            RenderUtils.writeToStream(bos,fonts.get(key));
            
            fontsAlreadyOnClient.add(key);
        }
        
        //new data on existing fonts
        RenderUtils.writeToStream(bos, updateCount);

        for (String key : fontsAlreadySent) {
            
            RenderUtils.writeToStream(bos,key);
            final PdfJavaGlyphs aa = (PdfJavaGlyphs) fonts.get(key);
            RenderUtils.writeToStream(bos,aa.getDisplayValues());
            RenderUtils.writeToStream(bos,aa.getCharGlyphs());
            RenderUtils.writeToStream(bos,aa.getEmbeddedEncs());
            
        }
        
        bos.close();
        
        fontsUsed.clear();
        
        return bos.toByteArray();
    }
    
    /**
     * for font if we are generatign glyph on first render
     */
    public void checkFontSaved(final Object glyph, final String name, final PdfFont currentFontData) {
        
        //save glyph at start
        pageObjects.addElement(glyph);
        objectType.addElement(DynamicVectorRenderer.MARKER);
        areas.addElement(null);
        currentItem++;
        
        if(fontsUsed.contains(name) || currentFontData.isFontSubsetted()){
            fonts.put(name,currentFontData.getGlyphData());
            fontsUsed.add(name);
        }
    }
    
    public void setPrintPage(final int currentPrintPage) {
        rawPageNumber = currentPrintPage;
    }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy