001: /*
002: * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /*
027: * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
028: *
029: */
030:
031: package sun.font;
032:
033: import java.awt.Font;
034: import java.awt.Graphics2D;
035: import java.awt.Rectangle;
036: import java.awt.Shape;
037: import java.awt.font.FontRenderContext;
038: import java.awt.font.LineMetrics;
039: import java.awt.font.GraphicAttribute;
040: import java.awt.font.GlyphJustificationInfo;
041: import java.awt.geom.AffineTransform;
042: import java.awt.geom.GeneralPath;
043: import java.awt.geom.Rectangle2D;
044: import java.text.Bidi;
045: import java.util.Map;
046:
047: public final class GraphicComponent implements TextLineComponent,
048: Decoration.Label {
049:
050: public static final float GRAPHIC_LEADING = 2;
051:
052: private GraphicAttribute graphic;
053: private int graphicCount;
054: private int[] charsLtoV; // possibly null
055: private byte[] levels; // possibly null
056:
057: // evaluated in computeVisualBounds
058: private Rectangle2D visualBounds = null;
059:
060: // used everywhere so we'll cache it
061: private float graphicAdvance;
062:
063: private AffineTransform baseTx;
064:
065: private CoreMetrics cm;
066: private Decoration decorator;
067:
068: /**
069: * Create a new GraphicComponent. start and limit are indices
070: * into charLtoV and levels. charsLtoV and levels may be adopted.
071: */
072: public GraphicComponent(GraphicAttribute graphic,
073: Decoration decorator, int[] charsLtoV, byte[] levels,
074: int start, int limit, AffineTransform baseTx) {
075:
076: if (limit <= start) {
077: throw new IllegalArgumentException(
078: "0 or negative length in GraphicComponent");
079: }
080: this .graphic = graphic;
081: this .graphicAdvance = graphic.getAdvance();
082: this .decorator = decorator;
083: this .cm = createCoreMetrics(graphic);
084: this .baseTx = baseTx;
085:
086: initLocalOrdering(charsLtoV, levels, start, limit);
087: }
088:
089: private GraphicComponent(GraphicComponent parent, int start,
090: int limit, int dir) {
091:
092: this .graphic = parent.graphic;
093: this .graphicAdvance = parent.graphicAdvance;
094: this .decorator = parent.decorator;
095: this .cm = parent.cm;
096: this .baseTx = parent.baseTx;
097:
098: int[] charsLtoV = null;
099: byte[] levels = null;
100:
101: if (dir == UNCHANGED) {
102: charsLtoV = parent.charsLtoV;
103: levels = parent.levels;
104: } else if (dir == LEFT_TO_RIGHT || dir == RIGHT_TO_LEFT) {
105: limit -= start;
106: start = 0;
107: if (dir == RIGHT_TO_LEFT) {
108: charsLtoV = new int[limit];
109: levels = new byte[limit];
110: for (int i = 0; i < limit; i++) {
111: charsLtoV[i] = limit - i - 1;
112: levels[i] = (byte) 1;
113: }
114: }
115: } else {
116: throw new IllegalArgumentException("Invalid direction flag");
117: }
118:
119: initLocalOrdering(charsLtoV, levels, start, limit);
120: }
121:
122: /**
123: * Initialize graphicCount, also charsLtoV and levels arrays.
124: */
125: private void initLocalOrdering(int[] charsLtoV, byte[] levels,
126: int start, int limit) {
127:
128: this .graphicCount = limit - start; // todo: should be codepoints?
129:
130: if (charsLtoV == null || charsLtoV.length == graphicCount) {
131: this .charsLtoV = charsLtoV;
132: } else {
133: this .charsLtoV = BidiUtils.createNormalizedMap(charsLtoV,
134: levels, start, limit);
135: }
136:
137: if (levels == null || levels.length == graphicCount) {
138: this .levels = levels;
139: } else {
140: this .levels = new byte[graphicCount];
141: System.arraycopy(levels, start, this .levels, 0,
142: graphicCount);
143: }
144: }
145:
146: public boolean isSimple() {
147: return false;
148: }
149:
150: public Rectangle getPixelBounds(FontRenderContext frc, float x,
151: float y) {
152: throw new InternalError("do not call if isSimple returns false");
153: }
154:
155: public Rectangle2D handleGetVisualBounds() {
156:
157: Rectangle2D bounds = graphic.getBounds();
158:
159: float width = (float) bounds.getWidth() + graphicAdvance
160: * (graphicCount - 1);
161:
162: return new Rectangle2D.Float((float) bounds.getX(),
163: (float) bounds.getY(), width, (float) bounds
164: .getHeight());
165: }
166:
167: public CoreMetrics getCoreMetrics() {
168: return cm;
169: }
170:
171: public static CoreMetrics createCoreMetrics(GraphicAttribute graphic) {
172: return new CoreMetrics(graphic.getAscent(), graphic
173: .getDescent(), GRAPHIC_LEADING, graphic.getAscent()
174: + graphic.getDescent() + GRAPHIC_LEADING, graphic
175: .getAlignment(), new float[] { 0,
176: -graphic.getAscent() / 2, -graphic.getAscent() },
177: -graphic.getAscent() / 2, graphic.getAscent() / 12,
178: graphic.getDescent() / 3, graphic.getAscent() / 12, 0, // ss offset
179: 0); // italic angle -- need api for this
180: }
181:
182: public float getItalicAngle() {
183:
184: return 0;
185: }
186:
187: public Rectangle2D getVisualBounds() {
188:
189: if (visualBounds == null) {
190: visualBounds = decorator.getVisualBounds(this );
191: }
192: Rectangle2D.Float bounds = new Rectangle2D.Float();
193: bounds.setRect(visualBounds);
194: return bounds;
195: }
196:
197: public Shape handleGetOutline(float x, float y) {
198: double[] matrix = { 1, 0, 0, 1, x, y };
199:
200: if (graphicCount == 1) {
201: AffineTransform tx = new AffineTransform(matrix);
202: return graphic.getOutline(tx);
203: }
204:
205: GeneralPath gp = new GeneralPath();
206: for (int i = 0; i < graphicCount; ++i) {
207: AffineTransform tx = new AffineTransform(matrix);
208: gp.append(graphic.getOutline(tx), false);
209: matrix[4] += graphicAdvance;
210: }
211:
212: return gp;
213: }
214:
215: public AffineTransform getBaselineTransform() {
216: return baseTx;
217: }
218:
219: public Shape getOutline(float x, float y) {
220:
221: return decorator.getOutline(this , x, y);
222: }
223:
224: public void handleDraw(Graphics2D g2d, float x, float y) {
225:
226: for (int i = 0; i < graphicCount; i++) {
227:
228: graphic.draw(g2d, x, y);
229: x += graphicAdvance;
230: }
231: }
232:
233: public void draw(Graphics2D g2d, float x, float y) {
234:
235: decorator.drawTextAndDecorations(this , g2d, x, y);
236: }
237:
238: public Rectangle2D getCharVisualBounds(int index) {
239:
240: return decorator.getCharVisualBounds(this , index);
241: }
242:
243: public int getNumCharacters() {
244:
245: return graphicCount;
246: }
247:
248: public float getCharX(int index) {
249:
250: int visIndex = charsLtoV == null ? index : charsLtoV[index];
251: return graphicAdvance * visIndex;
252: }
253:
254: public float getCharY(int index) {
255:
256: return 0;
257: }
258:
259: public float getCharAdvance(int index) {
260:
261: return graphicAdvance;
262: }
263:
264: public boolean caretAtOffsetIsValid(int index) {
265:
266: return true;
267: }
268:
269: public Rectangle2D handleGetCharVisualBounds(int index) {
270:
271: Rectangle2D bounds = graphic.getBounds();
272: // don't modify their rectangle, just in case they don't copy
273:
274: Rectangle2D.Float charBounds = new Rectangle2D.Float();
275: charBounds.setRect(bounds);
276: charBounds.x += graphicAdvance * index;
277:
278: return charBounds;
279: }
280:
281: // measures characters in context, in logical order
282: public int getLineBreakIndex(int start, float width) {
283:
284: int index = (int) (width / graphicAdvance);
285: if (index > graphicCount - start) {
286: index = graphicCount - start;
287: }
288: return index;
289: }
290:
291: // measures characters in context, in logical order
292: public float getAdvanceBetween(int start, int limit) {
293:
294: return graphicAdvance * (limit - start);
295: }
296:
297: public Rectangle2D getLogicalBounds() {
298:
299: float left = 0;
300: float top = -cm.ascent;
301: float width = graphicAdvance * graphicCount;
302: float height = cm.descent - top;
303:
304: return new Rectangle2D.Float(left, top, width, height);
305: }
306:
307: public float getAdvance() {
308: return graphicAdvance * graphicCount;
309: }
310:
311: public Rectangle2D getItalicBounds() {
312: return getLogicalBounds();
313: }
314:
315: public TextLineComponent getSubset(int start, int limit, int dir) {
316:
317: if (start < 0 || limit > graphicCount || start >= limit) {
318: throw new IllegalArgumentException("Invalid range. start="
319: + start + "; limit=" + limit);
320: }
321:
322: if (start == 0 && limit == graphicCount && dir == UNCHANGED) {
323: return this ;
324: }
325:
326: return new GraphicComponent(this , start, limit, dir);
327: }
328:
329: public String toString() {
330:
331: return "[graphic=" + graphic + ":count=" + getNumCharacters()
332: + "]";
333: }
334:
335: /**
336: * Return the number of justification records this uses.
337: */
338: public int getNumJustificationInfos() {
339: return 0;
340: }
341:
342: /**
343: * Return GlyphJustificationInfo objects for the characters between
344: * charStart and charLimit, starting at offset infoStart. Infos
345: * will be in visual order. All positions between infoStart and
346: * getNumJustificationInfos will be set. If a position corresponds
347: * to a character outside the provided range, it is set to null.
348: */
349: public void getJustificationInfos(GlyphJustificationInfo[] infos,
350: int infoStart, int charStart, int charLimit) {
351: }
352:
353: /**
354: * Apply deltas to the data in this component, starting at offset
355: * deltaStart, and return the new component. There are two floats
356: * for each justification info, for a total of 2 * getNumJustificationInfos.
357: * The first delta is the left adjustment, the second is the right
358: * adjustment.
359: * <p>
360: * If flags[0] is true on entry, rejustification is allowed. If
361: * the new component requires rejustification (ligatures were
362: * formed or split), flags[0] will be set on exit.
363: */
364: public TextLineComponent applyJustificationDeltas(float[] deltas,
365: int deltaStart, boolean[] flags) {
366: return this;
367: }
368: }
|