aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/util/AbstractPaintingState.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/org/apache/fop/util/AbstractPaintingState.java')
-rw-r--r--src/java/org/apache/fop/util/AbstractPaintingState.java531
1 files changed, 531 insertions, 0 deletions
diff --git a/src/java/org/apache/fop/util/AbstractPaintingState.java b/src/java/org/apache/fop/util/AbstractPaintingState.java
new file mode 100644
index 000000000..4fb6b173c
--- /dev/null
+++ b/src/java/org/apache/fop/util/AbstractPaintingState.java
@@ -0,0 +1,531 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.util;
+
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * A base class which holds information about the current painting state.
+ */
+public abstract class AbstractPaintingState implements Cloneable, Serializable {
+
+ private static final long serialVersionUID = 5998356138437094188L;
+
+ /** current state data */
+ private AbstractData data = null;
+
+ /** the state stack */
+ private StateStack/*<AbstractData>*/ stateStack = new StateStack/*<AbstractData>*/();
+
+ /**
+ * Instantiates a new state data object
+ *
+ * @return a new state data object
+ */
+ protected abstract AbstractData instantiateData();
+
+ /**
+ * Instantiates a new state object
+ *
+ * @return a new state object
+ */
+ protected abstract AbstractPaintingState instantiate();
+
+ /**
+ * Returns the currently valid state
+ *
+ * @return the currently valid state
+ */
+ public AbstractData getData() {
+ if (data == null) {
+ data = instantiateData();
+ }
+ return data;
+ }
+
+ /**
+ * Set the current color.
+ * Check if the new color is a change and then set the current color.
+ *
+ * @param col the color to set
+ * @return true if the color has changed
+ */
+ public boolean setColor(Color col) {
+ if (!col.equals(getData().color)) {
+ getData().color = col;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the color.
+ *
+ * @return the color
+ */
+ public Color getColor() {
+ if (getData().color == null) {
+ getData().color = Color.black;
+ }
+ return getData().color;
+ }
+
+ /**
+ * Get the background color.
+ *
+ * @return the background color
+ */
+ public Color getBackColor() {
+ if (getData().backColor == null) {
+ getData().backColor = Color.white;
+ }
+ return getData().backColor;
+ }
+
+ /**
+ * Set the current background color.
+ * Check if the new background color is a change and then set the current background color.
+ *
+ * @param col the background color to set
+ * @return true if the color has changed
+ */
+ public boolean setBackColor(Color col) {
+ if (!col.equals(getData().backColor)) {
+ getData().backColor = col;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set the current font name
+ *
+ * @param internalFontName the internal font name
+ * @return true if the font name has changed
+ */
+ public boolean setFontName(String internalFontName) {
+ if (!internalFontName.equals(getData().fontName)) {
+ getData().fontName = internalFontName;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Gets the current font name
+ *
+ * @return the current font name
+ */
+ public String getFontName() {
+ return getData().fontName;
+ }
+
+ /**
+ * Gets the current font size
+ *
+ * @return the current font size
+ */
+ public int getFontSize() {
+ return getData().fontSize;
+ }
+
+ /**
+ * Set the current font size.
+ * Check if the font size is a change and then set the current font size.
+ *
+ * @param size the font size to set
+ * @return true if the font size has changed
+ */
+ public boolean setFontSize(int size) {
+ if (size != getData().fontSize) {
+ getData().fontSize = size;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Set the current line width.
+ *
+ * @param width the line width in points
+ * @return true if the line width has changed
+ */
+ public boolean setLineWidth(float width) {
+ if (getData().lineWidth != width) {
+ getData().lineWidth = width;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the current line width
+ *
+ * @return the current line width
+ */
+ public float getLineWidth() {
+ return getData().lineWidth;
+ }
+
+ /**
+ * Sets the dash array (line type) for the current basic stroke
+ *
+ * @param dash the line dash array
+ * @return true if the dash array has changed
+ */
+ public boolean setDashArray(float[] dash) {
+ if (!Arrays.equals(dash, getData().dashArray)) {
+ getData().dashArray = dash;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the current transform.
+ * This gets the combination of all transforms in the
+ * current state.
+ *
+ * @return the calculate combined transform for the current state
+ */
+ public AffineTransform getTransform() {
+ AffineTransform at = new AffineTransform();
+ for (Iterator iter = stateStack.iterator(); iter.hasNext();) {
+ AbstractData data = (AbstractData)iter.next();
+ AffineTransform stackTrans = data.getTransform();
+ at.concatenate(stackTrans);
+ }
+ AffineTransform currentTrans = getData().getTransform();
+ at.concatenate(currentTrans);
+ return at;
+ }
+
+ /**
+ * Check the current transform.
+ * The transform for the current state is the combination of all
+ * transforms in the current state. The parameter is compared
+ * against this current transform.
+ *
+ * @param tf the transform the check against
+ * @return true if the new transform is different then the current transform
+ */
+ public boolean checkTransform(AffineTransform tf) {
+ return !tf.equals(getData().getTransform());
+ }
+
+ /**
+ * Get a copy of the base transform for the page. Used to translate
+ * IPP/BPP values into X,Y positions when positioning is "fixed".
+ *
+ * @return the base transform, or null if the state stack is empty
+ */
+ public AffineTransform getBaseTransform() {
+ if (stateStack.isEmpty()) {
+ return null;
+ } else {
+ AbstractData baseData = (AbstractData)stateStack.get(0);
+ return (AffineTransform) baseData.getTransform().clone();
+ }
+ }
+
+ /**
+ * Concatenates the given AffineTransform to the current one.
+ *
+ * @param at the transform to concatenate to the current level transform
+ */
+ public void concatenate(AffineTransform at) {
+ getData().concatenate(at);
+ }
+
+ /**
+ * Resets the current AffineTransform to the Base AffineTransform.
+ */
+ public void resetTransform() {
+ getData().setTransform(getBaseTransform());
+ }
+
+ /**
+ * Clears the current AffineTransform to the Identity AffineTransform
+ */
+ public void clearTransform() {
+ getData().clearTransform();
+ }
+
+
+ /**
+ * Save the current painting state.
+ * This pushes the current painting state onto the stack.
+ * This call should be used when the Q operator is used
+ * so that the state is known when popped.
+ */
+ public void save() {
+ AbstractData copy = (AbstractData)getData().clone();
+ stateStack.push(copy);
+ }
+
+ /**
+ * Restore the current painting state.
+ * This pops the painting state from the stack and sets current values to popped state.
+ *
+ * @return the restored state, null if the stack is empty
+ */
+ public AbstractData restore() {
+ if (!stateStack.isEmpty()) {
+ setData((AbstractData)stateStack.pop());
+ return this.data;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Save all painting state data.
+ * This pushes all painting state data in the given list to the stack
+ *
+ * @param dataList a state data list
+ */
+ public void saveAll(List/*<AbstractData>*/ dataList) {
+ Iterator it = dataList.iterator();
+ while (it.hasNext()) {
+ // save current data on stack
+ save();
+ setData((AbstractData)it.next());
+ }
+ }
+
+ /**
+ * Restore all painting state data.
+ * This pops all painting state data from the stack
+ *
+ * @return a list of state data popped from the stack
+ */
+ public List/*<AbstractData>*/ restoreAll() {
+ List/*<AbstractData>*/ dataList = new java.util.ArrayList/*<AbstractData>*/();
+ AbstractData data;
+ while (true) {
+ data = getData();
+ if (restore() == null) {
+ break;
+ }
+ // insert because of stack-popping
+ dataList.add(0, data);
+ }
+ return dataList;
+ }
+
+ /**
+ * Sets the current state data
+ *
+ * @param currentData state data
+ */
+ protected void setData(AbstractData data) {
+ this.data = data;
+ }
+
+ /**
+ * Clears the state stack
+ */
+ public void clear() {
+ stateStack.clear();
+ setData(null);
+ }
+
+ /**
+ * Return the state stack
+ *
+ * @return the state stack
+ */
+ protected Stack/*<AbstractData>*/ getStateStack() {
+ return this.stateStack;
+ }
+
+ /** {@inheritDoc} */
+ public Object clone() {
+ AbstractPaintingState state = instantiate();
+ state.stateStack = new StateStack(this.stateStack);
+ state.data = (AbstractData)this.data.clone();
+ return state;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return ", stateStack=" + stateStack
+ + ", currentData=" + data;
+ }
+
+
+ /**
+ * A stack implementation which holds state objects
+ */
+ public class StateStack extends java.util.Stack {
+
+ private static final long serialVersionUID = 4897178211223823041L;
+
+ /**
+ * Default constructor
+ */
+ public StateStack() {
+ super();
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param c initial contents of stack
+ */
+ public StateStack(Collection c) {
+ elementCount = c.size();
+ // 10% for growth
+ elementData = new Object[
+ (int)Math.min((elementCount * 110L) / 100, Integer.MAX_VALUE)];
+ c.toArray(elementData);
+ }
+ }
+
+
+ /**
+ * A base painting state data holding object
+ */
+ public abstract class AbstractData implements Cloneable, Serializable {
+
+ private static final long serialVersionUID = 5208418041189828624L;
+
+ /** The current color */
+ protected Color color = null;
+
+ /** The current background color */
+ protected Color backColor = null;
+
+ /** The current font name */
+ protected String fontName = null;
+
+ /** The current font size */
+ protected int fontSize = 0;
+
+ /** The current line width */
+ protected float lineWidth = 0;
+
+ /** The dash array for the current basic stroke (line type) */
+ protected float[] dashArray = null;
+
+ /** The current transform */
+ protected AffineTransform transform = null;
+
+ /**
+ * Returns a newly create data object
+ *
+ * @return a new data object
+ */
+ protected abstract AbstractData instantiate();
+
+ /**
+ * Concatenate the given AffineTransform with the current thus creating
+ * a new viewport. Note that all concatenation operations are logged
+ * so they can be replayed if necessary (ex. for block-containers with
+ * "fixed" positioning.
+ *
+ * @param at Transformation to perform
+ */
+ public void concatenate(AffineTransform at) {
+ getTransform().concatenate(at);
+ }
+
+ /**
+ * Get the current AffineTransform.
+ *
+ * @return the current transform
+ */
+ public AffineTransform getTransform() {
+ if (transform == null) {
+ transform = new AffineTransform();
+ }
+ return transform;
+ }
+
+ /**
+ * Sets the current AffineTransform.
+ */
+ public void setTransform(AffineTransform baseTransform) {
+ this.transform = baseTransform;
+ }
+
+ /**
+ * Resets the current AffineTransform.
+ */
+ public void clearTransform() {
+ transform = new AffineTransform();
+ }
+
+ /**
+ * Returns the derived rotation from the current transform
+ *
+ * @return the derived rotation from the current transform
+ */
+ public int getDerivedRotation() {
+ AffineTransform at = getTransform();
+ double sx = at.getScaleX();
+ double sy = at.getScaleY();
+ double shx = at.getShearX();
+ double shy = at.getShearY();
+ int rotation = 0;
+ if (sx == 0 && sy == 0 && shx > 0 && shy < 0) {
+ rotation = 270;
+ } else if (sx < 0 && sy < 0 && shx == 0 && shy == 0) {
+ rotation = 180;
+ } else if (sx == 0 && sy == 0 && shx < 0 && shy > 0) {
+ rotation = 90;
+ } else {
+ rotation = 0;
+ }
+ return rotation;
+ }
+
+ /** {@inheritDoc} */
+ public Object clone() {
+ AbstractData data = instantiate();
+ data.color = this.color;
+ data.backColor = this.backColor;
+ data.fontName = this.fontName;
+ data.fontSize = this.fontSize;
+ data.lineWidth = this.lineWidth;
+ data.dashArray = this.dashArray;
+ data.transform = new AffineTransform(this.transform);
+ return data;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return "color=" + color
+ + ", backColor=" + backColor
+ + ", fontName=" + fontName
+ + ", fontSize=" + fontSize
+ + ", lineWidth=" + lineWidth
+ + ", dashArray=" + dashArray
+ + ", transform=" + transform;
+ }
+ }
+}