]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/FlagNode.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / FlagNode.java
index c62fc84da66af45c2afe503ebe19cebc28cc9c55..4468996fb81d57e995bcc0c34162feb94e672cd8 100644 (file)
-/*******************************************************************************\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.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);
+
+    final transient Font                 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;
+
+    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"})
+    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) {
+        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;
+
+        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
+            Font f = FONT;
+            g.setFont(f);
+            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(f);
+            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, f, 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 isRenderingPdf = g.getRenderingHint(G2DPDFRenderingHints.KEY_PDF_WRITER) != null;
+
+            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 (isRenderingPdf)
+                    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();
+    }
+
+}