blob: 70fe0b449b2aaf93ad8daa2f01279dc1d4ab57c2 [file] [log] [blame]
/*
* 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.render.java2d;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.GeneralPath;
import org.apache.xmlgraphics.java2d.color.ColorUtil;
import org.apache.fop.fo.Constants;
import org.apache.fop.fonts.FontInfo;
/**
* Keeps information about the current state of the Graphics2D currentGraphics.
* It is also used as a stack to hold a graphics context.
* <p>
* The graphics context is updated with the updateXXX() methods.
*/
public class Java2DGraphicsState {
/** Holds the datas of the current state */
private Graphics2D currentGraphics;
private BasicStroke currentStroke;
private float currentStrokeWidth;
private int currentStrokeStyle;
/** Font configuration, passed from AWTRenderer */
private FontInfo fontInfo;
/** Initial AffinTransform passed by the renderer, includes scaling-info */
private AffineTransform initialTransform;
/**
* State for storing graphics state.
* @param graphics the graphics associated with the BufferedImage
* @param fontInfo the FontInfo from the renderer
* @param at the initial AffineTransform containing the scale transformation
*/
public Java2DGraphicsState(Graphics2D graphics, FontInfo fontInfo,
AffineTransform at) {
this.fontInfo = fontInfo;
this.currentGraphics = graphics;
this.initialTransform = at;
currentGraphics.setTransform(at);
}
/**
* Copy constructor.
* @param org the instance to copy
*/
public Java2DGraphicsState(Java2DGraphicsState org) {
this.currentGraphics = (Graphics2D)org.currentGraphics.create();
this.fontInfo = org.fontInfo;
this.initialTransform = org.initialTransform;
this.currentStroke = org.currentStroke;
this.currentStrokeStyle = org.currentStrokeStyle;
this.currentStrokeWidth = org.currentStrokeWidth;
}
/**
* @return the currently valid state
*/
public Graphics2D getGraph() {
return currentGraphics;
}
/** Frees resources allocated by the current Graphics2D instance. */
public void dispose() {
this.currentGraphics.dispose();
this.currentGraphics = null;
}
/**
* Set the current background color. Check if the background color will
* change and then set the new color.
*
* @param col the new color as a java.awt.Color
* @return true if the background color has changed
*/
public boolean updateColor(Color col) {
if (!ColorUtil.isSameColor(col, getGraph().getColor())) {
getGraph().setColor(col);
return true;
} else {
return false;
}
}
/**
* @return the current java.awt.Color
*/
public java.awt.Color getColor() {
return currentGraphics.getColor();
}
/**
* Set the current font name. Check if the font name will change and then
* set the new name.
*
* @param name the new font name
* @param size the font size
* @return true if the new Font changes the current Font
*/
public boolean updateFont(String name, int size) {
FontMetricsMapper mapper = (FontMetricsMapper) fontInfo.getMetricsFor(name);
boolean updateName = (!mapper.getFontName().equals(
getGraph().getFont().getFontName()));
boolean updateSize = (size != (getGraph().getFont().getSize() * 1000));
if (updateName || updateSize) {
// the font name and/or the font size have changed
java.awt.Font font = mapper.getFont(size);
currentGraphics.setFont(font);
return true;
} else {
return false;
}
}
/** @return the current java.awt.Font */
public java.awt.Font getFont() {
return currentGraphics.getFont();
}
/**
* Sets the current Stroke. The line width should be set with
* updateLineWidth() before calling this method
*
* @param width the line width
* @param style the constant for the style of the line as an int
* @return true if the new Stroke changes the current Stroke
*/
public boolean updateStroke(float width, int style) {
boolean update = false;
// only update if necessary
if ((width != currentStrokeWidth) || (style != currentStrokeStyle)) {
update = true;
switch (style) {
case Constants.EN_DOTTED:
currentStroke = new BasicStroke(width, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_BEVEL, 0f, new float[] {0, 2 * width}, width);
currentGraphics.setStroke(currentStroke);
currentStrokeWidth = width;
currentStrokeStyle = style;
break;
case Constants.EN_DASHED:
currentStroke = new BasicStroke(width, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL, 0f, new float[] {8f, 2f}, 0f);
currentGraphics.setStroke(currentStroke);
currentStrokeWidth = width;
currentStrokeStyle = style;
break;
default: // EN_SOLID:
currentStroke = new BasicStroke(width);
currentGraphics.setStroke(currentStroke);
currentStrokeWidth = width;
currentStrokeStyle = style;
break;
}
}
return update;
}
/** @return the currently active Stroke */
public BasicStroke getStroke() {
return (BasicStroke) currentGraphics.getStroke();
}
/**
* Set the current paint. This checks if the paint will change and then sets
* the current paint.
*
* @param p the new paint
* @return true if the new paint changes the current paint
*/
public boolean updatePaint(Paint p) {
Paint currentPaint = getGraph().getPaint();
if (currentPaint == null) {
if (p != null) {
getGraph().setPaint(p);
return true;
}
} else if (p instanceof Color && currentPaint instanceof Color) {
if (!ColorUtil.isSameColor((Color)p, (Color)currentPaint)) {
getGraph().setPaint(p);
return true;
}
} else if (!p.equals(currentPaint)) {
getGraph().setPaint(p);
return true;
}
return false;
}
/**
* Set the current clip. This either sets a new clip or sets the clip to the
* intersect of the old clip and the new clip.
*
* @param cl the new clip in the current state
* @return true if the clip shape needed to be updated
*/
public boolean updateClip(Shape cl) {
if (getGraph().getClip() != null) {
Area newClip = new Area(getGraph().getClip());
newClip.intersect(new Area(cl));
getGraph().setClip(new GeneralPath(newClip));
} else {
getGraph().setClip(cl);
}
return true; // TODO only update if necessary
}
/**
* Composes an AffineTransform object with the Transform in this Graphics2D
* according to the rule last-specified-first-applied.
* @see java.awt.Graphics2D#transform(AffineTransform)
*
* @param tf the transform to concatenate to the current level transform
*/
public void transform(AffineTransform tf) {
if (!tf.isIdentity()) {
getGraph().transform(tf);
}
}
/**
* 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() {
return getGraph().getTransform();
}
/** {@inheritDoc} */
@Override
public String toString() {
String s = "Java2DGraphicsState " + currentGraphics.toString()
+ ", Stroke (width: " + currentStrokeWidth + " style: "
+ currentStrokeStyle + "), " + getTransform();
return s;
}
}