-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.scenegraph.g2d.nodes;\r
-\r
-import java.awt.BasicStroke;\r
-import java.awt.Color;\r
-import java.awt.Font;\r
-import java.awt.FontMetrics;\r
-import java.awt.Graphics2D;\r
-import java.awt.RenderingHints;\r
-import java.awt.Shape;\r
-import java.awt.Stroke;\r
-import java.awt.font.FontRenderContext;\r
-import java.awt.font.TextLayout;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.util.Arrays;\r
-\r
-import org.simantics.scenegraph.g2d.G2DNode;\r
-import org.simantics.scenegraph.g2d.G2DPDFRenderingHints;\r
-import org.simantics.scenegraph.utils.GeometryUtils;\r
-\r
-public class FlagNode extends G2DNode {\r
-\r
- private static final long serialVersionUID = -1716729504104107151L;\r
-\r
- private static final AffineTransform IDENTITY = new AffineTransform();\r
-\r
- private static final byte LEADING = 0;\r
- private static final byte TRAILING = 1;\r
- private static final byte CENTER = 2;\r
-\r
- private static final boolean DEBUG = false;\r
-\r
- private static final double GLOBAL_SCALE = 0.1;\r
-\r
- private static final double TEXT_MARGIN = 5;\r
-\r
- static transient final BasicStroke STROKE = new BasicStroke(0.25f, BasicStroke.CAP_BUTT,\r
- BasicStroke.JOIN_MITER);\r
-\r
- final transient Font FONT = Font.decode("Arial 12");\r
-\r
- protected boolean visible;\r
-\r
- protected Shape flagShape;\r
- protected String[] flagText;\r
- protected Stroke stroke;\r
- protected Color border;\r
- protected Color fill;\r
- protected Color textColor;\r
- protected float width;\r
- protected float height;\r
- protected double direction; // in radians\r
- protected float beakAngle;\r
- protected Rectangle2D textArea;\r
- protected byte hAlign;\r
- protected byte vAlign;\r
-\r
- private transient final Point2D origin = new Point2D.Double();\r
- private transient final Point2D xa = new Point2D.Double();\r
- private transient final Point2D ya = new Point2D.Double();\r
-\r
- protected transient TextLayout[] textLayout = null;\r
- protected transient Rectangle2D[] rects = null;\r
- protected transient float textHeight = 0;\r
- protected transient float lastViewScale = 0;\r
-\r
- @SyncField("visible")\r
- public void setVisible(boolean visible) {\r
- this.visible = visible;\r
- }\r
-\r
- public boolean isVisible() {\r
- return visible;\r
- }\r
-\r
- @SyncField({"visible", "flagShape", "flagText", "stroke", "border", "fill", "textColor", "width", "height", "direction", "beakAngle", "textSize", "hAlign", "vAlign"})\r
- public void init(Shape flagShape, String[] flagText, Stroke stroke, Color border, Color fill, Color textColor, float width, float height, double direction, float beakAngle, Rectangle2D textArea, int hAlign, int vAlign) {\r
- this.visible = true;\r
- this.flagShape = flagShape;\r
- this.flagText = flagText;\r
- this.stroke = stroke;\r
- this.border = border;\r
- this.fill = fill;\r
- this.textColor = textColor;\r
- this.width = width;\r
- this.height = height;\r
- this.direction = direction;\r
- this.beakAngle = beakAngle;\r
- this.textArea = textArea;\r
- this.hAlign = (byte) hAlign;\r
- this.vAlign = (byte) vAlign;\r
-\r
- resetCaches();\r
- }\r
-\r
- private void resetCaches() {\r
- textLayout = null;\r
- rects = null;\r
- }\r
-\r
- @Override\r
- public void render(Graphics2D g) {\r
- if (!visible)\r
- return;\r
-\r
- if (DEBUG) {\r
- System.out.println("FlagNode.render:");\r
- System.out.println("\tflagShape: " + flagShape);\r
- System.out.println("\tflagText: " + Arrays.toString(flagText));\r
- System.out.println("\tstroke: " + stroke);\r
- System.out.println("\tborder: " + border);\r
- System.out.println("\tfill: " + fill);\r
- System.out.println("\ttextColor: " + textColor);\r
- System.out.println("\twidth: " + width);\r
- System.out.println("\theight: " + height);\r
- System.out.println("\tdirection: " + direction);\r
- System.out.println("\tbeakAngle: " + beakAngle);\r
- System.out.println("\ttextArea: " + textArea);\r
- System.out.println("\thAlign: " + hAlign);\r
- System.out.println("\tvAlign: " + vAlign);\r
- System.out.println("\tdraw: " + visible);\r
- }\r
-\r
- AffineTransform ot = g.getTransform();\r
- g.transform(transform);\r
-\r
- try {\r
- Object renderingHint = g.getRenderingHint(RenderingHints.KEY_RENDERING);\r
-\r
- //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);\r
-\r
- // Paint flag shape\r
- g.setColor(fill);\r
- g.fill(flagShape);\r
- g.setStroke(stroke);\r
- g.setColor(border);\r
- g.draw(flagShape);\r
-\r
- // Speed rendering optimization: don't draw text that is too small to read\r
- if (renderingHint != RenderingHints.VALUE_RENDER_QUALITY) {\r
- double viewScale = GeometryUtils.getScale(ot);\r
- viewScale *= GeometryUtils.getScale(transform);\r
- if (viewScale < 4.0)\r
- return;\r
- }\r
-\r
- if (flagText == null || flagText.length == 0)\r
- return;\r
-\r
- if (DEBUG) {\r
- g.setColor(Color.RED);\r
- g.draw(textArea);\r
- }\r
-\r
- // Paint flag text\r
- Font f = FONT;\r
- g.setFont(f);\r
- g.setColor(textColor);\r
-\r
- AffineTransform orig = g.getTransform();\r
-\r
- double det = orig.getDeterminant();\r
- if (DEBUG)\r
- System.out.println("DETERMINANT: " + det);\r
-\r
- if (det < 0) {\r
- // Invert the Y-axis if the symbol is "flipped" either vertically xor horizontally\r
- origin.setLocation(textArea.getMinX(), textArea.getMaxY());\r
- xa.setLocation(textArea.getMaxX(), textArea.getMaxY());\r
- ya.setLocation(textArea.getMinX(), textArea.getMinY());\r
- } else {\r
- origin.setLocation(textArea.getMinX(), textArea.getMinY());\r
- xa.setLocation(textArea.getMaxX(), textArea.getMinY());\r
- ya.setLocation(textArea.getMinX(), textArea.getMaxY());\r
- }\r
-\r
- orig.transform(origin, origin);\r
- orig.transform(xa, xa);\r
- orig.transform(ya, ya);\r
-\r
- double xAxisX = xa.getX() - origin.getX();\r
- double xAxisY = xa.getY() - origin.getY();\r
- double yAxisX = ya.getX() - origin.getX();\r
- double yAxisY = ya.getY() - origin.getY();\r
-\r
- boolean needToFlip = xAxisX < 0 || yAxisY < 0;\r
- if (DEBUG)\r
- System.out.println("TEXT NEEDS FLIPPING: " + needToFlip);\r
-\r
- byte horizAlign = hAlign;\r
-\r
- if (needToFlip) {\r
- // Okay, the text would be upside-down if rendered directly with these axes.\r
- // Let's flip the origin to the diagonal point and\r
- // invert both x & y axis of the text area to get\r
- // the text the right way around. Also, horizontal alignment\r
- // needs to be switched unless it's centered.\r
- origin.setLocation(origin.getX() + xAxisX + yAxisX, origin.getY() + xAxisY + yAxisY);\r
- xAxisX = -xAxisX;\r
- xAxisY = -xAxisY;\r
- yAxisX = -yAxisX;\r
- yAxisY = -yAxisY;\r
-\r
- // Must flip horizontal alignment to keep text visually at the same\r
- // end as before.\r
- if (horizAlign == LEADING)\r
- horizAlign = TRAILING;\r
- else if (horizAlign == TRAILING)\r
- horizAlign = LEADING;\r
- }\r
-\r
- final double gScale = GLOBAL_SCALE;\r
- final double gScaleRecip = 1.0 / gScale;\r
- final double scale = GeometryUtils.getMaxScale(orig) * gScale;\r
- final double rotation = Math.atan2(xAxisY, xAxisX);\r
- g.setTransform(IDENTITY);\r
- g.translate(origin.getX(), origin.getY());\r
- g.rotate(rotation);\r
- g.scale(scale, scale);\r
-\r
- if (DEBUG) {\r
- System.out.println("ORIGIN: " + origin);\r
- System.out.println("X-AXIS: (" + xAxisX + "," + xAxisY + ")");\r
- System.out.println("Y-AXIS: (" + yAxisX + "," + yAxisY + ")");\r
- System.out.println("rotation: " + Math.toDegrees(rotation));\r
- System.out.println("scale: " + scale);\r
- System.out.println("ORIG transform: " + orig);\r
- System.out.println("transform: " + g.getTransform());\r
- }\r
-\r
- FontMetrics fm = g.getFontMetrics(f);\r
- double fontHeight = fm.getHeight();\r
-\r
- if (textLayout == null || (float) scale != lastViewScale)\r
- {\r
- lastViewScale = (float) scale;\r
- FontRenderContext frc = g.getFontRenderContext();\r
- if (textLayout == null)\r
- textLayout = new TextLayout[flagText.length];\r
- if (rects == null)\r
- rects = new Rectangle2D[flagText.length];\r
- textHeight = 0;\r
- for (int i = 0; i < flagText.length; ++i) {\r
- String txt = flagText[i].isEmpty() ? " " : flagText[i]; \r
- textLayout[i] = new TextLayout(txt, f, frc);\r
- rects[i] = textLayout[i].getBounds();\r
-\r
- // If the bb height is not overridden with the font height\r
- // text lines will not be drawn in the correct Y location.\r
- rects[i].setRect(rects[i].getX(), rects[i].getY(), rects[i].getWidth(), fontHeight);\r
-\r
- textHeight += rects[i].getHeight() * gScale;\r
- if (DEBUG)\r
- System.out.println(" bounding rectangle for line " + i + " '" + flagText[i] + "': " + rects[i]);\r
- }\r
- }\r
-\r
- double leftoverHeight = textArea.getHeight() - textHeight;\r
- if (leftoverHeight < 0)\r
- leftoverHeight = 0;\r
-\r
- if (DEBUG) {\r
- System.out.println("text area height: " + textArea.getHeight());\r
- System.out.println("total text height: " + textHeight);\r
- System.out.println("leftover height: " + leftoverHeight);\r
- }\r
-\r
- double lineDist = 0;\r
- double startY = 0;\r
-\r
- switch (vAlign) {\r
- case LEADING:\r
- if (DEBUG)\r
- System.out.println("VERTICAL LEADING");\r
- lineDist = leftoverHeight / flagText.length;\r
- startY = fm.getMaxAscent();\r
- break;\r
- case TRAILING:\r
- if (DEBUG)\r
- System.out.println("VERTICAL TRAILING");\r
- lineDist = leftoverHeight / flagText.length;\r
- startY = fm.getMaxAscent() + lineDist * gScaleRecip;\r
- break;\r
- case CENTER:\r
- if (DEBUG)\r
- System.out.println("VERTICAL CENTER");\r
- lineDist = leftoverHeight / (flagText.length + 1);\r
- startY = fm.getMaxAscent() + lineDist * gScaleRecip;\r
- break;\r
- }\r
-\r
- if (DEBUG) {\r
- System.out.println("lineDist: " + lineDist);\r
- System.out.println("startY: " + startY);\r
- }\r
-\r
- lineDist *= gScaleRecip;\r
- double y = startY;\r
- double textAreaWidth = textArea.getWidth() * gScaleRecip;\r
- boolean isRenderingPdf = g.getRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER) != null;\r
-\r
- for (int i = 0; i < flagText.length; ++i) {\r
- //String line = flagText[i];\r
- Rectangle2D rect = rects[i];\r
-\r
- double x = 0;\r
-\r
- switch (horizAlign) {\r
- case LEADING:\r
- if (DEBUG)\r
- System.out.println("HORIZ LEADING: " + rect);\r
- x = TEXT_MARGIN;\r
- break;\r
- case TRAILING:\r
- if (DEBUG)\r
- System.out.println("HORIZ TRAILING: " + rect);\r
- x = textAreaWidth - rect.getWidth() - TEXT_MARGIN;;\r
- break;\r
- case CENTER:\r
- if (DEBUG)\r
- System.out.println("HORIZ CENTER: " + rect);\r
- x = textAreaWidth * 0.5 - rect.getWidth()*0.5;\r
- break;\r
- }\r
-\r
- if (DEBUG)\r
- System.out.println(" X, Y: " + x + ", " + y);\r
-\r
- if (DEBUG)\r
- System.out.println(" DRAW: '" + flagText[i] + "' with " + g.getTransform());\r
-\r
- // #6459: render as text in PDF and paths on screen\r
- if (isRenderingPdf)\r
- g.drawString(flagText[i], (float) x, (float) y);\r
- else\r
- textLayout[i].draw(g, (float) x, (float) y);\r
-\r
- y += lineDist;\r
- y += rect.getHeight();\r
- }\r
-\r
- } finally {\r
- g.setTransform(ot);\r
- }\r
- }\r
-\r
- public static double getBeakLength(double height, double beakAngle) {\r
- beakAngle = Math.min(180, Math.max(10, beakAngle));\r
- return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));\r
- }\r
-\r
- @Override\r
- public Rectangle2D getBoundsInLocal() {\r
- if (flagShape == null)\r
- return null;\r
- return flagShape.getBounds2D();\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.scenegraph.g2d.nodes;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Stroke;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Arrays;
+
+import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.scenegraph.g2d.G2DPDFRenderingHints;
+import org.simantics.scenegraph.g2d.G2DRenderingHints;
+import org.simantics.scenegraph.g2d.G2DRenderingHints.TextRenderingMode;
+import org.simantics.scenegraph.utils.GeometryUtils;
+
+public class FlagNode extends G2DNode {
+
+ private static final long serialVersionUID = -1716729504104107151L;
+
+ private static final AffineTransform IDENTITY = new AffineTransform();
+
+ private static final byte LEADING = 0;
+ private static final byte TRAILING = 1;
+ private static final byte CENTER = 2;
+
+ private static final boolean DEBUG = false;
+
+ private static final double GLOBAL_SCALE = 0.1;
+
+ private static final double TEXT_MARGIN = 5;
+
+ static transient final BasicStroke STROKE = new BasicStroke(0.25f, BasicStroke.CAP_BUTT,
+ BasicStroke.JOIN_MITER);
+
+ public final static Font DEFAULT_FONT = Font.decode("Arial 12");
+
+ protected boolean visible;
+
+ protected Shape flagShape;
+ protected String[] flagText;
+ protected Stroke stroke;
+ protected Color border;
+ protected Color fill;
+ protected Color textColor;
+ protected float width;
+ protected float height;
+ protected double direction; // in radians
+ protected float beakAngle;
+ protected Rectangle2D textArea;
+ protected byte hAlign;
+ protected byte vAlign;
+ protected Font font = DEFAULT_FONT;
+
+ private transient final Point2D origin = new Point2D.Double();
+ private transient final Point2D xa = new Point2D.Double();
+ private transient final Point2D ya = new Point2D.Double();
+
+ protected transient TextLayout[] textLayout = null;
+ protected transient Rectangle2D[] rects = null;
+ protected transient float textHeight = 0;
+ protected transient float lastViewScale = 0;
+
+ @SyncField("visible")
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+
+ @SyncField({"visible", "flagShape", "flagText", "stroke", "border", "fill", "textColor", "width", "height", "direction", "beakAngle", "textSize", "hAlign", "vAlign", "font"})
+ public void init(Shape flagShape, String[] flagText, Stroke stroke, Color border, Color fill, Color textColor, float width, float height, double direction, float beakAngle, Rectangle2D textArea, int hAlign, int vAlign, Font font) {
+ this.visible = true;
+ this.flagShape = flagShape;
+ this.flagText = flagText;
+ this.stroke = stroke;
+ this.border = border;
+ this.fill = fill;
+ this.textColor = textColor;
+ this.width = width;
+ this.height = height;
+ this.direction = direction;
+ this.beakAngle = beakAngle;
+ this.textArea = textArea;
+ this.hAlign = (byte) hAlign;
+ this.vAlign = (byte) vAlign;
+ this.font = font;
+
+ resetCaches();
+ }
+
+ private void resetCaches() {
+ textLayout = null;
+ rects = null;
+ }
+
+ @Override
+ public void render(Graphics2D g) {
+ if (!visible)
+ return;
+
+ if (DEBUG) {
+ System.out.println("FlagNode.render:");
+ System.out.println("\tflagShape: " + flagShape);
+ System.out.println("\tflagText: " + Arrays.toString(flagText));
+ System.out.println("\tstroke: " + stroke);
+ System.out.println("\tborder: " + border);
+ System.out.println("\tfill: " + fill);
+ System.out.println("\ttextColor: " + textColor);
+ System.out.println("\twidth: " + width);
+ System.out.println("\theight: " + height);
+ System.out.println("\tdirection: " + direction);
+ System.out.println("\tbeakAngle: " + beakAngle);
+ System.out.println("\ttextArea: " + textArea);
+ System.out.println("\thAlign: " + hAlign);
+ System.out.println("\tvAlign: " + vAlign);
+ System.out.println("\tdraw: " + visible);
+ }
+
+ AffineTransform ot = g.getTransform();
+ g.transform(transform);
+
+ try {
+ Object renderingHint = g.getRenderingHint(RenderingHints.KEY_RENDERING);
+
+ //g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+ // Paint flag shape
+ g.setColor(fill);
+ g.fill(flagShape);
+ g.setStroke(stroke);
+ g.setColor(border);
+ g.draw(flagShape);
+
+ // Speed rendering optimization: don't draw text that is too small to read
+ if (renderingHint != RenderingHints.VALUE_RENDER_QUALITY) {
+ double viewScale = GeometryUtils.getScale(ot);
+ viewScale *= GeometryUtils.getScale(transform);
+ if (viewScale < 4.0)
+ return;
+ }
+
+ if (flagText == null || flagText.length == 0)
+ return;
+
+ if (DEBUG) {
+ g.setColor(Color.RED);
+ g.draw(textArea);
+ }
+
+ // Paint flag text
+ g.setFont(font);
+ g.setColor(textColor);
+
+ AffineTransform orig = g.getTransform();
+
+ double det = orig.getDeterminant();
+ if (DEBUG)
+ System.out.println("DETERMINANT: " + det);
+
+ if (det < 0) {
+ // Invert the Y-axis if the symbol is "flipped" either vertically xor horizontally
+ origin.setLocation(textArea.getMinX(), textArea.getMaxY());
+ xa.setLocation(textArea.getMaxX(), textArea.getMaxY());
+ ya.setLocation(textArea.getMinX(), textArea.getMinY());
+ } else {
+ origin.setLocation(textArea.getMinX(), textArea.getMinY());
+ xa.setLocation(textArea.getMaxX(), textArea.getMinY());
+ ya.setLocation(textArea.getMinX(), textArea.getMaxY());
+ }
+
+ orig.transform(origin, origin);
+ orig.transform(xa, xa);
+ orig.transform(ya, ya);
+
+ double xAxisX = xa.getX() - origin.getX();
+ double xAxisY = xa.getY() - origin.getY();
+ double yAxisX = ya.getX() - origin.getX();
+ double yAxisY = ya.getY() - origin.getY();
+
+ boolean needToFlip = xAxisX < 0 || yAxisY < 0;
+ if (DEBUG)
+ System.out.println("TEXT NEEDS FLIPPING: " + needToFlip);
+
+ byte horizAlign = hAlign;
+
+ if (needToFlip) {
+ // Okay, the text would be upside-down if rendered directly with these axes.
+ // Let's flip the origin to the diagonal point and
+ // invert both x & y axis of the text area to get
+ // the text the right way around. Also, horizontal alignment
+ // needs to be switched unless it's centered.
+ origin.setLocation(origin.getX() + xAxisX + yAxisX, origin.getY() + xAxisY + yAxisY);
+ xAxisX = -xAxisX;
+ xAxisY = -xAxisY;
+ yAxisX = -yAxisX;
+ yAxisY = -yAxisY;
+
+ // Must flip horizontal alignment to keep text visually at the same
+ // end as before.
+ if (horizAlign == LEADING)
+ horizAlign = TRAILING;
+ else if (horizAlign == TRAILING)
+ horizAlign = LEADING;
+ }
+
+ final double gScale = GLOBAL_SCALE;
+ final double gScaleRecip = 1.0 / gScale;
+ final double scale = GeometryUtils.getMaxScale(orig) * gScale;
+ final double rotation = Math.atan2(xAxisY, xAxisX);
+ g.setTransform(IDENTITY);
+ g.translate(origin.getX(), origin.getY());
+ g.rotate(rotation);
+ g.scale(scale, scale);
+
+ if (DEBUG) {
+ System.out.println("ORIGIN: " + origin);
+ System.out.println("X-AXIS: (" + xAxisX + "," + xAxisY + ")");
+ System.out.println("Y-AXIS: (" + yAxisX + "," + yAxisY + ")");
+ System.out.println("rotation: " + Math.toDegrees(rotation));
+ System.out.println("scale: " + scale);
+ System.out.println("ORIG transform: " + orig);
+ System.out.println("transform: " + g.getTransform());
+ }
+
+ FontMetrics fm = g.getFontMetrics(font);
+ double fontHeight = fm.getHeight();
+
+ if (textLayout == null || (float) scale != lastViewScale)
+ {
+ lastViewScale = (float) scale;
+ FontRenderContext frc = g.getFontRenderContext();
+ if (textLayout == null)
+ textLayout = new TextLayout[flagText.length];
+ if (rects == null)
+ rects = new Rectangle2D[flagText.length];
+ textHeight = 0;
+ for (int i = 0; i < flagText.length; ++i) {
+ String txt = flagText[i].isEmpty() ? " " : flagText[i];
+ textLayout[i] = new TextLayout(txt, font, frc);
+ rects[i] = textLayout[i].getBounds();
+
+ // If the bb height is not overridden with the font height
+ // text lines will not be drawn in the correct Y location.
+ rects[i].setRect(rects[i].getX(), rects[i].getY(), rects[i].getWidth(), fontHeight);
+
+ textHeight += rects[i].getHeight() * gScale;
+ if (DEBUG)
+ System.out.println(" bounding rectangle for line " + i + " '" + flagText[i] + "': " + rects[i]);
+ }
+ }
+
+ double leftoverHeight = textArea.getHeight() - textHeight;
+ if (leftoverHeight < 0)
+ leftoverHeight = 0;
+
+ if (DEBUG) {
+ System.out.println("text area height: " + textArea.getHeight());
+ System.out.println("total text height: " + textHeight);
+ System.out.println("leftover height: " + leftoverHeight);
+ }
+
+ double lineDist = 0;
+ double startY = 0;
+
+ switch (vAlign) {
+ case LEADING:
+ if (DEBUG)
+ System.out.println("VERTICAL LEADING");
+ lineDist = leftoverHeight / flagText.length;
+ startY = fm.getMaxAscent();
+ break;
+ case TRAILING:
+ if (DEBUG)
+ System.out.println("VERTICAL TRAILING");
+ lineDist = leftoverHeight / flagText.length;
+ startY = fm.getMaxAscent() + lineDist * gScaleRecip;
+ break;
+ case CENTER:
+ if (DEBUG)
+ System.out.println("VERTICAL CENTER");
+ lineDist = leftoverHeight / (flagText.length + 1);
+ startY = fm.getMaxAscent() + lineDist * gScaleRecip;
+ break;
+ }
+
+ if (DEBUG) {
+ System.out.println("lineDist: " + lineDist);
+ System.out.println("startY: " + startY);
+ }
+
+ lineDist *= gScaleRecip;
+ double y = startY;
+ double textAreaWidth = textArea.getWidth() * gScaleRecip;
+ boolean renderAsText = g.getRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER) != null
+ || g.getRenderingHint(G2DRenderingHints.KEY_TEXT_RENDERING_MODE) == TextRenderingMode.AS_TEXT;
+
+ for (int i = 0; i < flagText.length; ++i) {
+ //String line = flagText[i];
+ Rectangle2D rect = rects[i];
+
+ double x = 0;
+
+ switch (horizAlign) {
+ case LEADING:
+ if (DEBUG)
+ System.out.println("HORIZ LEADING: " + rect);
+ x = TEXT_MARGIN;
+ break;
+ case TRAILING:
+ if (DEBUG)
+ System.out.println("HORIZ TRAILING: " + rect);
+ x = textAreaWidth - rect.getWidth() - TEXT_MARGIN;;
+ break;
+ case CENTER:
+ if (DEBUG)
+ System.out.println("HORIZ CENTER: " + rect);
+ x = textAreaWidth * 0.5 - rect.getWidth()*0.5;
+ break;
+ }
+
+ if (DEBUG)
+ System.out.println(" X, Y: " + x + ", " + y);
+
+ if (DEBUG)
+ System.out.println(" DRAW: '" + flagText[i] + "' with " + g.getTransform());
+
+ // #6459: render as text in PDF and paths on screen
+ if (renderAsText)
+ g.drawString(flagText[i], (float) x, (float) y);
+ else
+ textLayout[i].draw(g, (float) x, (float) y);
+
+ y += lineDist;
+ y += rect.getHeight();
+ }
+
+ } finally {
+ g.setTransform(ot);
+ }
+ }
+
+ public static double getBeakLength(double height, double beakAngle) {
+ beakAngle = Math.min(180, Math.max(10, beakAngle));
+ return height / (2*Math.tan(Math.toRadians(beakAngle) / 2));
+ }
+
+ @Override
+ public Rectangle2D getBoundsInLocal() {
+ if (flagShape == null)
+ return null;
+ return flagShape.getBounds2D();
+ }
+
+}