2 Copyright 2006 Jerry Huxtable
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
17 package org.simantics.scenegraph.utils;
20 import java.awt.Shape;
21 import java.awt.Stroke;
22 import java.awt.font.FontRenderContext;
23 import java.awt.font.GlyphVector;
24 import java.awt.geom.AffineTransform;
25 import java.awt.geom.FlatteningPathIterator;
26 import java.awt.geom.GeneralPath;
27 import java.awt.geom.PathIterator;
28 import java.awt.geom.Point2D;
31 * Taken from http://www.jhlabs.com/java/java2d/strokes/
33 public class TextStroke implements Stroke {
34 private final String text;
35 private final Font font;
36 private boolean stretchToFit = false;
37 private boolean repeat = false;
38 private final AffineTransform t = new AffineTransform();
40 private static final float FLATNESS = 1;
42 public TextStroke( String text, Font font ) {
43 this( text, font, true, false );
46 public TextStroke( String text, Font font, boolean stretchToFit, boolean repeat ) {
49 this.stretchToFit = stretchToFit;
54 public Shape createStrokedShape( Shape shape ) {
55 FontRenderContext frc = new FontRenderContext(null, true, true);
56 GlyphVector glyphVector = font.createGlyphVector(frc, text);
58 GeneralPath result = new GeneralPath();
59 PathIterator it = new FlatteningPathIterator( shape.getPathIterator( null ), FLATNESS );
60 float points[] = new float[6];
61 float moveX = 0, moveY = 0;
62 float lastX = 0, lastY = 0;
63 float thisX = 0, thisY = 0;
65 @SuppressWarnings("unused")
66 boolean first = false;
69 int length = glyphVector.getNumGlyphs();
74 float factor = stretchToFit ? measurePathLength( shape )/(float)glyphVector.getLogicalBounds().getWidth() : 1.0f;
75 float nextAdvance = 0;
77 while ( currentChar < length && !it.isDone() ) {
78 type = it.currentSegment( points );
80 case PathIterator.SEG_MOVETO:
81 moveX = lastX = points[0];
82 moveY = lastY = points[1];
83 result.moveTo( moveX, moveY );
85 nextAdvance = glyphVector.getGlyphMetrics( currentChar ).getAdvance() * 0.5f;
89 case PathIterator.SEG_CLOSE:
94 case PathIterator.SEG_LINETO:
97 float dx = thisX-lastX;
98 float dy = thisY-lastY;
99 float distance = (float)Math.sqrt( dx*dx + dy*dy );
100 if ( distance >= next ) {
101 float r = 1.0f/distance;
102 float angle = (float)Math.atan2( dy, dx );
103 while ( currentChar < length && distance >= next ) {
104 Shape glyph = glyphVector.getGlyphOutline( currentChar );
105 Point2D p = glyphVector.getGlyphPosition(currentChar);
106 float px = (float)p.getX();
107 float py = (float)p.getY();
108 float x = lastX + next*dx*r;
109 float y = lastY + next*dy*r;
110 float advance = nextAdvance;
111 nextAdvance = currentChar < length-1 ? glyphVector.getGlyphMetrics(currentChar+1).getAdvance() * 0.5f : 0;
112 t.setToTranslation( x, y );
114 t.translate( -px-advance, -py );
115 result.append( t.createTransformedShape( glyph ), false );
116 next += (advance+nextAdvance) * factor;
119 currentChar %= length;
134 public float measurePathLength( Shape shape ) {
135 PathIterator it = new FlatteningPathIterator( shape.getPathIterator( null ), FLATNESS );
136 float points[] = new float[6];
137 float moveX = 0, moveY = 0;
138 float lastX = 0, lastY = 0;
139 float thisX = 0, thisY = 0;
143 while ( !it.isDone() ) {
144 type = it.currentSegment( points );
146 case PathIterator.SEG_MOVETO:
147 moveX = lastX = points[0];
148 moveY = lastY = points[1];
151 case PathIterator.SEG_CLOSE:
156 case PathIterator.SEG_LINETO:
159 float dx = thisX-lastX;
160 float dy = thisY-lastY;
161 total += (float)Math.sqrt( dx*dx + dy*dy );