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

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

The newest version!
/*
 * ===========================================
 * 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-2015 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


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

import com.idrsolutions.pdf.color.blends.BlendMode;

import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.PathIterator;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.jpedal.color.ColorSpaces;
import org.jpedal.color.PdfColor;
import org.jpedal.color.PdfPaint;
import org.jpedal.constants.PDFImageProcessing;
import org.jpedal.examples.handlers.DefaultImageHelper;
import org.jpedal.exception.PdfException;
import org.jpedal.external.FontHandler;
import org.jpedal.fonts.PdfFont;
import org.jpedal.fonts.glyph.PdfGlyph;
import org.jpedal.io.ColorSpaceConvertor;
import org.jpedal.io.ObjectStore;
import org.jpedal.objects.GraphicsState;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.Matrix;
import org.jpedal.utils.repositories.Vector_Int;
import org.jpedal.utils.repositories.Vector_Object;
import org.jpedal.utils.repositories.generic.Vector_Rectangle_Int;

public abstract class BaseDisplay implements DynamicVectorRenderer {

    private boolean isRenderingToImage;
        
    /**holds object type*/
    protected Vector_Int objectType;
    
    /**default array size*/
    protected static final int defaultSize=5000;
    
    protected int type;

    boolean isType3Font;
    
    private boolean saveImageData=true;

    /**set flag to show if we add a background*/
    protected boolean addBackground = true;
    
    /**holds rectangular outline to test in redraw*/
    protected Vector_Rectangle_Int areas;

    protected ObjectStore objectStoreRef;
    
    protected int currentItem = -1;
    
    //Used purely to keep track of rendering for colour change functionality
    protected static int itemToRender = -1;
    
    //used to track end of PDF page in display
    protected static int endItem=-1;

    /**raw page rotation*/
    protected int pageRotation;

    Area lastClip;
    
    boolean hasClips;
    
    int blendMode=PdfDictionary.Normal;

    /**shows if colours over-ridden for type3 font*/
    boolean colorsLocked;

    Graphics2D g2;

    /**use hi res images to produce better quality display*/
    public boolean useHiResImageForDisplay;

    boolean extraRot;

    //used by type3 fonts as identifier
    String rawKey;

    /**global colours if set*/
    PdfPaint fillCol,strokeCol;

    public int rawPageNumber;

    int xx, yy;

    public static boolean invertHighlight;

    boolean isPrinting;

    org.jpedal.external.ImageHandler customImageHandler;

    org.jpedal.external.ColorHandler customColorHandler;

    double cropX, cropH;

    float scaling=1, lastScaling;
    
    /**initial Q & D object to hold data*/
    protected Vector_Object pageObjects;
    
    protected final Map imageIDtoName=new HashMap(10);
    
    protected boolean needsHorizontalInvert;
    
    protected boolean needsVerticalInvert;

    /**real size of pdf page */
    int w, h;

    /**background color*/
    protected Color backgroundColor = Color.WHITE;
    protected static Color textColor;
    protected static int colorThresholdToReplace = 255;
    
    protected boolean changeLineArtAndText;

    /**allow user to control*/
    public static RenderingHints userHints;
    
    private Mode mode = Mode.PDF;//declared in DynamicVectorRenderer
    
    @Override
    public void setInset(final int x, final int y) {
	xx = x;
	yy = y;

    }

    @Override
    public void setG2(final Graphics2D g2) {
    	this.g2 = g2;
    	//If user hints has been defined use these values.
    	if(userHints!=null){
    		this.g2.setRenderingHints(userHints);
    	}
    }
    
    @Override
    public void init(final int width, final int height, final int rawRotation, final Color backgroundColor) {
    	w = width;
    	h = height;
    	this.pageRotation = rawRotation;
    	this.backgroundColor = backgroundColor;
    }

    @Override
    public void paintBackground(final Shape dirtyRegion) {
    	if (addBackground && g2!=null){
    			g2.setColor(backgroundColor);

    			if (dirtyRegion == null) {
    				g2.fill(new Rectangle(xx, yy, (int) (w * scaling), (int) (h * scaling)));
    			} else {
    				g2.fill(dirtyRegion);
    			}
    		}
    	}
    

    protected static boolean checkColorThreshold(final int col){
		
    	final int r = (col)&0xFF;
		final int g = (col>>8)&0xFF;
		final int b = (col>>16)&0xFF;

        return r <= colorThresholdToReplace && g <= colorThresholdToReplace && b <= colorThresholdToReplace;
    }

    void renderEmbeddedText(final int text_fill_type, final Object rawglyph, final int glyphType,
	    final AffineTransform glyphAT, final Rectangle textHighlight,
	    PdfPaint strokePaint, PdfPaint fillPaint,
	    final float strokeOpacity, final float fillOpacity, final int lineWidth) {

        //ensure stroke only shows up
        float strokeOnlyLine = 0;
        if (text_fill_type == GraphicsState.STROKE && lineWidth >= 1.0) {
            strokeOnlyLine = lineWidth;
        }

        //get glyph to draw
        final PdfGlyph glyph = (PdfGlyph) rawglyph;

        final AffineTransform at = g2.getTransform();

        //and also as flat values so we can test below
        final double[] affValues = new double[6];
        at.getMatrix(affValues);

        if (glyph != null) {
            
            //set transform
            g2.transform(glyphAT);

            //type of draw operation to use
            final Composite comp = g2.getComposite();

            /**
             * Fill Text
             */
            if ((text_fill_type & GraphicsState.FILL) == GraphicsState.FILL) {
				
            	//If we have an alt text color, its within threshold and not an additional item, use alt color
            	if(textColor!=null && (itemToRender==-1 || (endItem==-1 || itemToRender<=endItem)) && checkColorThreshold(fillPaint.getRGB())){
            		fillPaint = new PdfColor(textColor.getRed(), textColor.getGreen(), textColor.getBlue());
            	}
            	
//                fillPaint.setScaling(cropX, cropH, scaling, 0, 0);
                fillPaint.setScaling(cropX, cropH, scaling, (float)glyphAT.getTranslateX(),(float)glyphAT.getTranslateY());
                
                if (customColorHandler != null) {
                    customColorHandler.setPaint(g2, fillPaint, rawPageNumber, isPrinting);
                } else if (DecoderOptions.Helper != null) {
                    DecoderOptions.Helper.setPaint(g2, fillPaint, rawPageNumber, isPrinting);
                } else {
                    g2.setPaint(fillPaint);
                }

                renderComposite(fillOpacity);
                
                if (textHighlight != null) {
                    if (invertHighlight) {
                        final Color color = g2.getColor();
                        g2.setColor(new Color(255 - color.getRed(), 255 - color.getGreen(), 255 - color.getBlue()));
                    } else if (DecoderOptions.backgroundColor != null) {
                        g2.setColor(DecoderOptions.backgroundColor);
                    }
                }


                //pass down color for drawing text
                if(glyphType==DynamicVectorRenderer.TYPE3 && !glyph.ignoreColors()){
                    glyph.setT3Colors(strokePaint, fillPaint,false);
                }

                glyph.render(GraphicsState.FILL, g2, scaling, false);

                //reset opacity
                g2.setComposite(comp);

            }

            /**
             * Stroke Text (Can be fill and stroke so not in else)
             */
            if (text_fill_type == GraphicsState.STROKE) {
                glyph.setStrokedOnly(true);
            }

            //creates shadow printing to Mac so added work around
            if (DecoderOptions.isRunningOnMac && isPrinting && text_fill_type == GraphicsState.FILLSTROKE) {
            } else if ((text_fill_type & GraphicsState.STROKE) == GraphicsState.STROKE) {

                if (strokePaint != null) {
                	//If we have an alt text color, its within threshold and not an additional item, use alt color
                	if(textColor!=null && (itemToRender==-1 || (endItem==-1 || itemToRender<=endItem)) && checkColorThreshold(strokePaint.getRGB())){
                		strokePaint = new PdfColor(textColor.getRed(), textColor.getGreen(), textColor.getBlue());
                	}
                    strokePaint.setScaling(cropX, cropH, scaling, 0, 0);
                }

                if (customColorHandler != null) {
                    customColorHandler.setPaint(g2, strokePaint, rawPageNumber, isPrinting);
                } else if (DecoderOptions.Helper != null) {
                    DecoderOptions.Helper.setPaint(g2, strokePaint, rawPageNumber, isPrinting);
                } else {
                    g2.setPaint(strokePaint);
                }

                renderComposite(strokeOpacity);

                if (textHighlight != null) {
                    if (invertHighlight) {
                        final Color color = g2.getColor();
                        g2.setColor(new Color(255 - color.getRed(), 255 - color.getGreen(), 255 - color.getBlue()));
                    } else if (DecoderOptions.backgroundColor != null) {
                        g2.setColor(DecoderOptions.backgroundColor);
                    }
                }

                try {
                    glyph.render(GraphicsState.STROKE, g2, strokeOnlyLine, false);
                } catch (final Exception e) {
                    //tell user and log
                    if(LogWriter.isOutput()) {
                        LogWriter.writeLog("Exception: " + e.getMessage());
                    }
                    //
                }

                //reset opacity
                g2.setComposite(comp);
            }

            //restore transform
            g2.setTransform(at);

        }
    }

    void renderShape(final Shape defaultClip, final int fillType, PdfPaint strokeCol, PdfPaint fillCol,
	    final Stroke shapeStroke, final Object currentShape, final float strokeOpacity,
	    final float fillOpacity) {
    
        System.out.println("renderShape in base display should never be called");
    }
    
    void renderShape(final Shape defaultClip, final int fillType, PdfPaint strokeCol, PdfPaint fillCol,
	    final Stroke shapeStroke, final Shape currentShape, final float strokeOpacity,
	    final float fillOpacity) {

    	boolean clipChanged=false;
    	
	final Shape clip = g2.getClip();

	final Composite comp = g2.getComposite();
	
	//stroke and fill (do fill first so we don't overwrite Stroke)
	if (fillType == GraphicsState.FILL || fillType == GraphicsState.FILLSTROKE) {
                // Fill color is null if the shape is a pattern
		if (fillCol != null){
                    if((fillCol.getRGB()!=-1) && 
                            //If we have an alt text color, are changing line art as well, its within threshold and not an additional item, use alt color
                    
                         (changeLineArtAndText && textColor != null && !fillCol.isPattern() && (itemToRender == -1 || (endItem == -1 || itemToRender <= endItem)) && checkColorThreshold(fillCol.getRGB()))) {
                            fillCol = new PdfColor(textColor.getRed(), textColor.getGreen(), textColor.getBlue());
                        
                    }
		
        	    fillCol.setScaling(cropX, cropH, scaling, 0, 0);
                }

	    if (customColorHandler != null) {
	    	customColorHandler.setPaint(g2, fillCol, rawPageNumber, isPrinting);
	    } else if (DecoderOptions.Helper != null) {
            DecoderOptions.Helper.setPaint(g2, fillCol, rawPageNumber, isPrinting);
	    } else {
	    	g2.setPaint(fillCol);
	    }
	    
        renderComposite(fillOpacity);
        
	    try{
            //thin lines do not appear unless we use fillRect
            final double iw=currentShape.getBounds2D().getWidth();
            final double ih=currentShape.getBounds2D().getHeight();

            if((ih==0d || iw==0d) && ((BasicStroke)g2.getStroke()).getLineWidth()<=1.0f){
                g2.fillRect(currentShape.getBounds().x,currentShape.getBounds().y,currentShape.getBounds().width,currentShape.getBounds().height);
            }else {
                g2.fill(currentShape);
            }

        }catch(final Exception e){
	    	if(LogWriter.isOutput()) {
                LogWriter.writeLog("Exception " + e + " filling shape");
            }
	    }
	    
	    g2.setComposite(comp);
	}

	if ((fillType == GraphicsState.STROKE) || (fillType == GraphicsState.FILLSTROKE)) {

	    //set values for drawing the shape
	    final Stroke currentStroke = g2.getStroke();

	    //fix for using large width on point to draw line
	    if (currentShape.getBounds2D().getWidth() < 1.0f && ((BasicStroke) shapeStroke).getLineWidth() > 10) {
	    	g2.setStroke(new BasicStroke(1));
	    } else {
	    	g2.setStroke(shapeStroke);
	    }

	  //If we have an alt text color, are changing line art, its within threshold and not an additional item, use alt color
	    if(changeLineArtAndText && textColor!=null && !strokeCol.isPattern() && (itemToRender==-1 || (endItem==-1 || itemToRender<=endItem)) && checkColorThreshold(strokeCol.getRGB())){
    		strokeCol = new PdfColor(textColor.getRed(), textColor.getGreen(), textColor.getBlue());
    	}
	    
	    strokeCol.setScaling(cropX, cropH, scaling, 0, 0);

	    if (customColorHandler != null) {
	    	customColorHandler.setPaint(g2, strokeCol, rawPageNumber, isPrinting);
	    } else if (DecoderOptions.Helper != null) {
            DecoderOptions.Helper.setPaint(g2, strokeCol, rawPageNumber, isPrinting);
	    } else {
	    	g2.setPaint(strokeCol);
	    }

	    renderComposite(strokeOpacity);
            
            if(!isPrinting && clip != null && clip.getBounds2D().getWidth()%1 > 0.65f && clip.getBounds2D().getHeight()%1 > 0.1f){
                if(currentShape.getBounds().getWidth() == clip.getBounds().getWidth()){
                    g2.setClip(BaseDisplay.convertPDFClipToJavaClip(new Area(clip)));  //use null or visible screen area
                    clipChanged=true;
                }
	    }

	    //breaks printing so disabled there
	    if (!isPrinting && clip != null && (clip.getBounds2D().getHeight() < 1 || clip.getBounds2D().getWidth() < 1)) {
	    	g2.setClip(defaultClip);  //use null or visible screen area
	    	clipChanged=true;
	    }
	    
	    g2.draw(currentShape);
	    g2.setStroke(currentStroke);
	    g2.setComposite(comp);
	}

	if(clipChanged) {
        g2.setClip(clip);
    }
    }

    void renderImage(final AffineTransform imageAf, BufferedImage image, final float alpha,
            final GraphicsState currentGraphicsState, final float x, final float y, final int optionsApplied) {

        final boolean renderDirect = (currentGraphicsState != null);

        if (image == null || g2 == null) {
            return;
        }

        final int w = image.getWidth();
        final int h = image.getHeight();

        //plot image (needs to be flipped as well as co-ords upside down)
        //graphics less than 1 get swallowed if flipped
        AffineTransform upside_down = new AffineTransform();

        boolean applyTransform = false;

        float CTM[][] = new float[3][3];
        if (currentGraphicsState != null) {
            CTM = currentGraphicsState.CTM;
        }

        //special case - ignore rotation
        if (CTM[0][0] < 0 && CTM[1][1] < 0 && CTM[1][0] > -2 && CTM[1][0] < 0 && CTM[0][1] > 0 && CTM[0][1] < 10) {
            CTM[0][1] = 0;
            CTM[1][0] = 0;
        }

        final AffineTransform before = g2.getTransform();

        boolean invertInAff = false;

        float dx = 0, dy = 0;

        /**
         * setup for printing
         */
        if (renderDirect || useHiResImageForDisplay) {

            if (renderDirect) {

                upside_down = null;

                //Turn image around if needed (ie JPEG not yet turned)
                if ((optionsApplied & PDFImageProcessing.IMAGE_INVERTED) != PDFImageProcessing.IMAGE_INVERTED) {

                    if ((CTM[0][1] < 0 && CTM[1][0] > 0) && (CTM[0][0] * CTM[1][1] == 0)) {

                        upside_down = new AffineTransform(CTM[0][0] / w, CTM[0][1] / w, -CTM[1][0] / h, CTM[1][1] / h, CTM[2][0] + CTM[1][0], CTM[2][1]);

                    } else if ((CTM[0][1] != 0 || CTM[1][0] != 0)) {

                        float[][] flip2 = {{1f / w, 0, 0}, {0, -1f / h, 0}, {0, 1f / h, 1}};
                        final float[][] rot = {{CTM[0][0], CTM[0][1], 0},
                        {CTM[1][0], CTM[1][1], 0},
                        {0, 0, 1}};

                        flip2 = Matrix.multiply(flip2, rot);
                        upside_down = new AffineTransform(flip2[0][0], flip2[0][1], flip2[1][0], flip2[1][1], flip2[2][0], flip2[2][1]);

                        if (image.getHeight() > 1) {
                            dx = CTM[2][0] - image.getHeight() * flip2[1][0];
                        } else {
                            dx = CTM[2][0];
                        }

                        dy = CTM[2][1];
                        dy += CTM[1][1];

                    } else if ((CTM[0][0] * CTM[0][1] == 0 && CTM[1][1] * CTM[1][0] == 0) && (CTM[0][1] > 0 && CTM[1][0] > 0)) {
                        float[][] flip2 = {{-1f / w, 0, 0}, {0, 1f / h, 0}, {1f / w, 0, 1}};
                        final float[][] rot = {{CTM[0][0],
                            CTM[0][1], 0},
                        {CTM[1][0], CTM[1][1], 0},
                        {0, 0, 1}};

                        flip2 = Matrix.multiply(flip2, rot);
                        upside_down = new AffineTransform(
                                flip2[0][0], flip2[1][0],
                                flip2[0][1], flip2[1][1],
                                flip2[2][0], flip2[2][1]);

                        dx = CTM[2][0] - image.getHeight() * flip2[0][1];
                        dy = CTM[2][1];

                    } else if (CTM[1][1] != 0) {
                        invertInAff = true;
                    }
                }

                if (upside_down == null) {
                    upside_down = new AffineTransform(CTM[0][0] / w, CTM[0][1] / w,
                            CTM[1][0] / h, CTM[1][1] / h, CTM[2][0], CTM[2][1]);
                }
            } else {
                upside_down = imageAf;

                invertInAff = false;
            }

            applyTransform = true;

        }

        final Composite c = g2.getComposite();

        renderComposite(alpha);

        /**
         * color type3 glyphs if not black
         */
        if (isType3Font && fillCol != null) {

            image = T3ImageUtils.handleType3Image(image, fillCol);

            if (image == null) {
                return;
            }
        }

        if (renderDirect || useHiResImageForDisplay) {

            if (invertInAff && (optionsApplied & PDFImageProcessing.IMAGE_INVERTED) != PDFImageProcessing.IMAGE_INVERTED) {

                final double[] values = new double[6];
                upside_down.getMatrix(values);

                dx = (float) (values[4] + (values[1] * image.getWidth()));
                dy = (float) (values[5] + (image.getHeight() * values[3]));

                //correct rotation case
                if (values[0] > 0 && values[1] > 0 && values[2] > 0 && values[3] < 0) {
                    values[2] = -values[2];
                }

                values[3] = -values[3];

                values[4] = 0;
                values[5] = 0;

                upside_down = new AffineTransform(values);

            }

            //allow user to over-ride
            boolean useCustomRenderer = customImageHandler != null;

            g2.translate(dx, dy);

            if (useCustomRenderer) {
                useCustomRenderer = customImageHandler.drawImageOnscreen(image, optionsApplied, upside_down, null, g2, renderDirect, objectStoreRef, isPrinting);
            }

            //exit if done
            if (useCustomRenderer) {
                g2.setComposite(c);
                return;
            }

            //hack to make bw
            if (customColorHandler != null) {
                final BufferedImage newImage = customColorHandler.processImage(image, rawPageNumber, isPrinting);
                if (newImage != null) {
                    image = newImage;
                }
            } else if (DecoderOptions.Helper != null) {
                final BufferedImage newImage = DecoderOptions.Helper.processImage(image, rawPageNumber, isPrinting);
                if (newImage != null) {
                    image = newImage;
                }
            }

            final Shape g2clip = g2.getClip();
            boolean isClipReset = false;

            //hack to fix clipping issues due to sub-pixels
            if (g2clip != null) {

                final double cy = g2.getClip().getBounds2D().getY();
                final double ch = g2.getClip().getBounds2D().getHeight();
                double diff = image.getHeight() - ch;
                if (diff < 0) {
                    diff = -diff;
                }

                if (diff > 0 && diff < 1 && cy < 0 && image.getHeight() > 1 && image.getHeight() < 10) {

                    final boolean isSimpleOutline = isSimpleOutline(g2.getClip());

                    if (isSimpleOutline) {
                        final double cx = g2.getClip().getBounds2D().getX();
                        final double cw = g2.getClip().getBounds2D().getWidth();

                        g2.setClip(new Rectangle((int) cx, (int) cy, (int) cw, (int) ch));

                        isClipReset = false;
                    }
                }
            }

            AffineTransform aff = g2.getTransform();

            double mx = aff.getScaleX();
            double my = aff.getScaleX();
            double sx = upside_down.getScaleX();
            double sy = upside_down.getScaleY();

            //Rotated images can cause issue when scaling up
            //Only handle rotated page with rotation on image
            if ((image.getType() != 0) && //Catch issue with images with odd types
                    (mx == 0 && my == 0 && sx > 0 && sy < 0)) {
                mx = aff.getShearX();
                my = aff.getShearY();
                sx = Math.abs(sx);
                sy = Math.abs(sy);

                //90 rotation on page
                if (mx > 0 && my > 0) {
                    int newWidth = Math.abs((int) ((image.getWidth() * sx) * mx));
                    int newHeight = Math.abs((int) ((image.getHeight() * sy) * my));

                    //Only use if new image size is large than the original image
                    if (newWidth > 0 && newHeight > 0 && newWidth > image.getWidth() && newHeight > image.getHeight()) {
                        BufferedImage bi = new BufferedImage(newWidth, newHeight, image.getType());
                        Graphics2D g = bi.createGraphics();

                        g.setRenderingHints(g2.getRenderingHints());

                        g.drawImage(image, AffineTransform.getScaleInstance(mx * sx, my * sy), null);

                        upside_down.scale(1 / sx, -(1 / sy));
                        aff.scale(1 / mx, -(1 / my));

                        g2.setTransform(aff);

                        image = bi;
                    }
                }
            }

            //Draw image as normal
            g2.drawImage(image, upside_down, null);

            if (isClipReset) {
                g2.setClip(g2clip);
            }

        } else {

            if (applyTransform) {
                final AffineTransformOp invert = new AffineTransformOp(upside_down, ColorSpaces.hints);
                image = invert.filter(image, null);
            }

            g2.drawImage(image, (int) x, (int) y, null);

        }

        g2.setTransform(before);

        g2.setComposite(c);

    }

    public static boolean isSimpleOutline(final Shape path) {
        
        int count = 0;
        final PathIterator i = path.getPathIterator(null);
        while (!i.isDone() && count < 6) { //see if rectangle or complex clip
            i.next();
            count++;
        }
        return count<6;
    }

    final void renderText(final float x, final float y, final int type, final Area transformedGlyph2,
	    final Rectangle textHighlight, PdfPaint strokePaint,
	    PdfPaint textFillCol, final float strokeOpacity, final float fillOpacity) {

	final Paint currentCol = g2.getPaint();

	
	//type of draw operation to use
	final Composite comp = g2.getComposite();

	if ((type & GraphicsState.FILL) == GraphicsState.FILL) {

	    if (textFillCol != null) {
	    	//If we have an alt text color, its within threshold and not an additional item, use alt color
	    	if(textColor!=null && (itemToRender==-1 || (endItem==-1 || itemToRender<=endItem)) && checkColorThreshold(textFillCol.getRGB())){
	    		textFillCol = new PdfColor(textColor.getRed(), textColor.getGreen(), textColor.getBlue());
	    	}
		textFillCol.setScaling(cropX, cropH, scaling, x, y);
	    }

	    if (customColorHandler != null) {
		customColorHandler.setPaint(g2, textFillCol, rawPageNumber, isPrinting);
	    } else if (DecoderOptions.Helper != null) {
            DecoderOptions.Helper.setPaint(g2, textFillCol, rawPageNumber, isPrinting);
	    } else {
		g2.setPaint(textFillCol);
	    }

            renderComposite(fillOpacity);
	    
	    if (textHighlight != null) {
		if (invertHighlight) {
		    final Color col = g2.getColor();
		    g2.setColor(new Color(255 - col.getRed(), 255 - col.getGreen(), 255 - col.getBlue()));
		} else if (DecoderOptions.backgroundColor != null) {
		    g2.setColor(DecoderOptions.backgroundColor);
		}
	    }

	    g2.fill(transformedGlyph2);

	    //reset opacity
	    g2.setComposite(comp);

	}

	if ((type & GraphicsState.STROKE) == GraphicsState.STROKE) {

	    if (strokePaint != null) {
	    	//If we have an alt text color, its within threshold and not an additional item, use alt color
	    	if(textColor!=null && (itemToRender==-1 || (endItem==-1 || itemToRender<=endItem)) && checkColorThreshold(strokePaint.getRGB())){
	    		strokePaint = new PdfColor(textColor.getRed(), textColor.getGreen(), textColor.getBlue());
	    	}
		strokePaint.setScaling(cropX + x, cropH + y, scaling, x, y);
	    }

	    if (customColorHandler != null) {
		customColorHandler.setPaint(g2, strokePaint, rawPageNumber, isPrinting);
	    } else if (DecoderOptions.Helper != null) {
            DecoderOptions.Helper.setPaint(g2, strokePaint, rawPageNumber, isPrinting);
	    } else {
		g2.setPaint(strokePaint);
	    }

	    renderComposite(strokeOpacity);

	    if (textHighlight != null) {
		if (invertHighlight) {
		    final Color col = g2.getColor();
		    g2.setColor(new Color(255 - col.getRed(), 255 - col.getGreen(), 255 - col.getBlue()));
		} else if (DecoderOptions.backgroundColor != null) {
		    g2.setColor(DecoderOptions.backgroundColor);
		}
	    }

	    //factor in scaling
	    float lineWidth = (float) (1f / g2.getTransform().getScaleX());

	    if (lineWidth < 0) {
		lineWidth = -lineWidth;
	    }

	    g2.setStroke(new BasicStroke(lineWidth));

	    if (lineWidth < 0.1f) {
		g2.draw(transformedGlyph2);
	    } else {
		g2.fill(transformedGlyph2);
	    }

	    //reset opacity
	    g2.setComposite(comp);
	}

	g2.setPaint(currentCol);
    }

    //used internally - please do not use
    @Override
    public ObjectStore getObjectStore() {
	return objectStoreRef;
    }

    /**
     * Screen drawing using hi res images and not down-sampled images but may be slower
     * and use more memory
     */
    @Override
    public void setHiResImageForDisplayMode(final boolean useHiResImageForDisplay) {
	    this.useHiResImageForDisplay = useHiResImageForDisplay;

    }

    @Override
    public void setScalingValues(final double cropX, final double cropH, final float scaling) {

	this.cropX = cropX;
	this.cropH = cropH;
	this.scaling = scaling;

    }

    @Override
    public void setCustomImageHandler(final org.jpedal.external.ImageHandler customImageHandler) {
	this.customImageHandler = customImageHandler;
    }

    @Override
    public void setCustomColorHandler(final org.jpedal.external.ColorHandler colorController) {
	this.customColorHandler = colorController;
    }

    ////////////////////NOT used except by screen/////////////////////////////////
    /**
     * reset on colorspace change to ensure cached data up to data
     */
    @Override
    public void resetOnColorspaceChange() {
	//To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void drawFontBounds(final Rectangle newfontBB) {
    }

    /**
     * store af info
     */
    @Override
    public void drawAffine(final double[] afValues) {
	//To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * store af info
     */
    @Override
    public void drawFontSize(final int fontSize) {
	//To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * store line width info
     */
    @Override
    public void setLineWidth(final int lineWidth) {
	//To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * stop screen bein cleared on repaint - used by Canoo code
     * 
* NOT PART OF API and subject to change (DO NOT USE) */ @Override public void stopClearOnNextRepaint(final boolean flag) { //To change body of implemented methods use File | Settings | File Templates. } @Override public boolean hasObjectsBehind(final float[][] CTM){ boolean hasObject = false; int x = (int) CTM[2][0]; int y = (int) CTM[2][1]; int w = (int) CTM[0][0]; if (w == 0) { w = (int) CTM[0][1]; } int h = (int)CTM[1][1]; if (h == 0) { h = (int) CTM[1][0]; } //if h or w are negative, reverse values //as intersects and contains can't cope with it if (h < 0) { y += h; //h = y - h; } if (w < 0) { x += w; w = x - w; } //JavaFX does not store locations so we should just return true if(this.areas==null){ return true; } final int[][] areas = this.areas.get(); final int count = areas.length; int rx,ry,rw,rh; for (int i = 0; i < count; i++) { if (areas[i] != null) { //find if overlap and exit once found rx=areas[i][0]; ry=areas[i][1]; rw=areas[i][2]; rh=areas[i][3]; //if(rw==0 || rh==0){ // continue; // } final boolean xOverlap = valueInRange(x, rx, rx + rw) || valueInRange(rx, x, x + w); final boolean yOverlap = xOverlap && valueInRange(y, ry, ry + rh) || valueInRange(ry, y, y + h); if(xOverlap && yOverlap){ //first match i=count; hasObject=true; } } } return hasObject; } private static boolean valueInRange(final int value, final int min, final int max) { return (value >= min && value <= max); } /** * operations to do once page done */ @Override public void flagDecodingFinished() { //To change body of implemented methods use File | Settings | File Templates. } @Override public void flagImageDeleted(final int i) { //To change body of implemented methods use File | Settings | File Templates. } @Override public void setOCR(final boolean isOCR) { //To change body of implemented methods use File | Settings | File Templates. } /** * 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 java.io.IOException */ @Override public byte[] serializeToByteArray(final Set fontsAlreadyOnClient) throws IOException { return new byte[0]; } /** * for font if we are generatign glyph on first render */ @Override public void checkFontSaved(final Object glyph, final String name, final PdfFont currentFontData) { //To change body of implemented methods use File | Settings | File Templates. } /** * This method is deprecated, please use getAreaAsArray and * create fx/swing rectangles where needed. * @deprecated * @param i * @return */ @Override public Rectangle getArea(final int i) { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override /** * Returns a Rectangles X,Y,W,H as an Array of integers * Where 0 = x, 1 = y, 2 = w, 3 = h. */ public int[] getAreaAsArray(final int i){ return areas.elementAt(i); } /** * return number of image in display queue * or -1 if none * @return */ @Override public int isInsideImage(final int x, final int y){ int outLine=-1; final int[][] areas=this.areas.get(); int[] possArea = null; final int count=areas.length; if(objectType!=null){ final int[] types=objectType.get(); for(int i=0;i -1; i--) { if ((areas[i] != null && RenderUtils.rectangleContains(areas[i], x, y)) && (types[i] != DynamicVectorRenderer.SHAPE && types[i] != DynamicVectorRenderer.CLIP)) { nothing = false; typeFound = types[i]; i = -1; } } if (nothing) { return -1; } } return typeFound; } @Override public void setneedsVerticalInvert(final boolean b) { needsVerticalInvert = b; } @Override public void setneedsHorizontalInvert(final boolean b) { needsHorizontalInvert=b; } /** * just for printing */ @Override public void stopG2HintSetting(final boolean isSet) { //To change body of implemented methods use File | Settings | File Templates. } @Override public void setPrintPage(final int currentPrintPage) { //To change body of implemented methods use File | Settings | File Templates. } @Override public void drawShape(final Shape currentShape, final GraphicsState currentGraphicsState, final int cmd) { // throw new UnsupportedOperationException("Not supported yet."); } @Override public void drawCustom(final Object value) { // throw new UnsupportedOperationException("Not supported yet."); } @Override public void drawEmbeddedText(final float[][] Trm, final int fontSize, final PdfGlyph embeddedGlyph, final Object javaGlyph, final int type, final GraphicsState gs, final double[] at, final String glyf, final PdfFont currentFontData, final float glyfWidth) { //throw new UnsupportedOperationException("Not supported yet."); } @Override public void paint(final Rectangle[] highlights, final AffineTransform viewScaling, final Rectangle userAnnot) { // throw new UnsupportedOperationException("Not supported yet."); } @Override public void setMessageFrame(final Container frame) { // throw new UnsupportedOperationException("Not supported yet."); } @Override public void dispose() { } @Override public int drawImage(final int pageNumber, final BufferedImage image, final GraphicsState currentGraphicsState, final boolean alreadyCached, final String name, final int optionsApplied, final int previousUse) { return -1; } @Override public void drawFillColor(final PdfPaint currentCol) { } @Override public void drawAdditionalObjectsOverPage(final int[] type, final Color[] colors, final Object[] obj) throws PdfException { } @Override public void flushAdditionalObjOnPage() { } @Override public void setOptimsePainting(final boolean optimsePainting) { // throw new UnsupportedOperationException("Not supported yet."); } @Override public void flush() { //throw new UnsupportedOperationException("Not supported yet."); } @Override public void drawText(final float[][] Trm, final String text, final GraphicsState currentGraphicsState, final float x, final float y, final Font javaFont) { //throw new UnsupportedOperationException("Not supported yet."); } @Override public Rectangle getOccupiedArea() { return null;//throw new UnsupportedOperationException("Not supported yet."); } @Override public void setGraphicsState(final int fillType, final float value, final int BM) { //throw new UnsupportedOperationException("Not supported yet."); } @Override public void drawStrokeColor(final Paint currentCol) { //throw new UnsupportedOperationException("Not supported yet."); } @Override public void drawTR(final int value) { //throw new UnsupportedOperationException("Not supported yet."); } @Override public void drawStroke(final Stroke current) { // throw new UnsupportedOperationException("Not supported yet."); } @Override public void drawClip(final GraphicsState currentGraphicsState, final Shape defaultClip, final boolean alwaysDraw) { // throw new UnsupportedOperationException("Not supported yet."); } /** * used by some custom version of DynamicVectorRenderer */ @Override public void writeCustom(final int key, final Object value) { } /** allow tracking of specific commands**/ @Override public void flagCommand(final int commandID, final int tokenNumber) { } @Override public void setValue(final int key, final int i) { switch(key){ case ALT_BACKGROUND_COLOR: backgroundColor = new Color(i); break; case ALT_FOREGROUND_COLOR: textColor = new Color(i); break; case FOREGROUND_INCLUDE_LINEART: changeLineArtAndText = i > 0; break; case COLOR_REPLACEMENT_THRESHOLD: colorThresholdToReplace = i; break; } } @Override public int getValue(final int key) { //used by HTML to get font handing mode, etc //this is the unused 'dummy' default implementation required for other modes as in Interface return -1; } /** * used by Pattern code internally (do not use) * @return */ @Override public BufferedImage getSingleImagePattern() { return null; //To change body of implemented methods use File | Settings | File Templates. } /**used by JavaFX and HTML5 conversion to override scaling*/ @Override public boolean isScalingControlledByUser() { return false; } /**used by HTML to retai nimage quality*/ @Override public boolean avoidDownSamplingImage() { return false; } /** * allow user to read */ @Override public boolean getBooleanValue(final int key) { return false; } /** * page scaling used by HTML code only * @return */ @Override public float getScaling() { return scaling; } /** * only used in HTML5 and SVG conversion * * @param baseFontName * @param s * @param potentialWidth */ @Override public void saveAdvanceWidth(final String baseFontName, final String s, final int potentialWidth) { } public static int isRectangle(final Shape bounds) { int count = 0; final PathIterator i = bounds.getPathIterator(null); while (!i.isDone() && count < 8) { //see if rectangle or complex clip i.next(); count++; } return count; } @Override public void setMode(final Mode mode) { this.mode = mode; } @Override public Mode getMode() { return mode; } @Override //used by HTML/SVG mode only public Object getObjectValue(final int id) { return null; } /*save shape in array to draw*/ @Override public void drawShape(final Object currentShape, final GraphicsState currentGraphicsState, final int cmd) { System.out.println("drawShape in BaseDisplay Should never be called"); } /*save shape in array to draw*/ @Override public void eliminateHiddenText(final Shape currentShape, final GraphicsState gs, final int count, boolean ignoreScaling) { } private void renderComposite(final float alpha) { if(blendMode==PdfDictionary.Normal || blendMode==PdfDictionary.Compatible){ if (alpha != 1.0f) { g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); } }else{/// if (alpha != 1.0f){ - possible fix for 19888 to test final Composite comp=new BlendMode(blendMode,alpha); g2.setComposite(comp); } } @Override public boolean isHTMLorSVG() { return false; } //public Graphics2D getG2() { // return g2; // } @Override public void saveImageData(final boolean value) { saveImageData=value; } @Override public boolean saveImageData() { return saveImageData; } /** * @return the isRenderingToImage */ public boolean isRenderingToImage() { return isRenderingToImage; } /** * @param isRenderingToImage the isRenderingToImage to set */ @Override public void setIsRenderingToImage(boolean isRenderingToImage) { this.isRenderingToImage = isRenderingToImage; } /** * Increases clip size without altering input area * @param clip The clipping areas that needs increasing * @return Area for the modified clip size */ public static Area convertPDFClipToJavaClip(Area clip){ if (clip != null) { //Increase clips size by 1 pixel in all direction as pdf clip includes bounds, //java only handles inside of bounds double sx = (clip.getBounds2D().getWidth() + 2) / clip.getBounds2D().getWidth(); double sy = (clip.getBounds2D().getHeight() + 2) / clip.getBounds2D().getHeight(); double posX = clip.getBounds2D().getX(); double posY = clip.getBounds2D().getY(); Area a = (Area) clip.clone(); a.transform(AffineTransform.getTranslateInstance(-posX, -posY)); a.transform(AffineTransform.getScaleInstance(sx, sy)); a.transform(AffineTransform.getTranslateInstance(posX - 1, posY - 1)); return a; } return clip; } @Override public FontHandler getFontHandler(){ return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy