Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
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) {
//When we allow transparency in printing there are cases that the JDK can not support, add a work around here to prevent issue.
//Added for Case 27484
if(isPrinting && img.getTransparency()==Transparency.TRANSLUCENT &&
((img.getHeight()==1 && afValues4[afCount]<1)
// || (img.getWidth()==1 && afValues1[afCount]<1) //Fix for similar issue with width. Commented out until example is found to confirm
)){
final AffineTransform imageAf=new AffineTransform(afValues1[afCount],afValues2[afCount],afValues3[afCount],1,x,y);
renderImage(imageAf, img, fillOpacity, null, x, y);
}else{
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;
}
}