jcckit.renderer.Graphics2DRenderer Maven / Gradle / Ivy
Show all versions of plantuml-gplv2 Show documentation
// THIS FILE HAS BEEN GENERATED BY A PREPROCESSOR.
/* +=======================================================================
* |
* | PlantUML : a free UML diagram generator
* |
* +=======================================================================
*
* (C) Copyright 2009-2024, Arnaud Roques
*
* Project Info: https://plantuml.com
*
* If you like this project or if you find it useful, you can support us at:
*
* https://plantuml.com/patreon (only 1$ per month!)
* https://plantuml.com/liberapay (only 1€ per month!)
* https://plantuml.com/paypal
*
*
* PlantUML is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License V2.
*
* THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
* LICENSE ("AGREEMENT"). [GNU General Public License V2]
*
* ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES
* RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
*
* You may obtain a copy of the License at
*
* https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* 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.
*
* PlantUML can occasionally display sponsored or advertising messages. Those
* messages are usually generated on welcome or error images and never on
* functional diagrams.
* See https://plantuml.com/professional if you want to remove them
*
* Images (whatever their format : PNG, SVG, EPS...) generated by running PlantUML
* are owned by the author of their corresponding sources code (that is, their
* textual description in PlantUML language). Those images are not covered by
* this GPL v2 license.
*
* The generated images can then be used without any reference to the GPL v2 license.
* It is not even necessary to stipulate that they have been generated with PlantUML,
* although this will be appreciated by the PlantUML team.
*
* There is an exception : if the textual description in PlantUML language is also covered
* by any license, then the generated images are logically covered
* by the very same license.
*
* This is the IGY distribution (Install GraphViz by Yourself).
* You have to install GraphViz and to setup the GRAPHVIZ_DOT environment variable
* (see https://plantuml.com/graphviz-dot )
*
* Icons provided by OpenIconic : https://useiconic.com/open
* Archimate sprites provided by Archi : http://www.archimatetool.com
* Stdlib AWS provided by https://github.com/milo-minderbinder/AWS-PlantUML
* Stdlib Icons provided https://github.com/tupadr3/plantuml-icon-font-sprites
* ASCIIMathML (c) Peter Jipsen http://www.chapman.edu/~jipsen
* ASCIIMathML (c) David Lippman http://www.pierce.ctc.edu/dlippman
* CafeUndZopfli ported by Eugene Klyuchnikov https://github.com/eustas/CafeUndZopfli
* Brotli (c) by the Brotli Authors https://github.com/google/brotli
* Themes (c) by Brett Schwarz https://github.com/bschwarz/puml-themes
* Twemoji (c) by Twitter at https://twemoji.twitter.com/
*
*/
package jcckit.renderer;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Shape;
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 jcckit.graphic.BasicGraphicalElement;
import jcckit.graphic.ClippingRectangle;
import jcckit.graphic.ClippingShape;
import jcckit.graphic.FillAttributes;
import jcckit.graphic.FontStyle;
import jcckit.graphic.GraphPoint;
import jcckit.graphic.GraphicAttributes;
import jcckit.graphic.GraphicalComposite;
import jcckit.graphic.GraphicalCompositeRenderer;
import jcckit.graphic.LineAttributes;
import jcckit.graphic.Oval;
import jcckit.graphic.OvalRenderer;
import jcckit.graphic.Polygon;
import jcckit.graphic.PolygonRenderer;
import jcckit.graphic.Rectangle;
import jcckit.graphic.RectangleRenderer;
import jcckit.graphic.Text;
import jcckit.graphic.TextAttributes;
import jcckit.graphic.TextRenderer;
/**
* Renderer who draws the {@link jcckit.graphic.GraphicalElement
* GraphicalElements} into a java.awt.Graphics2D context.
*
* The default color for lines and texts is determined by the current color of
* the Graphics2D context when a new instance of
* Graphics2DRenderer is created.
*
* The default font is SansSerif-12.
*
* @author Franz-Josef Elmer
*/
public class Graphics2DRenderer implements GraphicalCompositeRenderer, PolygonRenderer, OvalRenderer, TextRenderer,
RectangleRenderer {
private static final int FS = 1;
private static final String DEFAULT_FONT_NAME = "SansSerif";
private static final FontStyle DEFAULT_FONT_STYLE = FontStyle.NORMAL;
private static final int DEFAULT_FONT_SIZE = 12;
private Color _defaultColor;
private Graphics2D _graphics;
/**
* Initializes this instance. During renderering the current transformation
* will be leaved unchanged. But the current Clip may be cleared.
*
* @param graphics
* Graphics2D context into which the
* {@link BasicGraphicalElement BaiscGraphicalElements} are
* painted.
* @return this instance.
*/
public Graphics2DRenderer init(Graphics2D graphics) {
_graphics = graphics;
_defaultColor = graphics.getColor(); // the foreground color
return this;
}
/**
* Starts rendering of the specified composite. Does nothing except if
* composite has a {@link ClippingShape}. In this case the Clip
* of the Graphics2D context becomes the clipping rectangle
* determined by the bounding box of the ClippingShape.
*/
public void startRendering(GraphicalComposite composite) {
ClippingShape shape = composite.getClippingShape();
if (shape != null) {
ClippingRectangle rect = shape.getBoundingBox();
_graphics.clip(new Rectangle2D.Double(rect.getMinX(), rect.getMinY(), rect.getMaxX() - rect.getMinX(), rect
.getMaxY()
- rect.getMinY()));
}
}
/**
* Finishes rendering of the specified composite. Does nothing except if
* composite has a {@link ClippingShape}. In this case the Clip
* of the Graphics2D context will be cleared.
*/
public void finishRendering(GraphicalComposite composite) {
_graphics.setClip(null);
}
/** Paints the specified polygon into the Graphics2D context. */
public void render(Polygon polygon) {
int numberOfPoints = polygon.getNumberOfPoints();
if (numberOfPoints > 0) {
Color currentColor = _graphics.getColor();
GeneralPath p = new GeneralPath(GeneralPath.WIND_EVEN_ODD, numberOfPoints);
p.moveTo((float) polygon.getPoint(0).getX(), (float) polygon.getPoint(0).getY());
for (int i = 1; i < numberOfPoints; i++) {
p.lineTo((float) polygon.getPoint(i).getX(), (float) polygon.getPoint(i).getY());
}
if (polygon.isClosed()) {
p.closePath();
}
drawShape(p, polygon, currentColor);
}
}
/**
* Paints the specified rectangle into the current Graphics
* context.
*/
public void render(Rectangle rectangle) {
Color currentColor = _graphics.getColor();
GraphPoint center = rectangle.getCenter();
double width = rectangle.getWidth();
double height = rectangle.getHeight();
Rectangle2D rect = new Rectangle2D.Double(center.getX() - 0.5 * width, center.getY() - 0.5 * height, width,
height);
drawShape(rect, rectangle, currentColor);
}
/**
* Paints the specified oval into the current Graphics context.
*/
public void render(Oval oval) {
Color currentColor = _graphics.getColor();
GraphPoint center = oval.getCenter();
double width = oval.getWidth();
double height = oval.getHeight();
Ellipse2D ellipse = new Ellipse2D.Double(center.getX() - 0.5 * width, center.getY() - 0.5 * height, width,
height);
drawShape(ellipse, oval, currentColor);
}
private void drawShape(Shape shape, BasicGraphicalElement element, Color backupColor) {
GraphicAttributes attributes = element.getGraphicAttributes();
Color fillColor = null;
if (element.isClosed() && attributes instanceof FillAttributes) {
fillColor = ((FillAttributes) attributes).getFillColor();
}
if (fillColor != null) {
_graphics.setColor(fillColor);
_graphics.fill(shape);
}
Color lineColor = _defaultColor;
if (attributes instanceof LineAttributes) {
LineAttributes la = (LineAttributes) attributes;
BasicStroke stroke = new BasicStroke((float) la.getLineThickness());
double[] linePattern = la.getLinePattern();
if (linePattern != null) {
float[] dash = new float[linePattern.length];
for (int i = 0; i < dash.length; i++) {
dash[i] = (float) la.getLinePattern()[i];
}
stroke = new BasicStroke(stroke.getLineWidth(), BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10f,
dash, 0f);
}
_graphics.setStroke(stroke);
if (la.getLineColor() != null || fillColor != null) {
lineColor = la.getLineColor();
}
}
if (lineColor != null) {
_graphics.setColor(lineColor);
_graphics.draw(shape);
}
_graphics.setColor(backupColor);
}
/**
* Paints the specified text into the current Graphics context.
*
* If the font size is zero the default font size will be used.
*
* If the orientation angle is unequal zero the text will first be painted
* into an off-screen image and rotated. Finally, it will be drawn into the
* current Graphics context. Note, that only integer multiples of
* 90 degree rotation are performed. Other orientation angles will be
* adjusted to the nearest integer multiple of 90 degree.
*/
public void render(Text text) {
final GraphicAttributes ga = text.getGraphicAttributes();
if (ga instanceof TextAttributes) {
final TextAttributes ta = (TextAttributes) ga;
final Color currentColor = _graphics.getColor();
Color fontColor = ta.getTextColor();
if (fontColor == null) {
fontColor = _defaultColor;
}
_graphics.setColor(fontColor);
final double scale = _graphics.getTransform().getScaleX();
final String str = text.getText();
AffineTransform before = _graphics.getTransform();
_graphics.setTransform(new AffineTransform());
double fs = ta.getFontSize();
fs = fs == 0 ? 1 : fs * scale / DEFAULT_FONT_SIZE;
Font font = createFont(ta, 0);
AffineTransform fontTransform = new AffineTransform();
fontTransform.scale(fs, fs);
fontTransform.rotate(-ta.getOrientationAngle() * Math.PI / 180);
font = font.deriveFont(fontTransform);
_graphics.setFont(font);
Rectangle2D bounds = _graphics.getFontMetrics().getStringBounds(str, _graphics);
fontTransform.rotate(-ta.getOrientationAngle() * Math.PI / 180);
final double yy = bounds.getHeight() + bounds.getY();
Point2D.Double pos = new Point2D.Double(text.getPosition().getX(), text.getPosition().getY());
before.transform(pos, pos);
double x = 0;
double y = 0;
if (ta.getOrientationAngle() == 0) {
x = -0.5 * ta.getHorizontalAnchor().getFactor() * bounds.getWidth();
y = 0.5 * ta.getVerticalAnchor().getFactor() * bounds.getHeight() - yy;
x = pos.x + x;
y = pos.y + y;
} else {
x = 0.5 * ta.getVerticalAnchor().getFactor() * bounds.getHeight();
y = 0.5 * ta.getHorizontalAnchor().getFactor() * bounds.getWidth();
// System.err.println("yy="+y+" dx="+x+" dy="+y);
// x = 0;
// y = 0;
x = pos.x + x;
y = pos.y + y;
}
// if (ta.getOrientationAngle() == 0) {
//// System.err.println("x0=" + x);
//// System.err.println("y0=" + y);
// } else {
// System.err.println("bounds=" + bounds + " y=" + bounds.getY() + " h=" + bounds.getHeight() + " vert="
// + ta.getVerticalAnchor().getFactor()+" horz="+ta.getHorizontalAnchor().getFactor());
// System.err.println("x1=" + x);
// System.err.println("y1=" + y);
// }
_graphics.drawString(str, (float) x, (float) y);
// _graphics.fillRect((int)x, (int)y, 5, 5);
_graphics.setTransform(before);
_graphics.setColor(currentColor);
}
}
/**
* Creates a font instance based on the specified text attributes and font
* size.
*
* @param attributes
* Text attributes (font name and style).
* @param size
* Font size in pixel. If 0 {@link #DEFAULT_FONT_SIZE} will be
* used.
* @return new font instance.
*/
static Font createFont(TextAttributes attributes, int size) {
String fontName = attributes.getFontName();
if (fontName == null) {
fontName = DEFAULT_FONT_NAME;
}
FontStyle fontStyle = attributes.getFontStyle();
if (fontStyle == null) {
fontStyle = DEFAULT_FONT_STYLE;
}
int style = Font.PLAIN;
if (fontStyle == FontStyle.BOLD) {
style = Font.BOLD;
} else if (fontStyle == FontStyle.ITALIC) {
style = Font.ITALIC;
} else if (fontStyle == FontStyle.BOLD_ITALIC) {
style = Font.BOLD + Font.ITALIC;
}
if (size == 0) {
size = DEFAULT_FONT_SIZE;
}
return new Font(fontName, style, size);
}
}