001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.gvt.font;
020:
021: import java.awt.Graphics2D;
022: import java.awt.Shape;
023: import java.awt.font.GlyphMetrics;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.GeneralPath;
026: import java.awt.geom.Point2D;
027: import java.awt.geom.Rectangle2D;
028: import java.util.Vector;
029: import java.util.List;
030:
031: import org.apache.batik.gvt.GraphicsNode;
032: import org.apache.batik.gvt.text.TextPaintInfo;
033:
034: /**
035: * A Glyph describes a graphics node with some specific glyph rendering
036: * attributes.
037: *
038: * @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
039: * @version $Id: Glyph.java 501844 2007-01-31 13:54:05Z dvholten $
040: */
041: public class Glyph {
042:
043: private String unicode;
044: private Vector names;
045: private String orientation;
046: private String arabicForm;
047: private String lang;
048: private Point2D horizOrigin;
049: private Point2D vertOrigin;
050: private float horizAdvX;
051: private float vertAdvY;
052: private int glyphCode;
053: private AffineTransform transform;
054: private Point2D.Float position;
055: private GVTGlyphMetrics metrics;
056:
057: private Shape outline; // cache the glyph outline
058: private Rectangle2D bounds; // cache the glyph bounds
059:
060: private TextPaintInfo tpi;
061: private TextPaintInfo cacheTPI;
062: private Shape dShape;
063: private GraphicsNode glyphChildrenNode;
064:
065: /**
066: * Constructs a Glyph with the specified parameters.
067: */
068: public Glyph(String unicode, List names, String orientation,
069: String arabicForm, String lang, Point2D horizOrigin,
070: Point2D vertOrigin, float horizAdvX, float vertAdvY,
071: int glyphCode, TextPaintInfo tpi, Shape dShape,
072: GraphicsNode glyphChildrenNode) {
073:
074: if (unicode == null) {
075: throw new IllegalArgumentException();
076: }
077: if (horizOrigin == null) {
078: throw new IllegalArgumentException();
079: }
080: if (vertOrigin == null) {
081: throw new IllegalArgumentException();
082: }
083:
084: this .unicode = unicode;
085: this .names = new Vector(names);
086: this .orientation = orientation;
087: this .arabicForm = arabicForm;
088: this .lang = lang;
089: this .horizOrigin = horizOrigin;
090: this .vertOrigin = vertOrigin;
091: this .horizAdvX = horizAdvX;
092: this .vertAdvY = vertAdvY;
093: this .glyphCode = glyphCode;
094: this .position = new Point2D.Float(0, 0);
095: this .outline = null;
096: this .bounds = null;
097:
098: this .tpi = tpi;
099: this .dShape = dShape;
100: this .glyphChildrenNode = glyphChildrenNode;
101: }
102:
103: /**
104: * Returns the unicode char or chars this glyph represents.
105: *
106: * @return The glyphs unicode value.
107: */
108: public String getUnicode() {
109: return unicode;
110: }
111:
112: /**
113: * Returns the names of this glyph.
114: *
115: * @return The glyph names.
116: */
117: public Vector getNames() {
118: return names;
119: }
120:
121: /**
122: * Returns the orientation of this glyph.
123: * Indicates what inline-progression-direction this glyph
124: * can be used in. Should be either "h" for horizontal only, "v" for vertical
125: * only, or empty which indicates that the glyph can be used in both.
126: *
127: * @return The glyph orientation.
128: */
129: public String getOrientation() {
130: return orientation;
131: }
132:
133: /**
134: * Returns which of the four possible arabic forms this glyph represents.
135: * This is only used for arabic glyphs.
136: *
137: * @return The glyphs arabic form.
138: */
139: public String getArabicForm() {
140: return arabicForm;
141: }
142:
143: /**
144: * Returns a comma separated list of languages this glyph can be used in.
145: *
146: * @return The glyph languages.
147: */
148: public String getLang() {
149: return lang;
150: }
151:
152: /**
153: * Returns the horizontal origin of this glyph.
154: *
155: * @return The horizontal origin.
156: */
157: public Point2D getHorizOrigin() {
158: return horizOrigin;
159: }
160:
161: /**
162: * Returns the vertical origin of this glyph.
163: *
164: * @return The vertical origin.
165: */
166: public Point2D getVertOrigin() {
167: return vertOrigin;
168: }
169:
170: /**
171: * Returns the horizontal advance value.
172: *
173: * @return This glyph's horizontal advance.
174: */
175: public float getHorizAdvX() {
176: return horizAdvX;
177: }
178:
179: /**
180: * Returns the vertical advance value.
181: *
182: * @return the glyph's vertical advance.
183: */
184: public float getVertAdvY() {
185: return vertAdvY;
186: }
187:
188: /**
189: * Returns the glyphs unique code with resect to its font. This will be
190: * the index into the font's list of glyphs.
191: *
192: * @return The glyph's unique code.
193: */
194: public int getGlyphCode() {
195: return glyphCode;
196: }
197:
198: /**
199: * Returns the glpyh's transform.
200: *
201: * @return The glyph's transform.
202: */
203: public AffineTransform getTransform() {
204: return transform;
205: }
206:
207: /**
208: * Sets the transform to be applied to this glyph.
209: *
210: * @param transform The transform to set.
211: */
212: public void setTransform(AffineTransform transform) {
213: this .transform = transform;
214: outline = null;
215: bounds = null;
216: }
217:
218: /**
219: * Returns the position of this glyph.
220: *
221: * @return The glyph's position.
222: */
223: public Point2D getPosition() {
224: return position;
225: }
226:
227: /**
228: * Sets the position of the glyph.
229: *
230: * @param position The new glyph position.
231: */
232: public void setPosition(Point2D position) {
233: this .position.x = (float) position.getX();
234: this .position.y = (float) position.getY();
235: outline = null;
236: bounds = null;
237: }
238:
239: /**
240: * Returns the metrics of this Glyph if it is used in a horizontal layout.
241: *
242: * @return The glyph metrics.
243: */
244: public GVTGlyphMetrics getGlyphMetrics() {
245: if (metrics == null) {
246: Rectangle2D gb = getGeometryBounds();
247:
248: metrics = new GVTGlyphMetrics(getHorizAdvX(),
249: getVertAdvY(), new Rectangle2D.Double(gb.getX()
250: - position.getX(), gb.getY()
251: - position.getY(), gb.getWidth(), gb
252: .getHeight()), GlyphMetrics.COMPONENT);
253: }
254: return metrics;
255: }
256:
257: /**
258: * Returns the metics of this Glyph with the specified kerning value
259: * applied.
260: *
261: * @param hkern The horizontal kerning value to apply when calculating
262: * the glyph metrics.
263: * @param vkern The horizontal vertical value to apply when calculating
264: * the glyph metrics.
265: * @return The kerned glyph metics
266: */
267: public GVTGlyphMetrics getGlyphMetrics(float hkern, float vkern) {
268: return new GVTGlyphMetrics(getHorizAdvX() - hkern,
269: getVertAdvY() - vkern, getGeometryBounds(),
270: GlyphMetrics.COMPONENT);
271:
272: }
273:
274: public Rectangle2D getGeometryBounds() {
275: return getOutline().getBounds2D();
276: }
277:
278: public Rectangle2D getBounds2D() {
279: // Check if the TextPaintInfo has changed...
280: if ((bounds != null) && TextPaintInfo.equivilent(tpi, cacheTPI))
281: return bounds;
282:
283: AffineTransform tr = AffineTransform.getTranslateInstance(
284: position.getX(), position.getY());
285: if (transform != null) {
286: tr.concatenate(transform);
287: }
288:
289: Rectangle2D bounds = null;
290: if ((dShape != null) && (tpi != null)) {
291: if (tpi.fillPaint != null)
292: bounds = tr.createTransformedShape(dShape)
293: .getBounds2D();
294:
295: if ((tpi.strokeStroke != null) && (tpi.strokePaint != null)) {
296: Shape s = tpi.strokeStroke.createStrokedShape(dShape);
297: Rectangle2D r = tr.createTransformedShape(s)
298: .getBounds2D();
299: if (bounds == null)
300: bounds = r;
301: //else bounds = r.createUnion(bounds);
302: else
303: bounds.add(r);
304: }
305: }
306:
307: if (glyphChildrenNode != null) {
308: Rectangle2D r = glyphChildrenNode.getTransformedBounds(tr);
309: if (bounds == null)
310: bounds = r;
311: // else bounds = r.createUnion(bounds);
312: else
313: bounds.add(r);
314: }
315: if (bounds == null)
316: bounds = new Rectangle2D.Double(position.getX(), position
317: .getY(), 0, 0);
318:
319: cacheTPI = new TextPaintInfo(tpi);
320: return bounds;
321: }
322:
323: /**
324: * Returns the outline of this glyph. This will be positioned correctly and
325: * any glyph transforms will have been applied.
326: *
327: * @return the outline of this glyph.
328: */
329: public Shape getOutline() {
330: if (outline == null) {
331: AffineTransform tr = AffineTransform.getTranslateInstance(
332: position.getX(), position.getY());
333: if (transform != null) {
334: tr.concatenate(transform);
335: }
336: Shape glyphChildrenOutline = null;
337: if (glyphChildrenNode != null) {
338: glyphChildrenOutline = glyphChildrenNode.getOutline();
339: }
340: GeneralPath glyphOutline = null;
341: if (dShape != null && glyphChildrenOutline != null) {
342: glyphOutline = new GeneralPath(dShape);
343: glyphOutline.append(glyphChildrenOutline, false);
344: } else if (dShape != null && glyphChildrenOutline == null) {
345: glyphOutline = new GeneralPath(dShape);
346: } else if (dShape == null && glyphChildrenOutline != null) {
347: glyphOutline = new GeneralPath(glyphChildrenOutline);
348: } else {
349: // must be a whitespace glyph, return an empty shape
350: glyphOutline = new GeneralPath();
351: }
352: outline = tr.createTransformedShape(glyphOutline);
353: }
354: return outline;
355: }
356:
357: /**
358: * Draws this glyph.
359: *
360: * @param graphics2D The Graphics2D object to draw to.
361: */
362: public void draw(Graphics2D graphics2D) {
363: AffineTransform tr = AffineTransform.getTranslateInstance(
364: position.getX(), position.getY());
365: if (transform != null) {
366: tr.concatenate(transform);
367: }
368:
369: // paint the dShape first
370: if ((dShape != null) && (tpi != null)) {
371: Shape tShape = tr.createTransformedShape(dShape);
372: if (tpi.fillPaint != null) {
373: graphics2D.setPaint(tpi.fillPaint);
374: graphics2D.fill(tShape);
375: }
376:
377: // check if we need to draw the outline of this glyph
378: if (tpi.strokeStroke != null && tpi.strokePaint != null) {
379: graphics2D.setStroke(tpi.strokeStroke);
380: graphics2D.setPaint(tpi.strokePaint);
381: graphics2D.draw(tShape);
382: }
383: }
384:
385: // paint the glyph children nodes
386: if (glyphChildrenNode != null) {
387: glyphChildrenNode.setTransform(tr);
388: glyphChildrenNode.paint(graphics2D);
389: }
390: }
391: }
|