blob: 99190d8362aae3caa25a275a544e2b30017580b7 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1998-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26/*
27 * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
28 *
29 */
30
31package sun.font;
32
33import java.awt.Font;
34import java.awt.Graphics2D;
35import java.awt.Rectangle;
36import java.awt.Shape;
37import java.awt.font.FontRenderContext;
38import java.awt.font.LineMetrics;
39import java.awt.font.GraphicAttribute;
40import java.awt.font.GlyphJustificationInfo;
41import java.awt.geom.AffineTransform;
42import java.awt.geom.GeneralPath;
43import java.awt.geom.Rectangle2D;
44import java.text.Bidi;
45import java.util.Map;
46
47public final class GraphicComponent implements TextLineComponent,
48 Decoration.Label {
49
50 public static final float GRAPHIC_LEADING = 2;
51
52 private GraphicAttribute graphic;
53 private int graphicCount;
54 private int[] charsLtoV; // possibly null
55 private byte[] levels; // possibly null
56
57 // evaluated in computeVisualBounds
58 private Rectangle2D visualBounds = null;
59
60 // used everywhere so we'll cache it
61 private float graphicAdvance;
62
63 private AffineTransform baseTx;
64
65 private CoreMetrics cm;
66 private Decoration decorator;
67
68
69 /**
70 * Create a new GraphicComponent. start and limit are indices
71 * into charLtoV and levels. charsLtoV and levels may be adopted.
72 */
73 public GraphicComponent(GraphicAttribute graphic,
74 Decoration decorator,
75 int[] charsLtoV,
76 byte[] levels,
77 int start,
78 int limit,
79 AffineTransform baseTx) {
80
81 if (limit <= start) {
82 throw new IllegalArgumentException("0 or negative length in GraphicComponent");
83 }
84 this.graphic = graphic;
85 this.graphicAdvance = graphic.getAdvance();
86 this.decorator = decorator;
87 this.cm = createCoreMetrics(graphic);
88 this.baseTx = baseTx;
89
90 initLocalOrdering(charsLtoV, levels, start, limit);
91 }
92
93 private GraphicComponent(GraphicComponent parent, int start, int limit, int dir) {
94
95 this.graphic = parent.graphic;
96 this.graphicAdvance = parent.graphicAdvance;
97 this.decorator = parent.decorator;
98 this.cm = parent.cm;
99 this.baseTx = parent.baseTx;
100
101 int[] charsLtoV = null;
102 byte[] levels = null;
103
104 if (dir == UNCHANGED) {
105 charsLtoV = parent.charsLtoV;
106 levels = parent.levels;
107 }
108 else if (dir == LEFT_TO_RIGHT || dir == RIGHT_TO_LEFT) {
109 limit -= start;
110 start = 0;
111 if (dir == RIGHT_TO_LEFT) {
112 charsLtoV = new int[limit];
113 levels = new byte[limit];
114 for (int i=0; i < limit; i++) {
115 charsLtoV[i] = limit-i-1;
116 levels[i] = (byte) 1;
117 }
118 }
119 }
120 else {
121 throw new IllegalArgumentException("Invalid direction flag");
122 }
123
124 initLocalOrdering(charsLtoV, levels, start, limit);
125 }
126
127 /**
128 * Initialize graphicCount, also charsLtoV and levels arrays.
129 */
130 private void initLocalOrdering(int[] charsLtoV,
131 byte[] levels,
132 int start,
133 int limit) {
134
135 this.graphicCount = limit - start; // todo: should be codepoints?
136
137 if (charsLtoV == null || charsLtoV.length == graphicCount) {
138 this.charsLtoV = charsLtoV;
139 }
140 else {
141 this.charsLtoV = BidiUtils.createNormalizedMap(charsLtoV, levels, start, limit);
142 }
143
144 if (levels == null || levels.length == graphicCount) {
145 this.levels = levels;
146 }
147 else {
148 this.levels = new byte[graphicCount];
149 System.arraycopy(levels, start, this.levels, 0, graphicCount);
150 }
151 }
152
153 public boolean isSimple() {
154 return false;
155 }
156
157 public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
158 throw new InternalError("do not call if isSimple returns false");
159 }
160
161 public Rectangle2D handleGetVisualBounds() {
162
163 Rectangle2D bounds = graphic.getBounds();
164
165 float width = (float) bounds.getWidth() +
166 graphicAdvance * (graphicCount-1);
167
168 return new Rectangle2D.Float((float) bounds.getX(),
169 (float) bounds.getY(),
170 width,
171 (float) bounds.getHeight());
172 }
173
174 public CoreMetrics getCoreMetrics() {
175 return cm;
176 }
177
178 public static CoreMetrics createCoreMetrics(GraphicAttribute graphic) {
179 return new CoreMetrics(graphic.getAscent(),
180 graphic.getDescent(),
181 GRAPHIC_LEADING,
182 graphic.getAscent() + graphic.getDescent() + GRAPHIC_LEADING,
183 graphic.getAlignment(),
184 new float[] { 0, -graphic.getAscent() / 2, -graphic.getAscent() },
185 -graphic.getAscent() / 2,
186 graphic.getAscent() / 12,
187 graphic.getDescent() / 3,
188 graphic.getAscent() / 12,
189 0, // ss offset
190 0); // italic angle -- need api for this
191 }
192
193 public float getItalicAngle() {
194
195 return 0;
196 }
197
198 public Rectangle2D getVisualBounds() {
199
200 if (visualBounds == null) {
201 visualBounds = decorator.getVisualBounds(this);
202 }
203 Rectangle2D.Float bounds = new Rectangle2D.Float();
204 bounds.setRect(visualBounds);
205 return bounds;
206 }
207
208 public Shape handleGetOutline(float x, float y) {
209 double[] matrix = { 1, 0, 0, 1, x, y };
210
211 if (graphicCount == 1) {
212 AffineTransform tx = new AffineTransform(matrix);
213 return graphic.getOutline(tx);
214 }
215
216 GeneralPath gp = new GeneralPath();
217 for (int i = 0; i < graphicCount; ++i) {
218 AffineTransform tx = new AffineTransform(matrix);
219 gp.append(graphic.getOutline(tx), false);
220 matrix[4] += graphicAdvance;
221 }
222
223 return gp;
224 }
225
226 public AffineTransform getBaselineTransform() {
227 return baseTx;
228 }
229
230 public Shape getOutline(float x, float y) {
231
232 return decorator.getOutline(this, x, y);
233 }
234
235 public void handleDraw(Graphics2D g2d, float x, float y) {
236
237 for (int i=0; i < graphicCount; i++) {
238
239 graphic.draw(g2d, x, y);
240 x += graphicAdvance;
241 }
242 }
243
244 public void draw(Graphics2D g2d, float x, float y) {
245
246 decorator.drawTextAndDecorations(this, g2d, x, y);
247 }
248
249 public Rectangle2D getCharVisualBounds(int index) {
250
251 return decorator.getCharVisualBounds(this, index);
252 }
253
254 public int getNumCharacters() {
255
256 return graphicCount;
257 }
258
259 public float getCharX(int index) {
260
261 int visIndex = charsLtoV==null? index : charsLtoV[index];
262 return graphicAdvance * visIndex;
263 }
264
265 public float getCharY(int index) {
266
267 return 0;
268 }
269
270 public float getCharAdvance(int index) {
271
272 return graphicAdvance;
273 }
274
275 public boolean caretAtOffsetIsValid(int index) {
276
277 return true;
278 }
279
280 public Rectangle2D handleGetCharVisualBounds(int index) {
281
282 Rectangle2D bounds = graphic.getBounds();
283 // don't modify their rectangle, just in case they don't copy
284
285 Rectangle2D.Float charBounds = new Rectangle2D.Float();
286 charBounds.setRect(bounds);
287 charBounds.x += graphicAdvance * index;
288
289 return charBounds;
290 }
291
292 // measures characters in context, in logical order
293 public int getLineBreakIndex(int start, float width) {
294
295 int index = (int) (width / graphicAdvance);
296 if (index > graphicCount - start) {
297 index = graphicCount - start;
298 }
299 return index;
300 }
301
302 // measures characters in context, in logical order
303 public float getAdvanceBetween(int start, int limit) {
304
305 return graphicAdvance * (limit - start);
306 }
307
308 public Rectangle2D getLogicalBounds() {
309
310 float left = 0;
311 float top = -cm.ascent;
312 float width = graphicAdvance * graphicCount;
313 float height = cm.descent - top;
314
315 return new Rectangle2D.Float(left, top, width, height);
316 }
317
318 public float getAdvance() {
319 return graphicAdvance * graphicCount;
320 }
321
322 public Rectangle2D getItalicBounds() {
323 return getLogicalBounds();
324 }
325
326 public TextLineComponent getSubset(int start, int limit, int dir) {
327
328 if (start < 0 || limit > graphicCount || start >= limit) {
329 throw new IllegalArgumentException("Invalid range. start="
330 +start+"; limit="+limit);
331 }
332
333 if (start == 0 && limit == graphicCount && dir == UNCHANGED) {
334 return this;
335 }
336
337 return new GraphicComponent(this, start, limit, dir);
338 }
339
340 public String toString() {
341
342 return "[graphic=" + graphic + ":count=" + getNumCharacters() + "]";
343 }
344
345 /**
346 * Return the number of justification records this uses.
347 */
348 public int getNumJustificationInfos() {
349 return 0;
350 }
351
352 /**
353 * Return GlyphJustificationInfo objects for the characters between
354 * charStart and charLimit, starting at offset infoStart. Infos
355 * will be in visual order. All positions between infoStart and
356 * getNumJustificationInfos will be set. If a position corresponds
357 * to a character outside the provided range, it is set to null.
358 */
359 public void getJustificationInfos(GlyphJustificationInfo[] infos, int infoStart, int charStart, int charLimit) {
360 }
361
362 /**
363 * Apply deltas to the data in this component, starting at offset
364 * deltaStart, and return the new component. There are two floats
365 * for each justification info, for a total of 2 * getNumJustificationInfos.
366 * The first delta is the left adjustment, the second is the right
367 * adjustment.
368 * <p>
369 * If flags[0] is true on entry, rejustification is allowed. If
370 * the new component requires rejustification (ligatures were
371 * formed or split), flags[0] will be set on exit.
372 */
373 public TextLineComponent applyJustificationDeltas(float[] deltas, int deltaStart, boolean[] flags) {
374 return this;
375 }
376}