com.mxgraph.canvas.mxGraphicsCanvas2D Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jgraphx Show documentation
Show all versions of jgraphx Show documentation
JGraphX Swing Component - Java Graph Visualization Library
This is a binary & source redistribution of the original, unmodified JGraphX library originating from:
"https://github.com/jgraph/jgraphx/archive/v3.4.1.3.zip".
The purpose of this redistribution is to make the library available to other Maven projects.
package com.mxgraph.canvas;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.AttributedString;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Stack;
import javax.swing.CellRendererPane;
import javax.swing.JLabel;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxLightweightLabel;
import com.mxgraph.util.mxUtils;
/**
* Used for exporting images. To render to an image from a given XML string,
* graph size and background color, the following code is used:
*
*
* BufferedImage image = mxUtils.createBufferedImage(width, height, background);
* Graphics2D g2 = image.createGraphics();
* mxUtils.setAntiAlias(g2, true, true);
* XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
* reader.setContentHandler(new mxSaxOutputHandler(new mxGraphicsCanvas2D(g2)));
* reader.parse(new InputSource(new StringReader(xml)));
*
*
* Text rendering is available for plain text and HTML markup, the latter with optional
* word wrapping. CSS support is limited to the following:
* http://docs.oracle.com/javase/6/docs/api/index.html?javax/swing/text/html/CSS.html
*/
public class mxGraphicsCanvas2D implements mxICanvas2D
{
/**
* Specifies the image scaling quality. Default is Image.SCALE_SMOOTH.
* See {@link #scaleImage(Image, int, int)}
*/
public static int IMAGE_SCALING = Image.SCALE_SMOOTH;
/**
* Specifies the size of the cache used to store parsed colors
*/
public static int COLOR_CACHE_SIZE = 100;
/**
* Reference to the graphics instance for painting.
*/
protected Graphics2D graphics;
/**
* Specifies if anti aliasing should be disabled for rectangles
* and orthogonal paths. Default is true.
*/
protected boolean autoAntiAlias = true;
/**
* Represents the current state of the canvas.
*/
protected transient CanvasState state = new CanvasState();
/**
* Stack of states for save/restore.
*/
protected transient Stack stack = new Stack();
/**
* Holds the current path.
*/
protected transient GeneralPath currentPath;
/**
* Holds the current state for crisp rendering. This should be true while
* a subsequent stroke operation should be rendering without anti aliasing.
*/
protected transient boolean currentPathIsOrthogonal = true;
/**
* Holds the last point of a moveTo or lineTo operation to determine if the
* current path is orthogonal.
*/
protected transient Point2D lastPoint;
/**
* Holds the current stroke.
*/
protected transient Stroke currentStroke;
/**
* Holds the current font.
*/
protected transient Font currentFont;
/**
* Holds the current value for the shadow color. This is used to hold the
* input value of a shadow operation. The parsing result of this value is
* cached in the global scope as it should be repeating.
*/
protected transient String currentShadowValue;
/**
* Holds the current parsed shadow color. This holds the result of parsing
* the currentShadowValue, which is an expensive operation.
*/
protected transient Color currentShadowColor;
/**
* Optional renderer pane to be used for HTML label rendering.
*/
protected CellRendererPane rendererPane;
/**
* Caches parsed colors.
*/
@SuppressWarnings("serial")
protected transient LinkedHashMap colorCache = new LinkedHashMap() {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > COLOR_CACHE_SIZE;
}
};
/**
* Constructs a new graphics export canvas.
*/
public mxGraphicsCanvas2D(Graphics2D g)
{
setGraphics(g);
state.g = g;
// Initializes the cell renderer pane for drawing HTML markup
try
{
rendererPane = new CellRendererPane();
}
catch (Exception e)
{
// ignore
}
}
/**
* Sets the graphics instance.
*/
public void setGraphics(Graphics2D value)
{
graphics = value;
}
/**
* Returns the graphics instance.
*/
public Graphics2D getGraphics()
{
return graphics;
}
/**
* Returns true if automatic anti aliasing is enabled.
*/
public boolean isAutoAntiAlias()
{
return autoAntiAlias;
}
/**
* Disabled or enabled automatic anti aliasing.
*/
public void setAutoAntiAlias(boolean value)
{
autoAntiAlias = value;
}
/**
* Saves the current canvas state.
*/
public void save()
{
stack.push(state);
state = cloneState(state);
state.g = (Graphics2D) state.g.create();
}
/**
* Restores the last canvas state.
*/
public void restore()
{
state = stack.pop();
// TODO: Check if stroke is part of graphics state
currentStroke = state.g.getStroke();
currentFont = null;
}
/**
* Returns a clone of thec given state.
*/
protected CanvasState cloneState(CanvasState state)
{
try
{
return (CanvasState) state.clone();
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
return null;
}
/**
*
*/
public void scale(double value)
{
// This implementation uses custom scale/translate and built-in rotation
state.scale = state.scale * value;
state.strokeWidth *= value;
}
/**
*
*/
public void translate(double dx, double dy)
{
// This implementation uses custom scale/translate and built-in rotation
state.dx += dx;
state.dy += dy;
}
/**
*
*/
public void rotate(double theta, boolean flipH, boolean flipV, double cx,
double cy)
{
cx *= state.scale;
cy *= state.scale;
// This is a special case where the rotation center is scaled so dx/dy,
// which are also scaled, must be applied after scaling the center.
cx += state.dx;
cy += state.dy;
// This implementation uses custom scale/translate and built-in rotation
// Rotation state is part of the AffineTransform in state.transform
if (flipH ^ flipV)
{
double tx = (flipH) ? cx : 0;
int sx = (flipH) ? -1 : 1;
double ty = (flipV) ? cy : 0;
int sy = (flipV) ? -1 : 1;
state.g.translate(tx, ty);
state.g.scale(sx, sy);
state.g.translate(-tx, -ty);
}
state.g.rotate(Math.toRadians(theta), cx, cy);
}
/**
*
*/
public void setStrokeWidth(double value)
{
// Lazy and cached instantiation strategy for all stroke properties
if (value * state.scale != state.strokeWidth)
{
state.strokeWidth = value * state.scale;
// Invalidates cached stroke
currentStroke = null;
}
}
/**
* Caches color conversion as it is expensive.
*/
public void setStrokeColor(String value)
{
// Lazy and cached instantiation strategy for all stroke properties
if (!state.strokeColorValue.equals(value))
{
state.strokeColorValue = value;
state.strokeColor = null;
}
}
/**
*
*/
public void setDashed(boolean value)
{
// Lazy and cached instantiation strategy for all stroke properties
if (value != state.dashed)
{
state.dashed = value;
// Invalidates cached stroke
currentStroke = null;
}
}
/**
*
*/
public void setDashPattern(String value)
{
if (value != null && !state.dashPattern.equals(value) && value.length() > 0)
{
String[] tokens = value.split(" ");
float[] dashpattern = new float[tokens.length];
for (int i = 0; i < tokens.length; i++)
{
dashpattern[i] = (float) (Float.parseFloat(tokens[i]));
}
state.dashPattern = dashpattern;
currentStroke = null;
}
}
/**
*
*/
public void setLineCap(String value)
{
if (!state.lineCap.equals(value))
{
state.lineCap = value;
currentStroke = null;
}
}
/**
*
*/
public void setLineJoin(String value)
{
if (!state.lineJoin.equals(value))
{
state.lineJoin = value;
currentStroke = null;
}
}
/**
*
*/
public void setMiterLimit(double value)
{
if (value != state.miterLimit)
{
state.miterLimit = value;
currentStroke = null;
}
}
/**
*
*/
public void setFontSize(double value)
{
if (value != state.fontSize)
{
state.fontSize = value * state.scale;
currentFont = null;
}
}
/**
*
*/
public void setFontColor(String value)
{
if (!state.fontColorValue.equals(value))
{
state.fontColorValue = value;
state.fontColor = null;
}
}
/**
*
*/
public void setFontFamily(String value)
{
if (!state.fontFamily.equals(value))
{
state.fontFamily = value;
currentFont = null;
}
}
/**
*
*/
public void setFontStyle(int value)
{
if (value != state.fontStyle)
{
state.fontStyle = value;
currentFont = null;
}
}
/**
*
*/
public void setAlpha(double value)
{
if (state.alpha != value)
{
state.g.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, (float) (value)));
state.alpha = value;
}
}
/**
*
*/
public void setFillColor(String value)
{
if (!state.fillColorValue.equals(value))
{
state.fillColorValue = value;
state.fillColor = null;
// Setting fill color resets paint color
state.paint = null;
}
}
/**
*
*/
public void setGradient(String color1, String color2, double x, double y,
double w, double h, String direction)
{
// LATER: Add lazy instantiation and check if paint already created
float x1 = (float) (state.dx + x * state.scale);
float y1 = (float) (state.dy + y * state.scale);
float x2 = (float) x1;
float y2 = (float) y1;
h *= state.scale;
w *= state.scale;
if (direction == null || direction.length() == 0
|| direction.equals(mxConstants.DIRECTION_SOUTH))
{
y2 = (float) (y1 + h);
}
else if (direction.equals(mxConstants.DIRECTION_EAST))
{
x2 = (float) (x1 + w);
}
else if (direction.equals(mxConstants.DIRECTION_NORTH))
{
y1 = (float) (y1 + h);
}
else if (direction.equals(mxConstants.DIRECTION_WEST))
{
x1 = (float) (x1 + w);
}
state.paint = new GradientPaint(x1, y1, parseColor(color1), x2, y2,
parseColor(color2), true);
}
/**
* Helper method that uses {@link mxUtils#parseColor(String)}.
*/
protected Color parseColor(String hex)
{
Color result = colorCache.get(hex);
if (result == null)
{
result = mxUtils.parseColor(hex);
colorCache.put(hex, result);
}
return result;
}
/**
*
*/
public void setGlassGradient(double x, double y, double w, double h)
{
double size = 0.4;
x = state.dx + x * state.scale;
y = state.dy + y * state.scale;
h *= state.scale;
w *= state.scale;
state.paint = new GradientPaint((float) x, (float) y, new Color(1, 1,
1, 0.9f), (float) (x), (float) (y + h * size), new Color(1, 1,
1, 0.3f));
}
/**
*
*/
public void rect(double x, double y, double w, double h)
{
currentPath = new GeneralPath();
currentPath.append(new Rectangle2D.Double(state.dx + x * state.scale,
state.dy + y * state.scale, w * state.scale, h * state.scale),
false);
}
/**
* Implements a rounded rectangle using a path.
*/
public void roundrect(double x, double y, double w, double h, double dx,
double dy)
{
begin();
moveTo(x + dx, y);
lineTo(x + w - dx, y);
quadTo(x + w, y, x + w, y + dy);
lineTo(x + w, y + h - dy);
quadTo(x + w, y + h, x + w - dx, y + h);
lineTo(x + dx, y + h);
quadTo(x, y + h, x, y + h - dy);
lineTo(x, y + dy);
quadTo(x, y, x + dx, y);
}
/**
*
*/
public void ellipse(double x, double y, double w, double h)
{
currentPath = new GeneralPath();
currentPath.append(new Ellipse2D.Double(state.dx + x * state.scale,
state.dy + y * state.scale, w * state.scale, h * state.scale),
false);
currentPathIsOrthogonal = false;
}
/**
*
*/
public void image(double x, double y, double w, double h, String src,
boolean aspect, boolean flipH, boolean flipV)
{
if (src != null && w > 0 && h > 0)
{
Image img = loadImage(src);
if (img != null)
{
Rectangle bounds = getImageBounds(img, x, y, w, h, aspect);
img = scaleImage(img, bounds.width, bounds.height);
if (img != null)
{
drawImage(
createImageGraphics(bounds.x, bounds.y,
bounds.width, bounds.height, flipH, flipV),
img, bounds.x, bounds.y);
}
}
}
}
/**
*
*/
protected void drawImage(Graphics2D graphics, Image image, int x, int y)
{
graphics.drawImage(image, x, y, null);
}
/**
* Hook for image caching.
*/
protected Image loadImage(String src)
{
return mxUtils.loadImage(src);
}
/**
*
*/
protected final Rectangle getImageBounds(Image img, double x, double y,
double w, double h, boolean aspect)
{
x = state.dx + x * state.scale;
y = state.dy + y * state.scale;
w *= state.scale;
h *= state.scale;
if (aspect)
{
Dimension size = getImageSize(img);
double s = Math.min(w / size.width, h / size.height);
int sw = (int) Math.round(size.width * s);
int sh = (int) Math.round(size.height * s);
x += (w - sw) / 2;
y += (h - sh) / 2;
w = sw;
h = sh;
}
else
{
w = Math.round(w);
h = Math.round(h);
}
return new Rectangle((int) x, (int) y, (int) w, (int) h);
}
/**
* Returns the size for the given image.
*/
protected Dimension getImageSize(Image image)
{
return new Dimension(image.getWidth(null), image.getHeight(null));
}
/**
* Uses {@link #IMAGE_SCALING} to scale the given image.
*/
protected Image scaleImage(Image img, int w, int h)
{
Dimension size = getImageSize(img);
if (w == size.width && h == size.height)
{
return img;
}
else
{
return img.getScaledInstance(w, h, IMAGE_SCALING);
}
}
/**
* Creates a graphic instance for rendering an image.
*/
protected final Graphics2D createImageGraphics(double x, double y,
double w, double h, boolean flipH, boolean flipV)
{
Graphics2D g2 = state.g;
if (flipH || flipV)
{
g2 = (Graphics2D) g2.create();
int sx = 1;
int sy = 1;
int dx = 0;
int dy = 0;
if (flipH)
{
sx = -1;
dx = (int) (-w - 2 * x);
}
if (flipV)
{
sy = -1;
dy = (int) (-h - 2 * y);
}
g2.scale(sx, sy);
g2.translate(dx, dy);
}
return g2;
}
/**
* Creates a HTML document around the given markup.
*/
protected String createHtmlDocument(String text, String align,
String valign, int w, int h)
{
StringBuffer css = new StringBuffer();
css.append("font-family:" + state.fontFamily + ";");
css.append("font-size:" + Math.floor(state.fontSize / state.scale) + " pt;");
css.append("color:" + state.fontColorValue + ";");
if ((state.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
{
css.append("font-weight:bold;");
}
if ((state.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
{
css.append("font-style:italic;");
}
if ((state.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
{
css.append("text-decoration:underline;");
}
if (align != null)
{
if (align.equals(mxConstants.ALIGN_CENTER))
{
css.append("text-align:center;");
}
else if (align.equals(mxConstants.ALIGN_RIGHT))
{
css.append("text-align:right;");
}
}
return "" + text + "";
}
/**
* Hook to return the renderer for HTML formatted text. This implementation returns
* the shared instance of mxLighweightLabel.
*/
protected JLabel getTextRenderer()
{
return mxLightweightLabel.getSharedInstance();
}
/**
* Draws the given text.
*/
public void text(double x, double y, double w, double h, String str,
String align, String valign, boolean vertical, boolean wrap,
String format)
{
if (!state.fontColorValue.equals(mxConstants.NONE))
{
if (format != null && format.equals("html"))
{
x += state.dx / state.scale;
y += state.dy / state.scale;
JLabel textRenderer = getTextRenderer();
if (textRenderer != null && rendererPane != null)
{
// Use native scaling for HTML
AffineTransform previous = state.g.getTransform();
state.g.scale(state.scale, state.scale);
// Renders the scaled text with a correction factor of
// PX_PER_PIXEL for px in HTML vs pixels in the bitmap
str = createHtmlDocument(str, align, valign,
(int) Math.round(w * mxConstants.PX_PER_PIXEL),
(int) Math.round(h * mxConstants.PX_PER_PIXEL));
textRenderer.setText(str);
rendererPane.paintComponent(state.g, textRenderer,
rendererPane, (int) Math.round(x),
(int) Math.round(y), (int) Math.round(w),
(int) Math.round(h), true);
state.g.setTransform(previous);
}
}
else
{
x = state.dx + x * state.scale;
y = state.dy + y * state.scale;
w *= state.scale;
h *= state.scale;
// Font-metrics needed below this line
Graphics2D g2 = createTextGraphics(x, y, w, h, vertical);
FontMetrics fm = g2.getFontMetrics();
String[] lines = str.split("\n");
y = getVerticalTextPosition(x, y, w, h, align, valign,
vertical, fm, lines);
x = getHorizontalTextPosition(x, y, w, h, align, valign,
vertical, fm, lines);
for (int i = 0; i < lines.length; i++)
{
double dx = 0;
if (align != null)
{
if (align.equals(mxConstants.ALIGN_CENTER))
{
int sw = fm.stringWidth(lines[i]);
dx = (w - sw) / 2;
}
else if (align.equals(mxConstants.ALIGN_RIGHT))
{
int sw = fm.stringWidth(lines[i]);
dx = w - sw;
}
}
// Adds support for underlined text via attributed character iterator
if ((state.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
{
AttributedString as = new AttributedString(lines[i]);
as.addAttribute(TextAttribute.FONT, g2.getFont());
as.addAttribute(TextAttribute.UNDERLINE,
TextAttribute.UNDERLINE_ON);
g2.drawString(as.getIterator(),
(int) Math.round(x + dx), (int) Math.round(y));
}
else
{
g2.drawString(lines[i], (int) Math.round(x + dx),
(int) Math.round(y));
}
y += fm.getHeight() + mxConstants.LINESPACING;
}
}
}
}
/**
* Returns a new graphics instance with the correct color and font for
* text rendering.
*/
protected final Graphics2D createTextGraphics(double x, double y, double w,
double h, boolean vertical)
{
Graphics2D g2 = state.g;
updateFont();
if (vertical)
{
g2 = (Graphics2D) state.g.create();
g2.rotate(-Math.PI / 2, x + w / 2, y + h / 2);
}
if (state.fontColor == null)
{
state.fontColor = parseColor(state.fontColorValue);
}
g2.setColor(state.fontColor);
return g2;
}
/**
*
*/
protected double getVerticalTextPosition(double x, double y, double w,
double h, String align, String valign, boolean vertical,
FontMetrics fm, String[] lines)
{
double lineHeight = fm.getHeight() + mxConstants.LINESPACING;
double textHeight = lines.length * lineHeight;
double dy = h - textHeight;
// Top is default
if (valign == null || valign.equals(mxConstants.ALIGN_TOP))
{
y = Math.max(y - 2 * state.scale, y + dy / 2);
}
else if (valign.equals(mxConstants.ALIGN_MIDDLE))
{
y = y + dy / 2;
}
else if (valign.equals(mxConstants.ALIGN_BOTTOM))
{
y = Math.min(y, y + dy);
}
return y + fm.getHeight() * 0.75;
}
/**
* This implementation returns x.
*/
protected double getHorizontalTextPosition(double x, double y, double w,
double h, String align, String valign, boolean vertical,
FontMetrics fm, String[] lines)
{
if (align == null || align.equals(mxConstants.ALIGN_LEFT))
{
x += 2 * state.scale;
}
return x;
}
/**
*
*/
public void begin()
{
currentPath = new GeneralPath();
currentPathIsOrthogonal = true;
lastPoint = null;
}
/**
*
*/
public void moveTo(double x, double y)
{
if (currentPath != null)
{
currentPath.moveTo((float) (state.dx + x * state.scale),
(float) (state.dy + y * state.scale));
if (isAutoAntiAlias())
{
lastPoint = new Point2D.Double(x, y);
}
}
}
/**
*
*/
public void lineTo(double x, double y)
{
if (currentPath != null)
{
currentPath.lineTo((float) (state.dx + x * state.scale),
(float) (state.dy + y * state.scale));
if (isAutoAntiAlias())
{
if (lastPoint != null && currentPathIsOrthogonal
&& x != lastPoint.getX() && y != lastPoint.getY())
{
currentPathIsOrthogonal = false;
}
lastPoint = new Point2D.Double(x, y);
}
}
}
/**
*
*/
public void quadTo(double x1, double y1, double x2, double y2)
{
if (currentPath != null)
{
currentPath.quadTo((float) (state.dx + x1 * state.scale),
(float) (state.dy + y1 * state.scale),
(float) (state.dx + x2 * state.scale),
(float) (state.dy + y2 * state.scale));
currentPathIsOrthogonal = false;
}
}
/**
*
*/
public void curveTo(double x1, double y1, double x2, double y2, double x3,
double y3)
{
if (currentPath != null)
{
currentPath.curveTo((float) (state.dx + x1 * state.scale),
(float) (state.dy + y1 * state.scale),
(float) (state.dx + x2 * state.scale),
(float) (state.dy + y2 * state.scale),
(float) (state.dx + x3 * state.scale),
(float) (state.dy + y3 * state.scale));
currentPathIsOrthogonal = false;
}
}
/**
* Closes the current path.
*/
public void close()
{
if (currentPath != null)
{
currentPath.closePath();
}
}
/**
*
*/
public void stroke()
{
if (currentPath != null
&& !state.strokeColorValue.equals(mxConstants.NONE))
{
if (state.strokeColor == null)
{
state.strokeColor = parseColor(state.strokeColorValue);
}
updateStroke();
state.g.setColor(state.strokeColor);
Object previousHint = null;
if (isAutoAntiAlias() && currentPathIsOrthogonal)
{
previousHint = state.g
.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
state.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
}
state.g.draw(currentPath);
if (previousHint != null)
{
state.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
previousHint);
}
}
}
/**
*
*/
public void fill()
{
if (currentPath != null
&& (!state.fillColorValue.equals(mxConstants.NONE) || state.paint != null))
{
if (state.paint != null)
{
state.g.setPaint(state.paint);
}
else
{
if (state.fillColor == null)
{
state.fillColor = parseColor(state.fillColorValue);
}
state.g.setColor(state.fillColor);
state.g.setPaint(null);
}
state.g.fill(currentPath);
}
}
/**
*
*/
public void fillAndStroke()
{
fill();
stroke();
}
/**
*
*/
public void shadow(String value, boolean filled)
{
if (value != null && currentPath != null)
{
if (currentShadowColor == null || currentShadowValue == null
|| !currentShadowValue.equals(value))
{
currentShadowColor = parseColor(value);
currentShadowValue = value;
}
updateStroke();
state.g.setColor(currentShadowColor);
if (filled)
{
state.g.fill(currentPath);
}
state.g.draw(currentPath);
}
}
/**
*
*/
public void clip()
{
if (currentPath != null)
{
state.g.clip(currentPath);
}
}
/**
*
*/
protected void updateFont()
{
// LATER: Make currentFont part of state
if (currentFont == null)
{
int size = (int) Math.floor(state.fontSize);
int style = ((state.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) ? Font.BOLD
: Font.PLAIN;
style += ((state.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) ? Font.ITALIC
: Font.PLAIN;
currentFont = createFont(state.fontFamily, style, size);
state.g.setFont(currentFont);
}
}
/**
* Hook for subclassers to implement font caching.
*/
protected Font createFont(String family, int style, int size)
{
return new Font(getFontName(family), style, size);
}
/**
* Returns a font name for the given font family.
*/
protected String getFontName(String family)
{
if (family != null)
{
int comma = family.indexOf(',');
if (comma >= 0)
{
family = family.substring(0, comma);
}
}
return family;
}
/**
*
*/
protected void updateStroke()
{
if (currentStroke == null)
{
int cap = BasicStroke.CAP_BUTT;
if (state.lineCap.equals("round"))
{
cap = BasicStroke.CAP_ROUND;
}
else if (state.lineCap.equals("square"))
{
cap = BasicStroke.CAP_SQUARE;
}
int join = BasicStroke.JOIN_MITER;
if (state.lineJoin.equals("round"))
{
join = BasicStroke.JOIN_ROUND;
}
else if (state.lineJoin.equals("bevel"))
{
join = BasicStroke.JOIN_BEVEL;
}
float miterlimit = (float) state.miterLimit;
float[] dash = null;
if (state.dashed)
{
dash = new float[state.dashPattern.length];
for (int i = 0; i < dash.length; i++)
{
dash[i] = (float) (state.dashPattern[i] * state.strokeWidth);
}
}
currentStroke = new BasicStroke((float) state.strokeWidth, cap,
join, miterlimit, dash, 0);
state.g.setStroke(currentStroke);
}
}
/**
*
*/
protected class CanvasState implements Cloneable
{
/**
*
*/
protected double alpha = 1;
/**
*
*/
protected double scale = 1;
/**
*
*/
protected double dx = 0;
/**
*
*/
protected double dy = 0;
/**
*
*/
protected double miterLimit = 10;
/**
*
*/
protected int fontStyle = 0;
/**
*
*/
protected double fontSize = mxConstants.DEFAULT_FONTSIZE;
/**
*
*/
protected String fontFamily = mxConstants.DEFAULT_FONTFAMILIES;
/**
*
*/
protected String fontColorValue = "#000000";
/**
*
*/
protected Color fontColor;
/**
*
*/
protected String lineCap = "flat";
/**
*
*/
protected String lineJoin = "miter";
/**
*
*/
protected double strokeWidth = 1;
/**
*
*/
protected String strokeColorValue = mxConstants.NONE;
/**
*
*/
protected Color strokeColor;
/**
*
*/
protected String fillColorValue = mxConstants.NONE;
/**
*
*/
protected Color fillColor;
/**
*
*/
protected Paint paint;
/**
*
*/
protected boolean dashed = false;
/**
*
*/
protected float[] dashPattern = { 3, 3 };
/**
* Stores the actual state.
*/
protected transient Graphics2D g;
/**
*
*/
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
}