All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.objects.GraphicsState 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-2017 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
@LICENSE@
*
* ---------------
* GraphicsState.java
* ---------------
*/
package org.jpedal.objects;
import java.awt.BasicStroke;
import java.awt.Stroke;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import javafx.scene.shape.Shape;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import org.jpedal.color.*;
import org.jpedal.external.ExternalHandlers;
import org.jpedal.javafx.JavaFXSupport;
import org.jpedal.objects.raw.PdfArrayIterator;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
/**
* holds the graphics state as stream decoded
*/
public class GraphicsState {
//hold image co-ords
public float x, y;
TextState currentTextState = new TextState();
//transparency
private float strokeAlpha = 1.0f;
private float nonstrokeAlpha = 1.0f;
private float maxStrokeAlpha = 1.0f;
private float maxNonstrokeAlpha = 1.0f;
/**
* copy used for images
*/
public final float[][] lastCTM = new float[3][3];
//TR value
private PdfObject TRobj;
private byte[][] TR;
public PdfObject SMask;
/**
* stroke colorspace
*/
public GenericColorSpace strokeColorSpace = new DeviceGrayColorSpace();
/**
* nonstroke colorspace
*/
public GenericColorSpace nonstrokeColorSpace = new DeviceGrayColorSpace();
private boolean hasClipChanged;
//overprinting
private boolean op;
private boolean OP;
private float OPM;
private PdfPaint nonstrokeColor = new PdfColor(0, 0, 0);
private PdfPaint strokeColor = new PdfColor(0, 0, 0);
/**
* holds current clipping shape
*/
private Area current_clipping_shape;
private PdfClip current_clip;
private static final boolean debugClip = false;
/**
* CTM which is used for plotting (see pdf
* spec for explanation
*/
public float[][] CTM = new float[3][3];
public float[][] scaleFactor;
/**
* dash of lines (phase) for drawing
*/
private int current_line_dash_phase;
/**
* used for TR effect
*/
private GeneralPath TRmask;
/**
* fill type for drawing
*/
private int fill_type;
/**
* mitre limit for drawing - Set default limit to 10 as per the PDF spec
*/
private int mitre_limit = 10;
/**
* dash of lines (array) for drawing
*/
private float[] current_line_dash_array = new float[0];
/**
* join style of lines for drawing
*/
private int current_line_cap_style;
/**
* width of lines for drawing
*/
private float current_line_width = 1;
/**
* width of lines to use when outputting to HTML/SVG etc
**/
private int output_line_width = -1;
/**
* join of lines for drawing
*/
private int current_line_join_style;
/**
* Type of draw to use
*/
private int text_render_type = GraphicsState.FILL;
/**
* displacement to allow for negative page displacement
*/
private int minX; //%%
/**
* displacement to allow for negative page displacement
*/
private int minY; //%%
public static final int STROKE = 1;
public static final int FILL = 2;
public static final int FILLSTROKE = 3;
public static final int INVISIBLE = 4;
public static final int CLIPTEXT = 7;
private int BMvalue = PdfDictionary.Normal;
private Area area;
public GraphicsState(final GraphicsState gs) {
resetCTM();
BMvalue = gs.BMvalue;
}
public GraphicsState() {
resetCTM();
}
/**
* initialise the GraphicsState
*
* @param minX
* @param minY
*/
public GraphicsState(final int minX, final int minY) {
this.minX = -minX; /*keep%%*/
this.minY = -minY; /*keep%%*/
resetCTM();
}
public void setMaxAlpha(final int type, final float value) {
switch (type) {
case STROKE:
this.maxStrokeAlpha = value;
break;
case FILL:
this.maxNonstrokeAlpha = value;
break;
}
}
public void setAlpha(final int type, float value) {
switch (type) {
case STROKE:
if (value > maxStrokeAlpha) {
value = maxStrokeAlpha;
}
this.strokeAlpha = value;
break;
case FILL:
if (value > maxNonstrokeAlpha) {
value = maxNonstrokeAlpha;
}
this.nonstrokeAlpha = value;
break;
}
}
/**
* use STROKE or FILL
*
* @param type
* @return
*/
public float getAlpha(final int type) {
float value = 1f;
switch (type) {
case STROKE:
if (strokeAlpha > maxStrokeAlpha) {
value = maxStrokeAlpha;
} else {
value = this.strokeAlpha;
}
break;
case FILL:
if (nonstrokeAlpha > maxNonstrokeAlpha) {
value = maxNonstrokeAlpha;
} else {
value = this.nonstrokeAlpha;
}
break;
}
return value;
}
/**
* use STROKE or FILL
*
* @param type
* @return
*/
public float getAlphaMax(final int type) {
float value = 1f;
switch (type) {
case STROKE:
value = this.maxStrokeAlpha;
break;
case FILL:
value = this.maxNonstrokeAlpha;
break;
}
return value;
}
/**
* get stroke op
*
* @return
*/
public boolean getNonStrokeOP() {
return this.op;
}
/**
* get stroke op
*
* @return
*/
public float getOPM() {
return this.OPM;
}
public Object[] getTR() {
if (TRobj == null && TR == null) {
return null;
} else {
return new Object[]{TRobj, TR};
}
}
//////////////////////////////////////////////////////////////////////////
/**
* set text render type
*
* @param text_render_type
*/
public final void setTextRenderType(final int text_render_type) {
this.text_render_type = text_render_type;
TRmask = null;
area = null;
}
//////////////////////////////////////////////////////////////////////////
/**
* set text render type
*
* @return
*/
public final int getTextRenderType() {
return text_render_type;
}
///////////////////////////////////////////////////////////////////////////
/**
* set mitre limit
*
* @param mitre_limit
*/
public final void setMitreLimit(final int mitre_limit) {
this.mitre_limit = mitre_limit;
}
/**
* get line width
*
* @return
*/
public final float getLineWidth() {
return current_line_width;
}
/**
* get line width for HTML/SVG etc. value is -1 if not set.
*
* @return
*/
public final int getOutputLineWidth() {
return output_line_width;
}
//////////////////////////////////////////////////////////////////////////
/**
* set fill type
*
* @param fill_type
*/
public final void setFillType(final int fill_type) {
this.fill_type = fill_type;
}
//////////////////////////////////////////////////////////////////////////
/**
* update clip
*
* @param current_area
*/
public final void updateClip(final Area current_area) {
// System.out.println("Update clip "+current_area.getBounds());
if (current_clipping_shape == null || current_area == null) {
current_clipping_shape = current_area;
hasClipChanged = true;
} else { // if(current_clipping_shape.intersects(current_area.getBounds2D().getX(),current_area.getBounds2D().getY(),
//current_area.getBounds2D().getWidth(),current_area.getBounds2D().getHeight())){
current_clipping_shape.intersect(current_area);
hasClipChanged = true;
}
if (debugClip) {
System.out.println("[updateClip]");
if (current_clipping_shape == null) {
System.out.println("Null shape");
} else {
System.out.println("Shape bounds= " + current_clipping_shape.getBounds());
}
}
}
/**
* add to clip (used for TR 7)
*
* @param current_area
*/
public final void addClip(final Area current_area) {
if (TRmask == null) {
TRmask = new GeneralPath();
area = null;
}
TRmask.append(current_area, false);
}
//////////////////////////////////////////////////////////////////////////
/**
* get the current stroke to be used - basic solid line or pattern
*
* @return
*/
public final Stroke getStroke() {
//hold the stroke for the path
final Stroke current_stroke;
//factor in scaling to line width
float w = current_line_width;
if (CTM[0][0] != 0) {
w *= CTM[0][0];
} else if (CTM[0][1] != 0) {
w *= CTM[0][1];
}
if (w < 0) {
w = -w;
}
//check values all in legal boundaries
if (mitre_limit < 1) {
mitre_limit = 1;
}
final int dashCount = current_line_dash_array.length;
if (dashCount > 0) {
//factor in scaling
final float[] dash = new float[dashCount];
for (int aa = 0; aa < dashCount; aa++) {
if (CTM[0][0] != 0) {
dash[aa] = current_line_dash_array[aa] * CTM[0][0];
} else {
dash[aa] = current_line_dash_array[aa] * CTM[0][1];
}
if (dash[aa] < 0) {
dash[aa] = -dash[aa];
}
//smaller values throw a segmentation fault in JVM (not pretty)
if (dash[aa] < 0.07f) {
dash[aa] = 0.07f;
}
}
current_stroke = new BasicStroke(w, current_line_cap_style, current_line_join_style, mitre_limit, dash, Math.abs(current_line_dash_phase));
} else {
current_stroke = new BasicStroke(w, current_line_cap_style, current_line_join_style, mitre_limit);
}
return current_stroke;
}
/**
* Applies the stroke stored in the GraphicsState to the shape being passed in
*
* @param shape
*/
public final void applyFXStroke(final Shape shape) {
if (shape == null) {
return;
}
//factor in scaling to line width
float w = current_line_width;
//we factor in when we draw so not needed
// if(CTM[0][0]!=0) {
// w *= CTM[0][0];
// } else if( CTM[0][1]!=0) {
// w *= CTM[0][1];
// }
if (w < 0) {
w = -w;
}
//check values all in legal boundaries
if (mitre_limit < 1) {
mitre_limit = 1;
}
final int dashCount = current_line_dash_array.length;
if (dashCount > 0) {
//factor in scaling
final float[] dash = new float[dashCount];
for (int aa = 0; aa < dashCount; aa++) {
// if(CTM[0][0]!=0) {
// dash[aa] = current_line_dash_array[aa] * CTM[0][0];
// } else {
// dash[aa] = current_line_dash_array[aa] * CTM[0][1];
// }
if (dash[aa] < 0) {
dash[aa] = -dash[aa];
}
//smaller values throw a segmentation fault in JVM (not pretty)
if (dash[aa] < 0.05f) {
dash[aa] = 0.05f;
}
}
// Clear the current dash array and add values from dash
shape.getStrokeDashArray().clear();
for (final double value : dash) {
shape.getStrokeDashArray().add(value);
}
shape.setStrokeDashOffset(Math.abs(current_line_dash_phase));
}
shape.setStrokeWidth(w);
// Converts from Swing constants to FX constants
switch (current_line_cap_style) {
case 0:
shape.setStrokeLineCap(StrokeLineCap.BUTT);
break;
case 1:
shape.setStrokeLineCap(StrokeLineCap.ROUND);
break;
case 2:
shape.setStrokeLineCap(StrokeLineCap.SQUARE);
break;
}
switch (current_line_join_style) {
case 0:
shape.setStrokeLineJoin(StrokeLineJoin.MITER);
break;
case 1:
shape.setStrokeLineJoin(StrokeLineJoin.ROUND);
break;
case 2:
shape.setStrokeLineJoin(StrokeLineJoin.BEVEL);
break;
}
shape.setStrokeMiterLimit(mitre_limit);
}
/**
* get the stroke width after being adjusted by the CTM
*
* @return
*/
public float getCTMAdjustedLineWidth() {
//factor in scaling to line width
float w = current_line_width;
if (CTM[0][0] != 0) {
w *= CTM[0][0];
} else if (CTM[0][1] != 0) {
w *= CTM[0][1];
}
if (w < 0) {
w = -w;
}
return w;
}
//////////////////////////////////////////////////////////////////////////
/**
* set line width
*
* @param current_line_width
*/
public final void setLineWidth(final float current_line_width) {
this.current_line_width = current_line_width;
}
/**
* get the stroke width after being adjusted by the CTM
*
* @param width CTM adjusted line width
*/
public void setCTMAdjustedLineWidth(float width) {
//factor out scaling to line width
if (CTM[0][0] != 0) {
width /= CTM[0][0];
} else if (CTM[0][1] != 0) {
width /= CTM[0][1];
}
current_line_width = width;
}
/**
* set line width for HTML/SVG etc. value is -1 if not set.
*/
public final void setOutputLineWidth(final int output_line_width) {
this.output_line_width = output_line_width;
}
/**
* get clipping shape
*
* @return
*/
public final Area getClippingShape() {
if (TRmask != null) {
if (area == null) {
area = new Area(TRmask);
}
return area;
// }else if(TRmask!=null){
//
// // if(TRmask.intersects(current_clipping_shape.getBounds()))
//
// final Area clipAsArea=new Area(TRmask);
//
// clipAsArea.intersect(current_clipping_shape);
//
// if(debugClip){
// System.out.println("[getClippingShape1]");
// if(current_clipping_shape==null){
// System.out.println("Null shape");
// }else{
// System.out.println("Shape bounds= "+current_clipping_shape.getBounds());
// }
// }
//
// return clipAsArea;
} else {
if (debugClip) {
System.out.println("[getClippingShape2]");
if (current_clipping_shape == null) {
System.out.println("Null shape");
} else {
System.out.println("Shape bounds= " + current_clipping_shape.getBounds());
}
}
return current_clipping_shape;
}
}
/**
* read GS settings and set supported values
*
* @param GS
*/
public void setMode(final PdfObject GS) {
op = false;
OP = false;
if (GS == null) {
return;
}
final float LW = GS.getFloatNumber(PdfDictionary.LW);
if (LW != -1) {
current_line_width = LW;
}
/*
* set transparency
*/
final boolean AIS = GS.getBoolean(PdfDictionary.AIS);
SMask = GS.getDictionary(PdfDictionary.SMask);
final boolean notMask = (SMask == null || AIS || SMask.getGeneralType(PdfDictionary.SMask) == PdfDictionary.None);
if (notMask) {
final float newCA = GS.getFloatNumber(PdfDictionary.CA);
final float newca = GS.getFloatNumber(PdfDictionary.ca);
if (newCA != -1) {
setAlpha(GraphicsState.STROKE, newCA);
}
if (newca != -1) {
setAlpha(GraphicsState.FILL, newca);
}
}
//set overprinting
OP = GS.getBoolean(PdfDictionary.OP);
op = GS.getBoolean(PdfDictionary.op);
final float newOPM = GS.getFloatNumber(PdfDictionary.OPM);
if (newOPM != -1) {
OPM = newOPM;
} else {
OPM = 0;
}
TRobj = GS.getDictionary(PdfDictionary.TR);
TR = GS.getKeyArray(PdfDictionary.TR);
//transferFunction
if (TRobj != null || TR != null) {
boolean isIdentity = false;
if (TRobj != null && TRobj.getGeneralType(PdfDictionary.TR) == PdfDictionary.Identity) {
isIdentity = true;
} else if (TR != null) { //see if object or colors
final int count = TR.length;
if (count > 0) {
isIdentity = true;
for (final byte[] aMaskArray : TR) {
final int nextID = PdfDictionary.getIntKey(1, aMaskArray.length - 1, aMaskArray);
//System.out.println("ii="+ii+" "+nextID+" "+PdfDictionary.Identity+" "+new String(maskArray[ii]));
if (nextID != PdfDictionary.Identity) {
isIdentity = false;
break;
}
}
}
}
if (isIdentity) {
TRobj = null;
TR = null;
}
}
//set BM if present
final PdfArrayIterator BM = GS.getMixedArray(PdfDictionary.BM);
if (BM != null && BM.hasMoreTokens()) {
BMvalue = BM.getNextValueAsConstant(false);
// System.out.println("BMvalue="+BMvalue+" "+BM.getNextValueAsString(false));
// if(BMvalue!=PdfDictionary.Overlay && BMvalue!=PdfDictionary.Multiply
//// && BMvalue != PdfDictionary.Screen
// && BMvalue != PdfDictionary.HardLight){
//
// BMvalue=PdfDictionary.Normal;
// }
}
}
/**
* set line join style
*
* @param cap_style
*/
public final void setCapStyle(final int cap_style) {
this.current_line_cap_style = cap_style;
}
/**
* set line join style
*
* @param join_style
*/
public final void setJoinStyle(final int join_style) {
this.current_line_join_style = join_style;
}
/**
* check whole page clip (if whole page set clip to null)
*
* @param max_y
*/
public final void checkWholePageClip(final int max_y) {
if ((current_clipping_shape != null && current_clipping_shape.getBounds().getHeight() > max_y + 2) && //2 is margin for error needed on some files
(current_clipping_shape.getBounds().y >= 0)) {
current_clipping_shape = null;
hasClipChanged = true;
if (debugClip) {
System.out.println("[checkWholePageClip] current_clipping_shape=" + current_clipping_shape);
if (current_clipping_shape == null) {
System.out.println("Null shape");
} else {
System.out.println("Shape bounds= " + current_clipping_shape.getBounds());
}
}
}
}
/**
* set dash array
*
* @param current_line_dash_array
*/
public final void setDashArray(final float[] current_line_dash_array) {
this.current_line_dash_array = current_line_dash_array;
}
/**
* custom clone method
*/
public final GraphicsState deepCopy() {
final GraphicsState newGS = new GraphicsState();
newGS.x = x;
newGS.y = y;
if (TR != null) {
newGS.TR = TR;
}
if (TRobj != null) {
newGS.TRobj = TRobj;
}
newGS.maxNonstrokeAlpha = maxNonstrokeAlpha;
newGS.maxStrokeAlpha = maxStrokeAlpha;
newGS.strokeAlpha = strokeAlpha;
newGS.nonstrokeAlpha = nonstrokeAlpha;
newGS.op = op;
newGS.OP = OP;
newGS.OPM = OPM;
newGS.nonstrokeColor = nonstrokeColor;
newGS.strokeColor = strokeColor;
if (current_clipping_shape != null) {
newGS.current_clipping_shape = (Area) current_clipping_shape.clone();
}
if (CTM != null) {
for (int i = 0; i < 3; i++) {
System.arraycopy(CTM[i], 0, newGS.CTM[i], 0, 3);
}
}
newGS.hasClipChanged = hasClipChanged;
newGS.current_line_dash_phase = current_line_dash_phase;
if (TRmask != null) {
newGS.TRmask = (GeneralPath) TRmask.clone();
}
newGS.fill_type = fill_type;
newGS.mitre_limit = mitre_limit;
if (current_line_dash_array != null) {
final int size = current_line_dash_array.length;
newGS.current_line_dash_array = new float[size];
System.arraycopy(current_line_dash_array, 0, newGS.current_line_dash_array, 0, size);
}
newGS.current_line_cap_style = current_line_cap_style;
newGS.current_line_width = current_line_width;
newGS.current_line_join_style = current_line_join_style;
newGS.text_render_type = text_render_type;
newGS.minX = minX;
newGS.minY = minY;
return newGS;
}
/**
* reset CTM
*/
private void resetCTM() {
//init CTM
CTM[0][0] = (float) 1.0;
CTM[1][0] = (float) 0.0;
CTM[2][0] = minX;
CTM[0][1] = (float) 0.0;
CTM[1][1] = (float) 1.0;
CTM[2][1] = minY;
CTM[0][2] = (float) 0.0;
CTM[1][2] = (float) 0.0;
CTM[2][2] = (float) 1.0;
}
/**
* set dash phase
*
* @param current_line_dash_phase
*/
public final void setDashPhase(final int current_line_dash_phase) {
this.current_line_dash_phase = current_line_dash_phase;
}
/**
* get fill type
*
* @return
*/
public final int getFillType() {
return fill_type;
}
/**
* set clipping shape
*
* @param new_clip
*/
public final void setClippingShape(final Area new_clip) {
this.current_clipping_shape = new_clip;
hasClipChanged = true;
if (debugClip) {
System.out.println("[setClippingShape]");
if (current_clipping_shape == null) {
System.out.println("Null shape");
} else {
System.out.println("Shape bounds= " + current_clipping_shape.getBounds());
}
}
}
/**
* @return Returns the currentNonstrokeColor.
*/
public PdfPaint getNonstrokeColor() {
return nonstrokeColor;
}
/**
* @param currentNonstrokeColor The currentNonstrokeColor to set.
*/
public void setNonstrokeColor(final PdfPaint currentNonstrokeColor) {
this.nonstrokeColor = currentNonstrokeColor;
}
/**
* @return Returns the strokeColor.
*/
public PdfPaint getStrokeColor() {
return strokeColor;
}
/**
* @param strokeColor The strokeColor to set.
*/
public void setStrokeColor(final PdfPaint strokeColor) {
this.strokeColor = strokeColor;
}
/**
* tell software if clip has changed and
* return
*
* @return
*/
public boolean hasClipChanged() {
final boolean flag = hasClipChanged;
hasClipChanged = false;
return flag;
}
public void setTextState(final TextState currentTextState) {
this.currentTextState = currentTextState;
}
public TextState getTextState() {
return currentTextState;
}
public void updateClip(final Object fxPath) {
// Initialise when needed
if (current_clip == null) {
final JavaFXSupport fxSupport = ExternalHandlers.getFXHandler();
if (fxSupport != null) {
current_clip = fxSupport.getFXClip();
}
}
hasClipChanged = current_clip.updateClip(fxPath);
}
public Shape getFXClippingShape() {
if (current_clip == null) {
return null;
}
return (Shape) current_clip.getClippingShape();
}
public int getBMValue() {
return BMvalue;
}
public void setBMValue(final int bm) {
BMvalue = bm;
}
@Override
public String toString() {
final String str = "GraphicsState : BM:" + BMvalue + " op:" + op + " OP:" + OP
+ " opm:" + OPM + " sAlpha:" + strokeAlpha + " nsAlpha:" + nonstrokeAlpha
+ " msAlpha:" + maxStrokeAlpha + " mnsAlpha:" + maxNonstrokeAlpha + " smask:" + SMask;
return str;
}
public void resetColorSpaces(final int strokeColorData, final int nonStrokeColorData) {
// System.out.println("reset "+nonstrokeColorSpace+"="+nonStrokeColorData+" ");
if (strokeColorSpace.getID() != ColorSpaces.Pattern) {
strokeColorSpace.invalidateCaching(strokeColorData);
strokeColorSpace.setColor(new PdfColor(strokeColorData));
}
if (nonstrokeColorSpace.getID() != ColorSpaces.Pattern) {
nonstrokeColorSpace.invalidateCaching(nonStrokeColorData);
nonstrokeColorSpace.setColor(new PdfColor(nonStrokeColorData));
}
}
}