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

org.jpedal.objects.GraphicsState Maven / Gradle / Ivy

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


 *
 * ---------------
 * GraphicsState.java
 * ---------------
 */
package org.jpedal.objects;

import java.awt.BasicStroke;
import java.awt.Stroke;
import java.awt.geom.Area;

import org.jpedal.color.DeviceRGBColorSpace;
import org.jpedal.color.GenericColorSpace;
import org.jpedal.color.PdfColor;
import org.jpedal.color.PdfPaint;
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 float[][] lastCTM = new float[3][3];

	// TR value
	private PdfObject TR;

	public PdfObject SMask;

	/** stroke colorspace */
	public GenericColorSpace strokeColorSpace = new DeviceRGBColorSpace();

	/** nonstroke colorspace */
	public GenericColorSpace nonstrokeColorSpace = new DeviceRGBColorSpace();

	private boolean hasClipChanged = false;

	// overprinting
	private boolean op = false;
	private boolean OP = false;

	private float OPM = 0;

	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 = null;

	private final boolean debugClip = false;

	/**
	 * CTM which is used for plotting (see pdf spec for explanation
	 */
	public float[][] CTM = new float[3][3];

	/** dash of lines (phase) for drawing */
	private int current_line_dash_phase = 0;

	/** used for TR effect */
	private Area TRmask = null;

	/** fill type for drawing */
	private int fill_type;

	/** mitre limit for drawing */
	private int mitre_limit = 0;

	/** 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 = 0;

	/** 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 = 0;

	/** Type of draw to use */
	private int text_render_type = GraphicsState.FILL;

	/** displacement to allow for negative page displacement */
	private int minX = 0;// %%

	/** displacement to allow for negative page displacement */
	private int minY = 0;// %%

	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 PdfArrayIterator BM;

	public GraphicsState() {
		resetCTM();
	}

	/**
	 * initialise the GraphicsState
	 * 
	 * @param minX
	 * @param minY
	 */
	public GraphicsState(int minX, int minY) {
		this.minX = -minX;/* keep%% */
		this.minY = -minY;/* keep%% */
		resetCTM();
	}

	public void setMaxAlpha(int type, float value) {

		switch (type) {

			case STROKE:

				this.maxStrokeAlpha = value;
				break;

			case FILL:
				this.maxNonstrokeAlpha = value;
				break;

		}
	}

	public void setAlpha(int type, float value) {

		switch (type) {

			case STROKE:

				if (value > this.maxStrokeAlpha) value = this.maxStrokeAlpha;
				this.strokeAlpha = value;
				break;

			case FILL:
				if (value > this.maxNonstrokeAlpha) value = this.maxNonstrokeAlpha;
				this.nonstrokeAlpha = value;
				break;

		}
	}

	/**
	 * use STROKE or FILL
	 * 
	 * @param type
	 */
	public float getAlpha(int type) {

		float value = 1f;

		switch (type) {

			case STROKE:
				if (this.strokeAlpha > this.maxStrokeAlpha) {
					value = this.maxStrokeAlpha;
				}
				else {
					value = this.strokeAlpha;
				}
				break;

			case FILL:
				if (this.nonstrokeAlpha > this.maxNonstrokeAlpha) {
					value = this.maxNonstrokeAlpha;
				}
				else {
					value = this.nonstrokeAlpha;
				}
				break;

		}

		return value;
	}

	/**
	 * use STROKE or FILL
	 * 
	 * @param type
	 */
	public float getAlphaMax(int type) {

		float value = 1f;

		switch (type) {

			case STROKE:
				value = this.maxStrokeAlpha;
				break;

			case FILL:
				value = this.maxNonstrokeAlpha;
				break;

		}

		return value;
	}

	/*
	 * get stroke op* public boolean getStrokeOP(){ return this.OP; } /
	 */

	/**
	 * get stroke op
	 * 
	 */
	public boolean getNonStrokeOP() {
		return this.op;
	}

	/**
	 * get stroke op
	 * 
	 */
	public float getOPM() {
		return this.OPM;
	}

	public PdfObject getTR() {
		return this.TR;
	}

	// ////////////////////////////////////////////////////////////////////////
	/**
	 * set text render type
	 * 
	 * @param text_render_type
	 */
	final public void setTextRenderType(int text_render_type) {
		this.text_render_type = text_render_type;

		this.TRmask = null;
	}

	// ////////////////////////////////////////////////////////////////////////
	/**
	 * set text render type
	 * 
	 */
	final public int getTextRenderType() {
		return this.text_render_type;
	}

	// /////////////////////////////////////////////////////////////////////////
	/**
	 * set mitre limit
	 * 
	 * @param mitre_limit
	 */
	final public void setMitreLimit(int mitre_limit) {
		this.mitre_limit = mitre_limit;
	}

	/**
	 * get line width
	 * 
	 */
	final public float getLineWidth() {
		return this.current_line_width;
	}

	/**
	 * get line width for HTML/SVG etc. value is -1 if not set.
	 * 
	 */
	final public int getOutputLineWidth() {
		return this.output_line_width;
	}

	// ////////////////////////////////////////////////////////////////////////
	/**
	 * set fill type
	 * 
	 * @param fill_type
	 */
	final public void setFillType(int fill_type) {
		this.fill_type = fill_type;
	}

	// ////////////////////////////////////////////////////////////////////////
	/**
	 * update clip
	 * 
	 * @param current_area
	 */
	final public void updateClip(Area current_area) {
		// System.out.println("Update clip "+current_area.getBounds());
		if (this.current_clipping_shape == null || current_area == null) {
			this.current_clipping_shape = current_area;
			this.hasClipChanged = true;
		}
		else {// if(current_clipping_shape.intersects(current_area.getBounds2D().getX(),current_area.getBounds2D().getY(),
				// current_area.getBounds2D().getWidth(),current_area.getBounds2D().getHeight())){
			this.current_clipping_shape.intersect(current_area);
			/**
			 * if( current_clipping_shape.getBounds().getHeight() == 0 ){ current_clipping_shape = current_area; System.out.println("Reset to area");
			 * }
			 */
			this.hasClipChanged = true;
		}

		if (this.debugClip) {
			System.out.println("[checkWholePageClip]");
			if (this.current_clipping_shape == null) {
				System.out.println("Null shape");
			}
			else {
				System.out.println("Shape bounds= " + this.current_clipping_shape.getBounds());
			}
		}
	}

	/**
	 * add to clip (used for TR 7)
	 * 
	 * @param current_area
	 */
	final public void addClip(Area current_area) {

		if (this.TRmask == null) this.TRmask = current_area;
		else {
			this.TRmask.add(current_area);
		}
	}

	// ////////////////////////////////////////////////////////////////////////
	/**
	 * @return the current stroke to be used - basic solid line or pattern 
	 */
	final public Stroke getStroke() {

		// hold the stroke for the path
		Stroke current_stroke;

		// factor in scaling to line width
		float w = this.current_line_width;
		if (this.CTM[0][0] != 0) w = w * this.CTM[0][0];
		else
			if (this.CTM[0][1] != 0) w = w * this.CTM[0][1];

		if (w < 0) w = -w;

		// check values all in legal boundaries
		if (this.mitre_limit < 1) this.mitre_limit = 1;

		int dashCount = this.current_line_dash_array.length;
		if (dashCount > 0) {
			// factor in scaling
			float[] dash = new float[dashCount];
			for (int aa = 0; aa < dashCount; aa++) {
				if (this.CTM[0][0] != 0) dash[aa] = this.current_line_dash_array[aa] * this.CTM[0][0];
				else dash[aa] = this.current_line_dash_array[aa] * this.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;
				}
			}
			current_stroke = new BasicStroke(w, this.current_line_cap_style, this.current_line_join_style, this.mitre_limit, dash,
					this.current_line_dash_phase);
		}
		else {
			current_stroke = new BasicStroke(w, this.current_line_cap_style, this.current_line_join_style, this.mitre_limit);
		}

		return current_stroke;
	}

	/**
	 * @return the stroke width after being adjusted by the CTM
	 */
	public float getCTMAdjustedLineWidth() {
		// factor in scaling to line width
		float w = this.current_line_width;
		if (this.CTM[0][0] != 0) w = w * this.CTM[0][0];
		else
			if (this.CTM[0][1] != 0) w = w * this.CTM[0][1];

		if (w < 0) w = -w;

		return w;
	}

	// ////////////////////////////////////////////////////////////////////////
	/**
	 * set line width
	 * 
	 * @param current_line_width
	 */
	final public void setLineWidth(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 (this.CTM[0][0] != 0) width = width / this.CTM[0][0];
		else
			if (this.CTM[0][1] != 0) width = width / this.CTM[0][1];

		this.current_line_width = width;
	}

	/**
	 * set line width for HTML/SVG etc. value is -1 if not set.
	 * 
	 */
	public final void setOutputLineWidth(int output_line_width) {
		this.output_line_width = output_line_width;
	}

	/**
	 * @return clipping shape
	 */
	final public Area getClippingShape() {
		if (this.TRmask != null && this.current_clipping_shape == null) {
			return this.TRmask;
		}
		else
			if (this.TRmask != null) {

				// if(TRmask.intersects(current_clipping_shape.getBounds()))
				this.TRmask.intersect(this.current_clipping_shape);

				if (this.debugClip) {
					System.out.println("[getClippingShape1]");
					if (this.current_clipping_shape == null) {
						System.out.println("Null shape");
					}
					else {
						System.out.println("Shape bounds= " + this.current_clipping_shape.getBounds());
					}
				}

				return this.TRmask;
			}
			else {

				if (this.debugClip) {
					System.out.println("[getClippingShape2]");
					if (this.current_clipping_shape == null) {
						System.out.println("Null shape");
					}
					else {
						System.out.println("Shape bounds= " + this.current_clipping_shape.getBounds());
					}
				}

				return this.current_clipping_shape;
			}
	}

	/**
	 * read GS settings and set supported values
	 * 
	 * @param GS
	 */
	public void setMode(PdfObject GS) {

		/** set to defaults */
		// strokeAlpha=1.0f;
		this.nonstrokeAlpha = 1.0f;
		this.op = false;
		this.OP = false;

		if (GS == null) return;

		float LW = GS.getFloatNumber(PdfDictionary.LW);

		if (LW != -1) this.current_line_width = LW;

		/**
		 * set transparency
		 */
		boolean AIS = GS.getBoolean(PdfDictionary.AIS);
		this.SMask = GS.getDictionary(PdfDictionary.SMask);

		boolean notMask = (this.SMask == null || AIS || this.SMask.getGeneralType(PdfDictionary.SMask) == PdfDictionary.None);

		if (notMask) {
			float newCA = GS.getFloatNumber(PdfDictionary.CA), newca = GS.getFloatNumber(PdfDictionary.ca);
			if (newCA != -1) setAlpha(GraphicsState.STROKE, newCA);

			if (newca != -1) setAlpha(GraphicsState.FILL, newca);
		}

		// set overprinting
		this.OP = GS.getBoolean(PdfDictionary.OP);
		this.op = GS.getBoolean(PdfDictionary.op);

		float newOPM = GS.getFloatNumber(PdfDictionary.OPM);
		if (newOPM != -1) this.OPM = newOPM;
		else this.OPM = 0;

		this.TR = GS.getDictionary(PdfDictionary.TR);

		// transferFunction
		if (this.TR != null) {

			boolean isIdentity = false;

			if (this.TR.getGeneralType(PdfDictionary.TR) == PdfDictionary.Identity) {
				isIdentity = true;
			}
			else {
				byte[][] maskArray = this.TR.getKeyArray(PdfDictionary.TR);

				// see if object or colors
				if (maskArray != null) {

					int count = maskArray.length;
					if (count > 0) {

						isIdentity = true;

						for (byte[] aMaskArray : maskArray) {
							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) this.TR = null;

		}

		// set BM if present
		this.BM = GS.getMixedArray(PdfDictionary.BM);
	}

	/**
	 * set line join style
	 * 
	 * @param cap_style
	 */
	final public void setCapStyle(int cap_style) {
		this.current_line_cap_style = cap_style;
	}

	/**
	 * set line join style
	 * 
	 * @param join_style
	 */
	final public void setJoinStyle(int join_style) {
		this.current_line_join_style = join_style;
	}

	/**
	 * check whole page clip (if whole page set clip to null)
	 * 
	 * @param max_y
	 */
	final public void checkWholePageClip(int max_y) {

		if (this.current_clipping_shape != null && this.current_clipping_shape.getBounds().getHeight() > max_y + 2) { // 2 is margin for error needed
																														// on some files

			if (this.current_clipping_shape.getBounds().y >= 0) {
				this.current_clipping_shape = null;

				this.hasClipChanged = true;

				if (this.debugClip) {
					System.out.println("[checkWholePageClip]");
					if (this.current_clipping_shape == null) {
						System.out.println("Null shape");
					}
					else {
						System.out.println("Shape bounds= " + this.current_clipping_shape.getBounds());
					}
				}
			}
		}
	}

	/**
	 * set dash array
	 * 
	 * @param current_line_dash_array
	 */
	final public void setDashArray(float[] current_line_dash_array) {
		this.current_line_dash_array = current_line_dash_array;
	}

	/**
	 * custom clone method
	 */
	@Override
	final public Object clone() {

		GraphicsState newGS = new GraphicsState();

		newGS.x = this.x;
		newGS.y = this.y;

		if (this.TR != null) newGS.TR = (PdfObject) this.TR.clone();

		newGS.maxNonstrokeAlpha = this.maxNonstrokeAlpha;
		newGS.maxStrokeAlpha = this.maxStrokeAlpha;

		newGS.strokeAlpha = this.strokeAlpha;
		newGS.nonstrokeAlpha = this.nonstrokeAlpha;

		newGS.op = this.op;
		newGS.OP = this.OP;
		newGS.OPM = this.OPM;

		newGS.nonstrokeColor = this.nonstrokeColor;
		newGS.strokeColor = this.strokeColor;

		if (this.current_clipping_shape != null) newGS.current_clipping_shape = (Area) this.current_clipping_shape.clone();

		if (this.CTM != null) {
			for (int i = 0; i < 3; i++) {
				System.arraycopy(this.CTM[i], 0, newGS.CTM[i], 0, 3);
			}
		}

		newGS.hasClipChanged = this.hasClipChanged;

		newGS.current_line_dash_phase = this.current_line_dash_phase;

		if (this.TRmask != null) newGS.TRmask = (Area) this.TRmask.clone();

		newGS.fill_type = this.fill_type;

		newGS.mitre_limit = this.mitre_limit;

		if (this.current_line_dash_array != null) {
			int size = this.current_line_dash_array.length;
			newGS.current_line_dash_array = new float[size];
			System.arraycopy(this.current_line_dash_array, 0, newGS.current_line_dash_array, 0, size);
		}

		newGS.current_line_cap_style = this.current_line_cap_style;

		newGS.current_line_width = this.current_line_width;

		newGS.current_line_join_style = this.current_line_join_style;

		newGS.text_render_type = this.text_render_type;

		newGS.minX = this.minX;

		newGS.minY = this.minY;

		return newGS;
	}

	/**
	 * reset CTM
	 */
	private void resetCTM() {
		// init CTM
		this.CTM[0][0] = (float) 1.0;
		this.CTM[1][0] = (float) 0.0;
		this.CTM[2][0] = this.minX;
		this.CTM[0][1] = (float) 0.0;
		this.CTM[1][1] = (float) 1.0;
		this.CTM[2][1] = this.minY;
		this.CTM[0][2] = (float) 0.0;
		this.CTM[1][2] = (float) 0.0;
		this.CTM[2][2] = (float) 1.0;
	}

	/**
	 * set dash phase
	 * 
	 * @param current_line_dash_phase
	 */
	final public void setDashPhase(int current_line_dash_phase) {
		this.current_line_dash_phase = current_line_dash_phase;
	}

	/**
	 * get fill type
	 * 
	 */
	final public int getFillType() {
		return this.fill_type;
	}

	/**
	 * set clipping shape
	 * 
	 * @param new_clip
	 */
	final public void setClippingShape(Area new_clip) {
		this.current_clipping_shape = new_clip;

		this.hasClipChanged = true;

		if (this.debugClip) {
			System.out.println("[setClippingShape]");

			if (this.current_clipping_shape == null) {
				System.out.println("Null shape");
			}
			else {
				System.out.println("Shape bounds= " + this.current_clipping_shape.getBounds());
			}
		}
	}

	/**
	 * @return Returns the currentNonstrokeColor.
	 */
	public PdfPaint getNonstrokeColor() {
		return this.nonstrokeColor;
	}

	/**
	 * @param currentNonstrokeColor
	 *            The currentNonstrokeColor to set.
	 */
	public void setNonstrokeColor(PdfPaint currentNonstrokeColor) {
		this.nonstrokeColor = currentNonstrokeColor;
	}

	/**
	 * @return Returns the strokeColor.
	 */
	public PdfPaint getStrokeColor() {
		return this.strokeColor;
	}

	/**
	 * @param strokeColor
	 *            The strokeColor to set.
	 */
	public void setStrokeColor(PdfPaint strokeColor) {
		this.strokeColor = strokeColor;
	}

	public PdfArrayIterator getBM() {
		return this.BM;
	}

	/**
	 * tell software if clip has changed and return
	 * 
	 */
	public boolean hasClipChanged() {

		boolean flag = this.hasClipChanged;

		this.hasClipChanged = false;
		return flag;
	}

	public void setTextState(TextState currentTextState) {
		this.currentTextState = currentTextState;
	}

	public TextState getTextState() {
		return this.currentTextState;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy