
org.jpedal.render.BaseDisplay 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-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;
/**internal flag to control how we turn images*/
boolean optimisedTurnCode = true;
/**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 = scaling;
}
//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) {
final Stroke currentStoke = g2.getStroke();
final boolean useNewCode = true;
if (lineWidth != 0 || (useNewCode && text_fill_type==GraphicsState.FILLSTROKE)) {
float w = lineWidth * (float)g2.getTransform().getScaleX();
if (w < 0) {
w = -w;
}
//hack to allow for some PDFs which use a FILL and STROKE (TR 2) to make font look Bold
if((text_fill_type==GraphicsState.FILLSTROKE || useNewCode) && (isPrinting || useNewCode)){
// if(w<1)
// w=1;
w += (32-(float)g2.getTransform().getScaleX());
}
g2.setStroke(new BasicStroke(w));
}
//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);
if (lineWidth != 0) {
g2.setStroke(currentStoke);
}
}
}
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 (!optimisedTurnCode) {
image = RenderUtils.invertImage(image);
} else {
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];
//special case
//if (CTM[0][0] < 0 && CTM[1][0] < 0 && CTM[0][1] > 0 && CTM[1][1] < 0) {
// System.out.println("a");
//} else if (CTM[1][1] != 0) {
dy += CTM[1][1];
// System.out.println("b");
//}
} 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 = ((optionsApplied & PDFImageProcessing.TURN_ON_DRAW) == PDFImageProcessing.TURN_ON_DRAW);
}
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) {
try {
if (optimisedTurnCode && (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);
}
} catch (final Exception e) {
//tell user and log
if(LogWriter.isOutput()) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
//
}
} else {
try {
if (applyTransform) {
final AffineTransformOp invert = new AffineTransformOp(upside_down, ColorSpaces.hints);
image = invert.filter(image, null);
}
g2.translate(x, y);
if (optimisedTurnCode && (optionsApplied & PDFImageProcessing.TURN_ON_DRAW) == PDFImageProcessing.TURN_ON_DRAW) {
final AffineTransform flip2;
float[] flip = {1f, 0f, 0f, -1f, 0f, image.getHeight()};
final AffineTransform flip3;
if (pageRotation == 0) {
flip2 = new AffineTransform(flip);
} else if (pageRotation == 90) {
flip2 = new AffineTransform();
if (extraRot) {
flip2.rotate(Math.PI, 0, 0);
} else {
flip2.rotate(Math.PI / 2, 0, 0);
}
flip2.translate(-image.getWidth(), -image.getHeight());
flip = new float[]{-1f, 0f, 0f, 1f, image.getWidth(), 0};//(float)image.getHeight()};
flip3 = new AffineTransform(flip);
flip2.concatenate(flip3);
} else if (pageRotation == 180) {
flip2 = new AffineTransform();
if (extraRot) {
flip2.rotate(Math.PI, 0, 0);
}
flip2.translate(-image.getWidth(), -image.getHeight());
flip = new float[]{-1f, 0f, 0f, 1f, image.getWidth(), 0};//(float)image.getHeight()};
flip3 = new AffineTransform(flip);
flip2.concatenate(flip3);
} else {
flip2 = new AffineTransform();
if (extraRot) {
flip2.rotate(Math.PI, 0, 0);
} else {
flip2.rotate(Math.PI / 2 + Math.PI, 0, 0);
}
flip2.translate(-image.getWidth(), -image.getHeight());
flip = new float[]{-1f, 0f, 0f, 1f, image.getWidth(), 0};
flip3 = new AffineTransform(flip);
flip2.concatenate(flip3);
}
g2.drawImage(image, flip2, null);
} else {
g2.drawImage(image, 0, 0, null);
}
g2.translate(-x, -y);
} catch (final Exception e) {
//tell user and log
if(LogWriter.isOutput()) {
LogWriter.writeLog("Exception: " + e.getMessage());
}
//
}
}
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 - 2025 Weber Informatics LLC | Privacy Policy