KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > font > TextPathLayout


1 /*
2
3    Copyright 1999-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17 */

18
19 package org.apache.batik.ext.awt.font;
20
21 import java.awt.Shape JavaDoc;
22 import java.awt.font.GlyphMetrics JavaDoc;
23 import java.awt.font.GlyphVector JavaDoc;
24 import java.awt.geom.AffineTransform JavaDoc;
25 import java.awt.geom.GeneralPath JavaDoc;
26 import java.awt.geom.Point2D JavaDoc;
27
28 import org.apache.batik.ext.awt.geom.PathLength;
29
30 /**
31  * PathLayout can layout text along a Shape, usually a Path object.
32  * <p>
33  * There are a number of improvements that could be made to this class.
34  * I'll try to list some of them:
35  * <ul>
36  * <li> The layout should really only modify the GlyphVector, rather
37  * than converting to a Shape.
38  * <li> Maybe the functions should take a AttributedCharacterIterator
39  * or something? Should this class do the entire layout?
40  * <li> The layout code works, but it's definitely not perfect.
41  * </ul>
42  * @author <a HREF="mailto:dean.jackson@cmis.csiro.au">Dean Jackson</a>
43  * @version $Id: TextPathLayout.java,v 1.4 2004/10/30 18:38:04 deweese Exp $
44  */

45
46 public class TextPathLayout {
47
48     /**
49      * Align the text at the start of the path.
50      */

51     static public final int ALIGN_START = 0;
52     /**
53      * Align the text at the middle of the path.
54      */

55     static public final int ALIGN_MIDDLE = 1;
56     /**
57      * Align the text at the end of the path.
58      */

59     static public final int ALIGN_END = 2;
60
61     /**
62      * Use the spacing between the glyphs to adjust for textLength.
63      */

64     static public final int ADJUST_SPACING = 0;
65     /**
66      * Use the entire glyph to adjust for textLength.
67      */

68     static public final int ADJUST_GLYPHS = 1;
69
70     /**
71      * Wraps the GlyphVector around the given path. The results
72      * are mostly quite nice but you need to be careful choosing
73      * the size of the font that created the GlyphVector, as
74      * well as the "curvyness" of the path (really dynamic curves
75      * don't look so great, abrupt changes/vertices look worse).
76      *
77      * @param glyphs The GlyphVector to layout.
78      * @param path The path (or shape) to wrap around
79      * @param align The text alignment to use. Should be one
80      * of ALIGN_START, ALIGN_MIDDLE or ALIGN_END.
81      * @param startOffset The offset from the start of the path for the initial
82      * text position.
83      * @param textLength The length that the text should fill.
84      * @param lengthAdjustMode The method used to expand or contract
85      * the text to meet the textLength.
86      * @return A shape that is the outline of the glyph vector
87      * wrapped along the path
88      */

89
90
91     static public Shape JavaDoc layoutGlyphVector(GlyphVector JavaDoc glyphs,
92                       Shape JavaDoc path, int align,
93                       float startOffset,
94                       float textLength,
95                       int lengthAdjustMode) {
96
97     GeneralPath JavaDoc newPath = new GeneralPath JavaDoc();
98     PathLength pl = new PathLength(path);
99     float pathLength = pl.lengthOfPath();
100     float glyphsLength = (float) glyphs.getVisualBounds().getWidth();
101
102     // return from the ugly cases
103
if (path == null ||
104         glyphs == null ||
105         glyphs.getNumGlyphs() == 0 ||
106         pl.lengthOfPath() == 0f ||
107         glyphsLength == 0f) {
108         return newPath;
109     }
110
111     // work out the expansion/contraction per character
112
float lengthRatio = textLength / glyphsLength;
113
114     // the current start point of the character on the path
115
float currentPosition = startOffset;
116
117     // if align is START then a currentPosition of 0f
118
// is correct.
119
// if align is END then the currentPosition should
120
// be enough to place the last character on the end
121
// of the path
122
// if align is MIDDLE then the currentPosition should
123
// be enough to center the glyphs on the path
124

125     if (align == ALIGN_END) {
126         currentPosition += pathLength - textLength;
127     } else if (align == ALIGN_MIDDLE) {
128         currentPosition += (pathLength - textLength) / 2;
129     }
130
131     // iterate through the GlyphVector placing each glyph
132

133     for (int i = 0; i < glyphs.getNumGlyphs(); i++) {
134         
135         GlyphMetrics JavaDoc gm = glyphs.getGlyphMetrics(i);
136
137         float charAdvance = gm.getAdvance();
138
139         Shape JavaDoc glyph = glyphs.getGlyphOutline(i);
140
141         // if lengthAdjust was GLYPHS, then scale the glyph
142
// by the lengthRatio in the X direction
143
// FIXME: for vertical text this will be the Y direction
144
if (lengthAdjustMode == ADJUST_GLYPHS) {
145         AffineTransform JavaDoc scale = AffineTransform.getScaleInstance(lengthRatio, 1f);
146         glyph = scale.createTransformedShape(glyph);
147
148         // charAdvance has to scale accordingly
149
charAdvance *= lengthRatio;
150         }
151
152         float glyphWidth = (float) glyph.getBounds2D().getWidth();
153         
154         // Use either of these to calculate the mid point
155
// of the character along the path.
156
// If you change this, you must also change the
157
// transform on the glyph down below
158
// In some case this gives better layout, but
159
// the way it is at the moment is a closer match
160
// to the textPath layout from the SVG spec
161

162         //float charMidPos = currentPosition + charAdvance / 2f;
163
float charMidPos = currentPosition + glyphWidth / 2f;
164
165         // Calculate the actual point to place the glyph around
166
Point2D JavaDoc charMidPoint = pl.pointAtLength(charMidPos);
167
168         // Check if the glyph is actually on the path
169

170         if (charMidPoint != null) {
171
172         // Calculate the normal to the path (midline of glyph)
173
float angle = pl.angleAtLength(charMidPos);
174
175         // Define the transform of the glyph
176
AffineTransform JavaDoc glyphTrans = new AffineTransform JavaDoc();
177
178         // translate to the point on the path
179
glyphTrans.translate(charMidPoint.getX(), charMidPoint.getY());
180
181         // rotate midline of glyph to be normal to path
182
glyphTrans.rotate(angle);
183
184         // translate glyph backwards so we rotate about the
185
// center of the glyph
186
// Choose one of these translations - see the comments
187
// in the charMidPos calculation above
188
glyphTrans.translate(charAdvance / -2f, 0f);
189         //glyphTrans.translate(glyphWidth / -2f, 0f);
190

191         glyph = glyphTrans.createTransformedShape(glyph);
192         newPath.append(glyph, false);
193
194         }
195         
196         // move along by the advance value
197
// if the lengthAdjustMode was SPACING then
198
// we have to take this into account here
199
if (lengthAdjustMode == ADJUST_SPACING) {
200         currentPosition += (charAdvance * lengthRatio);
201         } else {
202         currentPosition += charAdvance;
203         }
204         
205     }
206
207     return newPath;
208     }
209
210     /**
211      * Wraps the GlyphVector around the given path.
212      *
213      * @param glyphs The GlyphVector to layout.
214      * @param path The path (or shape) to wrap around
215      * @param align The text alignment to use. Should be one
216      * of ALIGN_START, ALIGN_MIDDLE or ALIGN_END.
217      * @return A shape that is the outline of the glyph vector
218      * wrapped along the path
219      */

220
221     static public Shape JavaDoc layoutGlyphVector(GlyphVector JavaDoc glyphs,
222                       Shape JavaDoc path, int align) {
223
224     return layoutGlyphVector(glyphs, path, align, 0f,
225                  (float) glyphs.getVisualBounds().getWidth(),
226                  ADJUST_SPACING);
227     }
228
229     /**
230      * Wraps the GlyphVector around the given path.
231      *
232      * @param glyphs The GlyphVector to layout.
233      * @param path The path (or shape) to wrap around
234      * @return A shape that is the outline of the glyph vector
235      * wrapped along the path
236      */

237
238     static public Shape JavaDoc layoutGlyphVector(GlyphVector JavaDoc glyphs,
239                       Shape JavaDoc path) {
240
241     return layoutGlyphVector(glyphs, path, ALIGN_START);
242     }
243
244
245 } // TextPathLayout
246
Popular Tags