History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/awt/font (56 of 18352 files). Show all

Unified · Split

56 files
  1. java/pkg/java/awt/font/CharArrayIterator.class +0 −0
  2. java/pkg/java/awt/font/CharArrayIterator.java +193 −0
  3. java/pkg/java/awt/font/FontRenderContext.class +0 −0
  4. java/pkg/java/awt/font/FontRenderContext.java +358 −0
  5. java/pkg/java/awt/font/GlyphJustificationInfo.class +0 −0
  6. java/pkg/java/awt/font/GlyphJustificationInfo.java +212 −0
  7. java/pkg/java/awt/font/GlyphMetrics.class +0 −0
  8. java/pkg/java/awt/font/GlyphMetrics.java +324 −0
  9. java/pkg/java/awt/font/GlyphVector.class +0 −0
  10. java/pkg/java/awt/font/GlyphVector.java +611 −0
  11. java/pkg/java/awt/font/GraphicAttribute.class +0 −0
  12. java/pkg/java/awt/font/GraphicAttribute.java +221 −0
  13. java/pkg/java/awt/font/ImageGraphicAttribute.class +0 −0
  14. java/pkg/java/awt/font/ImageGraphicAttribute.java +227 −0
  15. java/pkg/java/awt/font/LayoutPath.class +0 −0
  16. java/pkg/java/awt/font/LayoutPath.java +84 −0
  17. java/pkg/java/awt/font/LineBreakMeasurer.class +0 −0
  18. java/pkg/java/awt/font/LineBreakMeasurer.java +533 −0
  19. java/pkg/java/awt/font/LineMetrics.class +0 −0
  20. java/pkg/java/awt/font/LineMetrics.java +143 −0
  21. java/pkg/java/awt/font/MultipleMaster.class +0 −0
  22. java/pkg/java/awt/font/MultipleMaster.java +106 −0
  23. java/pkg/java/awt/font/NumericShaper$1.class +0 −0
  24. java/pkg/java/awt/font/NumericShaper$Range$1.class +0 −0
  25. java/pkg/java/awt/font/NumericShaper$Range.class +0 −0
  26. java/pkg/java/awt/font/NumericShaper.class +0 −0
  27. java/pkg/java/awt/font/NumericShaper.java +1507 −0
  28. java/pkg/java/awt/font/OpenType.class +0 −0
  29. java/pkg/java/awt/font/OpenType.java +432 −0
  30. java/pkg/java/awt/font/ShapeGraphicAttribute.class +0 −0
  31. java/pkg/java/awt/font/ShapeGraphicAttribute.java +256 −0
  32. java/pkg/java/awt/font/StyledParagraph.class +0 −0
  33. java/pkg/java/awt/font/StyledParagraph.java +492 −0
  34. java/pkg/java/awt/font/TextAttribute.class +0 −0
  35. java/pkg/java/awt/font/TextAttribute.java +1117 −0
  36. java/pkg/java/awt/font/TextHitInfo.class +0 −0
  37. java/pkg/java/awt/font/TextHitInfo.java +247 −0
  38. java/pkg/java/awt/font/TextJustifier.class +0 −0
  39. java/pkg/java/awt/font/TextJustifier.java +240 −0
  40. java/pkg/java/awt/font/TextLayout$CaretPolicy.class +0 −0
  41. java/pkg/java/awt/font/TextLayout.class +0 −0
  42. java/pkg/java/awt/font/TextLayout.java +2753 −0
  43. java/pkg/java/awt/font/TextLine$1.class +0 −0
  44. java/pkg/java/awt/font/TextLine$2.class +0 −0
  45. java/pkg/java/awt/font/TextLine$3.class +0 −0
  46. java/pkg/java/awt/font/TextLine$4.class +0 −0
  47. java/pkg/java/awt/font/TextLine$Function.class +0 −0
  48. java/pkg/java/awt/font/TextLine$TextLineMetrics.class +0 −0
  49. java/pkg/java/awt/font/TextLine.class +0 −0
  50. java/pkg/java/awt/font/TextLine.java +1437 −0
  51. java/pkg/java/awt/font/TextMeasurer.class +0 −0
  52. java/pkg/java/awt/font/TextMeasurer.java +759 −0
  53. java/pkg/java/awt/font/TransformAttribute.class +0 −0
  54. java/pkg/java/awt/font/TransformAttribute.java +155 −0
  55. java/pkg/java/awt/font/mkfile +29 −0
  56. java/pkg/java/awt/font/package.html +52 −0
java/pkg/java/awt/font/CharArrayIterator.class+0−0

Binary file not shown.

java/pkg/java/awt/font/CharArrayIterator.java+193−0
@@ -0,0 +1,193 @@
1/*
2 * Copyright (c) 1999, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.awt.font;
27
28import java.text.CharacterIterator;
29
30class CharArrayIterator implements CharacterIterator {
31
32 private char[] chars;
33 private int pos;
34 private int begin;
35
36 CharArrayIterator(char[] chars) {
37
38 reset(chars, 0);
39 }
40
41 CharArrayIterator(char[] chars, int begin) {
42
43 reset(chars, begin);
44 }
45
46 /**
47 * Sets the position to getBeginIndex() and returns the character at that
48 * position.
49 * @return the first character in the text, or DONE if the text is empty
50 * @see getBeginIndex
51 */
52 public char first() {
53
54 pos = 0;
55 return current();
56 }
57
58 /**
59 * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
60 * and returns the character at that position.
61 * @return the last character in the text, or DONE if the text is empty
62 * @see getEndIndex
63 */
64 public char last() {
65
66 if (chars.length > 0) {
67 pos = chars.length-1;
68 }
69 else {
70 pos = 0;
71 }
72 return current();
73 }
74
75 /**
76 * Gets the character at the current position (as returned by getIndex()).
77 * @return the character at the current position or DONE if the current
78 * position is off the end of the text.
79 * @see getIndex
80 */
81 public char current() {
82
83 if (pos >= 0 && pos < chars.length) {
84 return chars[pos];
85 }
86 else {
87 return DONE;
88 }
89 }
90
91 /**
92 * Increments the iterator's index by one and returns the character
93 * at the new index. If the resulting index is greater or equal
94 * to getEndIndex(), the current index is reset to getEndIndex() and
95 * a value of DONE is returned.
96 * @return the character at the new position or DONE if the new
97 * position is off the end of the text range.
98 */
99 public char next() {
100
101 if (pos < chars.length-1) {
102 pos++;
103 return chars[pos];
104 }
105 else {
106 pos = chars.length;
107 return DONE;
108 }
109 }
110
111 /**
112 * Decrements the iterator's index by one and returns the character
113 * at the new index. If the current index is getBeginIndex(), the index
114 * remains at getBeginIndex() and a value of DONE is returned.
115 * @return the character at the new position or DONE if the current
116 * position is equal to getBeginIndex().
117 */
118 public char previous() {
119
120 if (pos > 0) {
121 pos--;
122 return chars[pos];
123 }
124 else {
125 pos = 0;
126 return DONE;
127 }
128 }
129
130 /**
131 * Sets the position to the specified position in the text and returns that
132 * character.
133 * @param position the position within the text. Valid values range from
134 * getBeginIndex() to getEndIndex(). An IllegalArgumentException is thrown
135 * if an invalid value is supplied.
136 * @return the character at the specified position or DONE if the specified position is equal to getEndIndex()
137 */
138 public char setIndex(int position) {
139
140 position -= begin;
141 if (position < 0 || position > chars.length) {
142 throw new IllegalArgumentException("Invalid index");
143 }
144 pos = position;
145 return current();
146 }
147
148 /**
149 * Returns the start index of the text.
150 * @return the index at which the text begins.
151 */
152 public int getBeginIndex() {
153 return begin;
154 }
155
156 /**
157 * Returns the end index of the text. This index is the index of the first
158 * character following the end of the text.
159 * @return the index after the last character in the text
160 */
161 public int getEndIndex() {
162 return begin+chars.length;
163 }
164
165 /**
166 * Returns the current index.
167 * @return the current index.
168 */
169 public int getIndex() {
170 return begin+pos;
171 }
172
173 /**
174 * Create a copy of this iterator
175 * @return A copy of this
176 */
177 public Object clone() {
178 CharArrayIterator c = new CharArrayIterator(chars, begin);
179 c.pos = this.pos;
180 return c;
181 }
182
183 void reset(char[] chars) {
184 reset(chars, 0);
185 }
186
187 void reset(char[] chars, int begin) {
188
189 this.chars = chars;
190 this.begin = begin;
191 pos = 0;
192 }
193}
java/pkg/java/awt/font/FontRenderContext.class+0−0

Binary file not shown.

java/pkg/java/awt/font/FontRenderContext.java+358−0
@@ -0,0 +1,358 @@
1/*
2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * @author Charlton Innovations, Inc.
28 */
29
30package java.awt.font;
31
32import java.awt.RenderingHints;
33import static java.awt.RenderingHints.*;
34import java.awt.geom.AffineTransform;
35
36/**
37* The <code>FontRenderContext</code> class is a container for the
38* information needed to correctly measure text. The measurement of text
39* can vary because of rules that map outlines to pixels, and rendering
40* hints provided by an application.
41* <p>
42* One such piece of information is a transform that scales
43* typographical points to pixels. (A point is defined to be exactly 1/72
44* of an inch, which is slightly different than
45* the traditional mechanical measurement of a point.) A character that
46* is rendered at 12pt on a 600dpi device might have a different size
47* than the same character rendered at 12pt on a 72dpi device because of
48* such factors as rounding to pixel boundaries and hints that the font
49* designer may have specified.
50* <p>
51* Anti-aliasing and Fractional-metrics specified by an application can also
52* affect the size of a character because of rounding to pixel
53* boundaries.
54* <p>
55* Typically, instances of <code>FontRenderContext</code> are
56* obtained from a {@link java.awt.Graphics2D Graphics2D} object. A
57* <code>FontRenderContext</code> which is directly constructed will
58* most likely not represent any actual graphics device, and may lead
59* to unexpected or incorrect results.
60* <p>
61* @see java.awt.RenderingHints#KEY_TEXT_ANTIALIASING
62* @see java.awt.RenderingHints#KEY_FRACTIONALMETRICS
63* @see java.awt.Graphics2D#getFontRenderContext()
64* @see java.awt.font.LineMetrics
65*/
66
67public class FontRenderContext {
68 private transient AffineTransform tx;
69 private transient Object aaHintValue;
70 private transient Object fmHintValue;
71 private transient boolean defaulting;
72
73 /**
74 * Constructs a new <code>FontRenderContext</code>
75 * object.
76 *
77 */
78 protected FontRenderContext() {
79 aaHintValue = VALUE_TEXT_ANTIALIAS_DEFAULT;
80 fmHintValue = VALUE_FRACTIONALMETRICS_DEFAULT;
81 defaulting = true;
82 }
83
84 /**
85 * Constructs a <code>FontRenderContext</code> object from an
86 * optional {@link AffineTransform} and two <code>boolean</code>
87 * values that determine if the newly constructed object has
88 * anti-aliasing or fractional metrics.
89 * In each case the boolean values <CODE>true</CODE> and <CODE>false</CODE>
90 * correspond to the rendering hint values <CODE>ON</CODE> and
91 * <CODE>OFF</CODE> respectively.
92 * <p>
93 * To specify other hint values, use the constructor which
94 * specifies the rendering hint values as parameters :
95 * {@link #FontRenderContext(AffineTransform, Object, Object)}.
96 * @param tx the transform which is used to scale typographical points
97 * to pixels in this <code>FontRenderContext</code>. If null, an
98 * identity transform is used.
99 * @param isAntiAliased determines if the newly constructed object
100 * has anti-aliasing.
101 * @param usesFractionalMetrics determines if the newly constructed
102 * object has fractional metrics.
103 */
104 public FontRenderContext(AffineTransform tx,
105 boolean isAntiAliased,
106 boolean usesFractionalMetrics) {
107 if (tx != null && !tx.isIdentity()) {
108 this.tx = new AffineTransform(tx);
109 }
110 if (isAntiAliased) {
111 aaHintValue = VALUE_TEXT_ANTIALIAS_ON;
112 } else {
113 aaHintValue = VALUE_TEXT_ANTIALIAS_OFF;
114 }
115 if (usesFractionalMetrics) {
116 fmHintValue = VALUE_FRACTIONALMETRICS_ON;
117 } else {
118 fmHintValue = VALUE_FRACTIONALMETRICS_OFF;
119 }
120 }
121
122 /**
123 * Constructs a <code>FontRenderContext</code> object from an
124 * optional {@link AffineTransform} and two <code>Object</code>
125 * values that determine if the newly constructed object has
126 * anti-aliasing or fractional metrics.
127 * @param tx the transform which is used to scale typographical points
128 * to pixels in this <code>FontRenderContext</code>. If null, an
129 * identity tranform is used.
130 * @param aaHint - one of the text antialiasing rendering hint values
131 * defined in {@link java.awt.RenderingHints java.awt.RenderingHints}.
132 * Any other value will throw <code>IllegalArgumentException</code>.
133 * {@link java.awt.RenderingHints#VALUE_TEXT_ANTIALIAS_DEFAULT VALUE_TEXT_ANTIALIAS_DEFAULT}
134 * may be specified, in which case the mode used is implementation
135 * dependent.
136 * @param fmHint - one of the text fractional rendering hint values defined
137 * in {@link java.awt.RenderingHints java.awt.RenderingHints}.
138 * {@link java.awt.RenderingHints#VALUE_FRACTIONALMETRICS_DEFAULT VALUE_FRACTIONALMETRICS_DEFAULT}
139 * may be specified, in which case the mode used is implementation
140 * dependent.
141 * Any other value will throw <code>IllegalArgumentException</code>
142 * @throws IllegalArgumentException if the hints are not one of the
143 * legal values.
144 * @since 1.6
145 */
146 public FontRenderContext(AffineTransform tx, Object aaHint, Object fmHint){
147 if (tx != null && !tx.isIdentity()) {
148 this.tx = new AffineTransform(tx);
149 }
150 try {
151 if (KEY_TEXT_ANTIALIASING.isCompatibleValue(aaHint)) {
152 aaHintValue = aaHint;
153 } else {
154 throw new IllegalArgumentException("AA hint:" + aaHint);
155 }
156 } catch (Exception e) {
157 throw new IllegalArgumentException("AA hint:" +aaHint);
158 }
159 try {
160 if (KEY_FRACTIONALMETRICS.isCompatibleValue(fmHint)) {
161 fmHintValue = fmHint;
162 } else {
163 throw new IllegalArgumentException("FM hint:" + fmHint);
164 }
165 } catch (Exception e) {
166 throw new IllegalArgumentException("FM hint:" +fmHint);
167 }
168 }
169
170 /**
171 * Indicates whether or not this <code>FontRenderContext</code> object
172 * measures text in a transformed render context.
173 * @return <code>true</code> if this <code>FontRenderContext</code>
174 * object has a non-identity AffineTransform attribute.
175 * <code>false</code> otherwise.
176 * @see java.awt.font.FontRenderContext#getTransform
177 * @since 1.6
178 */
179 public boolean isTransformed() {
180 if (!defaulting) {
181 return tx != null;
182 } else {
183 return !getTransform().isIdentity();
184 }
185 }
186
187 /**
188 * Returns the integer type of the affine transform for this
189 * <code>FontRenderContext</code> as specified by
190 * {@link java.awt.geom.AffineTransform#getType()}
191 * @return the type of the transform.
192 * @see AffineTransform
193 * @since 1.6
194 */
195 public int getTransformType() {
196 if (!defaulting) {
197 if (tx == null) {
198 return AffineTransform.TYPE_IDENTITY;
199 } else {
200 return tx.getType();
201 }
202 } else {
203 return getTransform().getType();
204 }
205 }
206
207 /**
208 * Gets the transform that is used to scale typographical points
209 * to pixels in this <code>FontRenderContext</code>.
210 * @return the <code>AffineTransform</code> of this
211 * <code>FontRenderContext</code>.
212 * @see AffineTransform
213 */
214 public AffineTransform getTransform() {
215 return (tx == null) ? new AffineTransform() : new AffineTransform(tx);
216 }
217
218 /**
219 * Returns a boolean which indicates whether or not some form of
220 * antialiasing is specified by this <code>FontRenderContext</code>.
221 * Call {@link #getAntiAliasingHint() getAntiAliasingHint()}
222 * for the specific rendering hint value.
223 * @return <code>true</code>, if text is anti-aliased in this
224 * <code>FontRenderContext</code>; <code>false</code> otherwise.
225 * @see java.awt.RenderingHints#KEY_TEXT_ANTIALIASING
226 * @see #FontRenderContext(AffineTransform,boolean,boolean)
227 * @see #FontRenderContext(AffineTransform,Object,Object)
228 */
229 public boolean isAntiAliased() {
230 return !(aaHintValue == VALUE_TEXT_ANTIALIAS_OFF ||
231 aaHintValue == VALUE_TEXT_ANTIALIAS_DEFAULT);
232 }
233
234 /**
235 * Returns a boolean which whether text fractional metrics mode
236 * is used in this <code>FontRenderContext</code>.
237 * Call {@link #getFractionalMetricsHint() getFractionalMetricsHint()}
238 * to obtain the corresponding rendering hint value.
239 * @return <code>true</code>, if layout should be performed with
240 * fractional metrics; <code>false</code> otherwise.
241 * in this <code>FontRenderContext</code>.
242 * @see java.awt.RenderingHints#KEY_FRACTIONALMETRICS
243 * @see #FontRenderContext(AffineTransform,boolean,boolean)
244 * @see #FontRenderContext(AffineTransform,Object,Object)
245 */
246 public boolean usesFractionalMetrics() {
247 return !(fmHintValue == VALUE_FRACTIONALMETRICS_OFF ||
248 fmHintValue == VALUE_FRACTIONALMETRICS_DEFAULT);
249 }
250
251 /**
252 * Return the text anti-aliasing rendering mode hint used in this
253 * <code>FontRenderContext</code>.
254 * This will be one of the text antialiasing rendering hint values
255 * defined in {@link java.awt.RenderingHints java.awt.RenderingHints}.
256 * @return text anti-aliasing rendering mode hint used in this
257 * <code>FontRenderContext</code>.
258 * @since 1.6
259 */
260 public Object getAntiAliasingHint() {
261 if (defaulting) {
262 if (isAntiAliased()) {
263 return VALUE_TEXT_ANTIALIAS_ON;
264 } else {
265 return VALUE_TEXT_ANTIALIAS_OFF;
266 }
267 }
268 return aaHintValue;
269 }
270
271 /**
272 * Return the text fractional metrics rendering mode hint used in this
273 * <code>FontRenderContext</code>.
274 * This will be one of the text fractional metrics rendering hint values
275 * defined in {@link java.awt.RenderingHints java.awt.RenderingHints}.
276 * @return the text fractional metrics rendering mode hint used in this
277 * <code>FontRenderContext</code>.
278 * @since 1.6
279 */
280 public Object getFractionalMetricsHint() {
281 if (defaulting) {
282 if (usesFractionalMetrics()) {
283 return VALUE_FRACTIONALMETRICS_ON;
284 } else {
285 return VALUE_FRACTIONALMETRICS_OFF;
286 }
287 }
288 return fmHintValue;
289 }
290
291 /**
292 * Return true if obj is an instance of FontRenderContext and has the same
293 * transform, antialiasing, and fractional metrics values as this.
294 * @param obj the object to test for equality
295 * @return <code>true</code> if the specified object is equal to
296 * this <code>FontRenderContext</code>; <code>false</code>
297 * otherwise.
298 */
299 public boolean equals(Object obj) {
300 try {
301 return equals((FontRenderContext)obj);
302 }
303 catch (ClassCastException e) {
304 return false;
305 }
306 }
307
308 /**
309 * Return true if rhs has the same transform, antialiasing,
310 * and fractional metrics values as this.
311 * @param rhs the <code>FontRenderContext</code> to test for equality
312 * @return <code>true</code> if <code>rhs</code> is equal to
313 * this <code>FontRenderContext</code>; <code>false</code>
314 * otherwise.
315 * @since 1.4
316 */
317 public boolean equals(FontRenderContext rhs) {
318 if (this == rhs) {
319 return true;
320 }
321 if (rhs == null) {
322 return false;
323 }
324
325 /* if neither instance is a subclass, reference values directly. */
326 if (!rhs.defaulting && !defaulting) {
327 if (rhs.aaHintValue == aaHintValue &&
328 rhs.fmHintValue == fmHintValue) {
329
330 return tx == null ? rhs.tx == null : tx.equals(rhs.tx);
331 }
332 return false;
333 } else {
334 return
335 rhs.getAntiAliasingHint() == getAntiAliasingHint() &&
336 rhs.getFractionalMetricsHint() == getFractionalMetricsHint() &&
337 rhs.getTransform().equals(getTransform());
338 }
339 }
340
341 /**
342 * Return a hashcode for this FontRenderContext.
343 */
344 public int hashCode() {
345 int hash = tx == null ? 0 : tx.hashCode();
346 /* SunHints value objects have identity hashcode, so we can rely on
347 * this to ensure that two equal FRC's have the same hashcode.
348 */
349 if (defaulting) {
350 hash += getAntiAliasingHint().hashCode();
351 hash += getFractionalMetricsHint().hashCode();
352 } else {
353 hash += aaHintValue.hashCode();
354 hash += fmHintValue.hashCode();
355 }
356 return hash;
357 }
358}
java/pkg/java/awt/font/GlyphJustificationInfo.class+0−0

Binary file not shown.

java/pkg/java/awt/font/GlyphJustificationInfo.java+212−0
@@ -0,0 +1,212 @@
1/*
2 * Copyright (c) 1997, 1999, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 */
39
40package java.awt.font;
41
42/**
43 * The <code>GlyphJustificationInfo</code> class represents information
44 * about the justification properties of a glyph. A glyph is the visual
45 * representation of one or more characters. Many different glyphs can
46 * be used to represent a single character or combination of characters.
47 * The four justification properties represented by
48 * <code>GlyphJustificationInfo</code> are weight, priority, absorb and
49 * limit.
50 * <p>
51 * Weight is the overall 'weight' of the glyph in the line. Generally it is
52 * proportional to the size of the font. Glyphs with larger weight are
53 * allocated a correspondingly larger amount of the change in space.
54 * <p>
55 * Priority determines the justification phase in which this glyph is used.
56 * All glyphs of the same priority are examined before glyphs of the next
57 * priority. If all the change in space can be allocated to these glyphs
58 * without exceeding their limits, then glyphs of the next priority are not
59 * examined. There are four priorities, kashida, whitespace, interchar,
60 * and none. KASHIDA is the first priority examined. NONE is the last
61 * priority examined.
62 * <p>
63 * Absorb determines whether a glyph absorbs all change in space. Within a
64 * given priority, some glyphs may absorb all the change in space. If any of
65 * these glyphs are present, no glyphs of later priority are examined.
66 * <p>
67 * Limit determines the maximum or minimum amount by which the glyph can
68 * change. Left and right sides of the glyph can have different limits.
69 * <p>
70 * Each <code>GlyphJustificationInfo</code> represents two sets of
71 * metrics, which are <i>growing</i> and <i>shrinking</i>. Growing
72 * metrics are used when the glyphs on a line are to be
73 * spread apart to fit a larger width. Shrinking metrics are used when
74 * the glyphs are to be moved together to fit a smaller width.
75 */
76
77public final class GlyphJustificationInfo {
78
79 /**
80 * Constructs information about the justification properties of a
81 * glyph.
82 * @param weight the weight of this glyph when allocating space. Must be non-negative.
83 * @param growAbsorb if <code>true</code> this glyph absorbs
84 * all extra space at this priority and lower priority levels when it
85 * grows
86 * @param growPriority the priority level of this glyph when it
87 * grows
88 * @param growLeftLimit the maximum amount by which the left side of this
89 * glyph can grow. Must be non-negative.
90 * @param growRightLimit the maximum amount by which the right side of this
91 * glyph can grow. Must be non-negative.
92 * @param shrinkAbsorb if <code>true</code>, this glyph absorbs all
93 * remaining shrinkage at this and lower priority levels when it
94 * shrinks
95 * @param shrinkPriority the priority level of this glyph when
96 * it shrinks
97 * @param shrinkLeftLimit the maximum amount by which the left side of this
98 * glyph can shrink. Must be non-negative.
99 * @param shrinkRightLimit the maximum amount by which the right side
100 * of this glyph can shrink. Must be non-negative.
101 */
102 public GlyphJustificationInfo(float weight,
103 boolean growAbsorb,
104 int growPriority,
105 float growLeftLimit,
106 float growRightLimit,
107 boolean shrinkAbsorb,
108 int shrinkPriority,
109 float shrinkLeftLimit,
110 float shrinkRightLimit)
111 {
112 if (weight < 0) {
113 throw new IllegalArgumentException("weight is negative");
114 }
115
116 if (!priorityIsValid(growPriority)) {
117 throw new IllegalArgumentException("Invalid grow priority");
118 }
119 if (growLeftLimit < 0) {
120 throw new IllegalArgumentException("growLeftLimit is negative");
121 }
122 if (growRightLimit < 0) {
123 throw new IllegalArgumentException("growRightLimit is negative");
124 }
125
126 if (!priorityIsValid(shrinkPriority)) {
127 throw new IllegalArgumentException("Invalid shrink priority");
128 }
129 if (shrinkLeftLimit < 0) {
130 throw new IllegalArgumentException("shrinkLeftLimit is negative");
131 }
132 if (shrinkRightLimit < 0) {
133 throw new IllegalArgumentException("shrinkRightLimit is negative");
134 }
135
136 this.weight = weight;
137 this.growAbsorb = growAbsorb;
138 this.growPriority = growPriority;
139 this.growLeftLimit = growLeftLimit;
140 this.growRightLimit = growRightLimit;
141 this.shrinkAbsorb = shrinkAbsorb;
142 this.shrinkPriority = shrinkPriority;
143 this.shrinkLeftLimit = shrinkLeftLimit;
144 this.shrinkRightLimit = shrinkRightLimit;
145 }
146
147 private static boolean priorityIsValid(int priority) {
148
149 return priority >= PRIORITY_KASHIDA && priority <= PRIORITY_NONE;
150 }
151
152 /** The highest justification priority. */
153 public static final int PRIORITY_KASHIDA = 0;
154
155 /** The second highest justification priority. */
156 public static final int PRIORITY_WHITESPACE = 1;
157
158 /** The second lowest justification priority. */
159 public static final int PRIORITY_INTERCHAR = 2;
160
161 /** The lowest justification priority. */
162 public static final int PRIORITY_NONE = 3;
163
164 /**
165 * The weight of this glyph.
166 */
167 public final float weight;
168
169 /**
170 * The priority level of this glyph as it is growing.
171 */
172 public final int growPriority;
173
174 /**
175 * If <code>true</code>, this glyph absorbs all extra
176 * space at this and lower priority levels when it grows.
177 */
178 public final boolean growAbsorb;
179
180 /**
181 * The maximum amount by which the left side of this glyph can grow.
182 */
183 public final float growLeftLimit;
184
185 /**
186 * The maximum amount by which the right side of this glyph can grow.
187 */
188 public final float growRightLimit;
189
190 /**
191 * The priority level of this glyph as it is shrinking.
192 */
193 public final int shrinkPriority;
194
195 /**
196 * If <code>true</code>,this glyph absorbs all remaining shrinkage at
197 * this and lower priority levels as it shrinks.
198 */
199 public final boolean shrinkAbsorb;
200
201 /**
202 * The maximum amount by which the left side of this glyph can shrink
203 * (a positive number).
204 */
205 public final float shrinkLeftLimit;
206
207 /**
208 * The maximum amount by which the right side of this glyph can shrink
209 * (a positive number).
210 */
211 public final float shrinkRightLimit;
212}
java/pkg/java/awt/font/GlyphMetrics.class+0−0

Binary file not shown.

java/pkg/java/awt/font/GlyphMetrics.java+324−0
@@ -0,0 +1,324 @@
1/*
2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.awt.geom.Rectangle2D;
44
45/**
46 * The <code>GlyphMetrics</code> class represents infomation for a
47 * single glyph. A glyph is the visual representation of one or more
48 * characters. Many different glyphs can be used to represent a single
49 * character or combination of characters. <code>GlyphMetrics</code>
50 * instances are produced by {@link java.awt.Font Font} and are applicable
51 * to a specific glyph in a particular <code>Font</code>.
52 * <p>
53 * Glyphs are either STANDARD, LIGATURE, COMBINING, or COMPONENT.
54 * <ul>
55 * <li>STANDARD glyphs are commonly used to represent single characters.
56 * <li>LIGATURE glyphs are used to represent sequences of characters.
57 * <li>COMPONENT glyphs in a {@link GlyphVector} do not correspond to a
58 * particular character in a text model. Instead, COMPONENT glyphs are
59 * added for typographical reasons, such as Arabic justification.
60 * <li>COMBINING glyphs embellish STANDARD or LIGATURE glyphs, such
61 * as accent marks. Carets do not appear before COMBINING glyphs.
62 * </ul>
63 * <p>
64 * Other metrics available through <code>GlyphMetrics</code> are the
65 * components of the advance, the visual bounds, and the left and right
66 * side bearings.
67 * <p>
68 * Glyphs for a rotated font, or obtained from a <code>GlyphVector</code>
69 * which has applied a rotation to the glyph, can have advances that
70 * contain both X and Y components. Usually the advance only has one
71 * component.
72 * <p>
73 * The advance of a glyph is the distance from the glyph's origin to the
74 * origin of the next glyph along the baseline, which is either vertical
75 * or horizontal. Note that, in a <code>GlyphVector</code>,
76 * the distance from a glyph to its following glyph might not be the
77 * glyph's advance, because of kerning or other positioning adjustments.
78 * <p>
79 * The bounds is the smallest rectangle that completely contains the
80 * outline of the glyph. The bounds rectangle is relative to the
81 * glyph's origin. The left-side bearing is the distance from the glyph
82 * origin to the left of its bounds rectangle. If the left-side bearing is
83 * negative, part of the glyph is drawn to the left of its origin. The
84 * right-side bearing is the distance from the right side of the bounds
85 * rectangle to the next glyph origin (the origin plus the advance). If
86 * negative, part of the glyph is drawn to the right of the next glyph's
87 * origin. Note that the bounds does not necessarily enclose all the pixels
88 * affected when rendering the glyph, because of rasterization and pixel
89 * adjustment effects.
90 * <p>
91 * Although instances of <code>GlyphMetrics</code> can be directly
92 * constructed, they are almost always obtained from a
93 * <code>GlyphVector</code>. Once constructed, <code>GlyphMetrics</code>
94 * objects are immutable.
95 * <p>
96 * <strong>Example</strong>:<p>
97 * Querying a <code>Font</code> for glyph information
98 * <blockquote><pre>
99 * Font font = ...;
100 * int glyphIndex = ...;
101 * GlyphMetrics metrics = GlyphVector.getGlyphMetrics(glyphIndex);
102 * int isStandard = metrics.isStandard();
103 * float glyphAdvance = metrics.getAdvance();
104 * </pre></blockquote>
105 * @see java.awt.Font
106 * @see GlyphVector
107 */
108
109public final class GlyphMetrics {
110 /**
111 * Indicates whether the metrics are for a horizontal or vertical baseline.
112 */
113 private boolean horizontal;
114
115 /**
116 * The x-component of the advance.
117 */
118 private float advanceX;
119
120 /**
121 * The y-component of the advance.
122 */
123 private float advanceY;
124
125 /**
126 * The bounds of the associated glyph.
127 */
128 private Rectangle2D.Float bounds;
129
130 /**
131 * Additional information about the glyph encoded as a byte.
132 */
133 private byte glyphType;
134
135 /**
136 * Indicates a glyph that represents a single standard
137 * character.
138 */
139 public static final byte STANDARD = 0;
140
141 /**
142 * Indicates a glyph that represents multiple characters
143 * as a ligature, for example 'fi' or 'ffi'. It is followed by
144 * filler glyphs for the remaining characters. Filler and combining
145 * glyphs can be intermixed to control positioning of accent marks
146 * on the logically preceeding ligature.
147 */
148 public static final byte LIGATURE = 1;
149
150 /**
151 * Indicates a glyph that represents a combining character,
152 * such as an umlaut. There is no caret position between this glyph
153 * and the preceeding glyph.
154 */
155 public static final byte COMBINING = 2;
156
157 /**
158 * Indicates a glyph with no corresponding character in the
159 * backing store. The glyph is associated with the character
160 * represented by the logicaly preceeding non-component glyph. This
161 * is used for kashida justification or other visual modifications to
162 * existing glyphs. There is no caret position between this glyph
163 * and the preceeding glyph.
164 */
165 public static final byte COMPONENT = 3;
166
167 /**
168 * Indicates a glyph with no visual representation. It can
169 * be added to the other code values to indicate an invisible glyph.
170 */
171 public static final byte WHITESPACE = 4;
172
173 /**
174 * Constructs a <code>GlyphMetrics</code> object.
175 * @param advance the advance width of the glyph
176 * @param bounds the black box bounds of the glyph
177 * @param glyphType the type of the glyph
178 */
179 public GlyphMetrics(float advance, Rectangle2D bounds, byte glyphType) {
180 this.horizontal = true;
181 this.advanceX = advance;
182 this.advanceY = 0;
183 this.bounds = new Rectangle2D.Float();
184 this.bounds.setRect(bounds);
185 this.glyphType = glyphType;
186 }
187
188 /**
189 * Constructs a <code>GlyphMetrics</code> object.
190 * @param horizontal if true, metrics are for a horizontal baseline,
191 * otherwise they are for a vertical baseline
192 * @param advanceX the X-component of the glyph's advance
193 * @param advanceY the Y-component of the glyph's advance
194 * @param bounds the visual bounds of the glyph
195 * @param glyphType the type of the glyph
196 * @since 1.4
197 */
198 public GlyphMetrics(boolean horizontal, float advanceX, float advanceY,
199 Rectangle2D bounds, byte glyphType) {
200
201 this.horizontal = horizontal;
202 this.advanceX = advanceX;
203 this.advanceY = advanceY;
204 this.bounds = new Rectangle2D.Float();
205 this.bounds.setRect(bounds);
206 this.glyphType = glyphType;
207 }
208
209 /**
210 * Returns the advance of the glyph along the baseline (either
211 * horizontal or vertical).
212 * @return the advance of the glyph
213 */
214 public float getAdvance() {
215 return horizontal ? advanceX : advanceY;
216 }
217
218 /**
219 * Returns the x-component of the advance of the glyph.
220 * @return the x-component of the advance of the glyph
221 * @since 1.4
222 */
223 public float getAdvanceX() {
224 return advanceX;
225 }
226
227 /**
228 * Returns the y-component of the advance of the glyph.
229 * @return the y-component of the advance of the glyph
230 * @since 1.4
231 */
232 public float getAdvanceY() {
233 return advanceY;
234 }
235
236 /**
237 * Returns the bounds of the glyph. This is the bounding box of the glyph outline.
238 * Because of rasterization and pixel alignment effects, it does not necessarily
239 * enclose the pixels that are affected when rendering the glyph.
240 * @return a {@link Rectangle2D} that is the bounds of the glyph.
241 */
242 public Rectangle2D getBounds2D() {
243 return new Rectangle2D.Float(bounds.x, bounds.y, bounds.width, bounds.height);
244 }
245
246 /**
247 * Returns the left (top) side bearing of the glyph.
248 * <p>
249 * This is the distance from 0,&nbsp;0 to the left (top) of the glyph
250 * bounds. If the bounds of the glyph is to the left of (above) the
251 * origin, the LSB is negative.
252 * @return the left side bearing of the glyph.
253 */
254 public float getLSB() {
255 return horizontal ? bounds.x : bounds.y;
256 }
257
258 /**
259 * Returns the right (bottom) side bearing of the glyph.
260 * <p>
261 * This is the distance from the right (bottom) of the glyph bounds to
262 * the advance. If the bounds of the glyph is to the right of (below)
263 * the advance, the RSB is negative.
264 * @return the right side bearing of the glyph.
265 */
266 public float getRSB() {
267 return horizontal ?
268 advanceX - bounds.x - bounds.width :
269 advanceY - bounds.y - bounds.height;
270 }
271
272 /**
273 * Returns the raw glyph type code.
274 * @return the raw glyph type code.
275 */
276 public int getType() {
277 return glyphType;
278 }
279
280 /**
281 * Returns <code>true</code> if this is a standard glyph.
282 * @return <code>true</code> if this is a standard glyph;
283 * <code>false</code> otherwise.
284 */
285 public boolean isStandard() {
286 return (glyphType & 0x3) == STANDARD;
287 }
288
289 /**
290 * Returns <code>true</code> if this is a ligature glyph.
291 * @return <code>true</code> if this is a ligature glyph;
292 * <code>false</code> otherwise.
293 */
294 public boolean isLigature() {
295 return (glyphType & 0x3) == LIGATURE;
296 }
297
298 /**
299 * Returns <code>true</code> if this is a combining glyph.
300 * @return <code>true</code> if this is a combining glyph;
301 * <code>false</code> otherwise.
302 */
303 public boolean isCombining() {
304 return (glyphType & 0x3) == COMBINING;
305 }
306
307 /**
308 * Returns <code>true</code> if this is a component glyph.
309 * @return <code>true</code> if this is a component glyph;
310 * <code>false</code> otherwise.
311 */
312 public boolean isComponent() {
313 return (glyphType & 0x3) == COMPONENT;
314 }
315
316 /**
317 * Returns <code>true</code> if this is a whitespace glyph.
318 * @return <code>true</code> if this is a whitespace glyph;
319 * <code>false</code> otherwise.
320 */
321 public boolean isWhitespace() {
322 return (glyphType & 0x4) == WHITESPACE;
323 }
324}
java/pkg/java/awt/font/GlyphVector.class+0−0

Binary file not shown.

java/pkg/java/awt/font/GlyphVector.java+611−0
@@ -0,0 +1,611 @@
1/*
2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * @author Charlton Innovations, Inc.
28 */
29
30package java.awt.font;
31
32import java.awt.Graphics2D;
33import java.awt.Font;
34import java.awt.Polygon; // remind - need a floating point version
35import java.awt.Rectangle;
36import java.awt.geom.Point2D;
37import java.awt.geom.Rectangle2D;
38import java.awt.geom.AffineTransform;
39import java.awt.Shape;
40import java.awt.font.GlyphMetrics;
41import java.awt.font.GlyphJustificationInfo;
42
43/**
44 * A <code>GlyphVector</code> object is a collection of glyphs
45 * containing geometric information for the placement of each glyph
46 * in a transformed coordinate space which corresponds to the
47 * device on which the <code>GlyphVector</code> is ultimately
48 * displayed.
49 * <p>
50 * The <code>GlyphVector</code> does not attempt any interpretation of
51 * the sequence of glyphs it contains. Relationships between adjacent
52 * glyphs in sequence are solely used to determine the placement of
53 * the glyphs in the visual coordinate space.
54 * <p>
55 * Instances of <code>GlyphVector</code> are created by a {@link Font}.
56 * <p>
57 * In a text processing application that can cache intermediate
58 * representations of text, creation and subsequent caching of a
59 * <code>GlyphVector</code> for use during rendering is the fastest
60 * method to present the visual representation of characters to a user.
61 * <p>
62 * A <code>GlyphVector</code> is associated with exactly one
63 * <code>Font</code>, and can provide data useful only in relation to
64 * this <code>Font</code>. In addition, metrics obtained from a
65 * <code>GlyphVector</code> are not generally geometrically scaleable
66 * since the pixelization and spacing are dependent on grid-fitting
67 * algorithms within a <code>Font</code>. To facilitate accurate
68 * measurement of a <code>GlyphVector</code> and its component
69 * glyphs, you must specify a scaling transform, anti-alias mode, and
70 * fractional metrics mode when creating the <code>GlyphVector</code>.
71 * These characteristics can be derived from the destination device.
72 * <p>
73 * For each glyph in the <code>GlyphVector</code>, you can obtain:
74 * <ul>
75 * <li>the position of the glyph
76 * <li>the transform associated with the glyph
77 * <li>the metrics of the glyph in the context of the
78 * <code>GlyphVector</code>. The metrics of the glyph may be
79 * different under different transforms, application specified
80 * rendering hints, and the specific instance of the glyph within
81 * the <code>GlyphVector</code>.
82 * </ul>
83 * <p>
84 * Altering the data used to create the <code>GlyphVector</code> does not
85 * alter the state of the <code>GlyphVector</code>.
86 * <p>
87 * Methods are provided to adjust the positions of the glyphs
88 * within the <code>GlyphVector</code>. These methods are most
89 * appropriate for applications that are performing justification
90 * operations for the presentation of the glyphs.
91 * <p>
92 * Methods are provided to transform individual glyphs within the
93 * <code>GlyphVector</code>. These methods are primarily useful for
94 * special effects.
95 * <p>
96 * Methods are provided to return both the visual, logical, and pixel bounds
97 * of the entire <code>GlyphVector</code> or of individual glyphs within
98 * the <code>GlyphVector</code>.
99 * <p>
100 * Methods are provided to return a {@link Shape} for the
101 * <code>GlyphVector</code>, and for individual glyphs within the
102 * <code>GlyphVector</code>.
103 * @see Font
104 * @see GlyphMetrics
105 * @see TextLayout
106 * @author Charlton Innovations, Inc.
107 */
108
109public abstract class GlyphVector implements Cloneable {
110
111 //
112 // methods associated with creation-time state
113 //
114
115 /**
116 * Returns the <code>Font</code> associated with this
117 * <code>GlyphVector</code>.
118 * @return <code>Font</code> used to create this
119 * <code>GlyphVector</code>.
120 * @see Font
121 */
122 public abstract Font getFont();
123
124 /**
125 * Returns the {@link FontRenderContext} associated with this
126 * <code>GlyphVector</code>.
127 * @return <code>FontRenderContext</code> used to create this
128 * <code>GlyphVector</code>.
129 * @see FontRenderContext
130 * @see Font
131 */
132 public abstract FontRenderContext getFontRenderContext();
133
134 //
135 // methods associated with the GlyphVector as a whole
136 //
137
138 /**
139 * Assigns default positions to each glyph in this
140 * <code>GlyphVector</code>. This can destroy information
141 * generated during initial layout of this <code>GlyphVector</code>.
142 */
143 public abstract void performDefaultLayout();
144
145 /**
146 * Returns the number of glyphs in this <code>GlyphVector</code>.
147 * @return number of glyphs in this <code>GlyphVector</code>.
148 */
149 public abstract int getNumGlyphs();
150
151 /**
152 * Returns the glyphcode of the specified glyph.
153 * This return value is meaningless to anything other
154 * than the <code>Font</code> object that created this
155 * <code>GlyphVector</code>.
156 * @param glyphIndex the index into this <code>GlyphVector</code>
157 * that corresponds to the glyph from which to retrieve the
158 * glyphcode.
159 * @return the glyphcode of the glyph at the specified
160 * <code>glyphIndex</code>.
161 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
162 * is less than 0 or greater than or equal to the
163 * number of glyphs in this <code>GlyphVector</code>
164 */
165 public abstract int getGlyphCode(int glyphIndex);
166
167 /**
168 * Returns an array of glyphcodes for the specified glyphs.
169 * The contents of this return value are meaningless to anything other
170 * than the <code>Font</code> used to create this
171 * <code>GlyphVector</code>. This method is used
172 * for convenience and performance when processing glyphcodes.
173 * If no array is passed in, a new array is created.
174 * @param beginGlyphIndex the index into this
175 * <code>GlyphVector</code> at which to start retrieving glyphcodes
176 * @param numEntries the number of glyphcodes to retrieve
177 * @param codeReturn the array that receives the glyphcodes and is
178 * then returned
179 * @return an array of glyphcodes for the specified glyphs.
180 * @throws IllegalArgumentException if <code>numEntries</code> is
181 * less than 0
182 * @throws IndexOutOfBoundsException if <code>beginGlyphIndex</code>
183 * is less than 0
184 * @throws IndexOutOfBoundsException if the sum of
185 * <code>beginGlyphIndex</code> and <code>numEntries</code> is
186 * greater than the number of glyphs in this
187 * <code>GlyphVector</code>
188 */
189 public abstract int[] getGlyphCodes(int beginGlyphIndex, int numEntries,
190 int[] codeReturn);
191
192 /**
193 * Returns the character index of the specified glyph.
194 * The character index is the index of the first logical
195 * character represented by the glyph. The default
196 * implementation assumes a one-to-one, left-to-right mapping
197 * of glyphs to characters.
198 * @param glyphIndex the index of the glyph
199 * @return the index of the first character represented by the glyph
200 * @since 1.4
201 */
202 public int getGlyphCharIndex(int glyphIndex) {
203 return glyphIndex;
204 }
205
206 /**
207 * Returns the character indices of the specified glyphs.
208 * The character index is the index of the first logical
209 * character represented by the glyph. Indices are returned
210 * in glyph order. The default implementation invokes
211 * getGlyphCharIndex for each glyph, and subclassers will probably
212 * want to override this implementation for performance reasons.
213 * Use this method for convenience and performance
214 * in processing of glyphcodes. If no array is passed in,
215 * a new array is created.
216 * @param beginGlyphIndex the index of the first glyph
217 * @param numEntries the number of glyph indices
218 * @param codeReturn the array into which to return the character indices
219 * @return an array of character indices, one per glyph.
220 * @since 1.4
221 */
222 public int[] getGlyphCharIndices(int beginGlyphIndex, int numEntries,
223 int[] codeReturn) {
224 if (codeReturn == null) {
225 codeReturn = new int[numEntries];
226 }
227 for (int i = 0, j = beginGlyphIndex; i < numEntries; ++i, ++j) {
228 codeReturn[i] = getGlyphCharIndex(j);
229 }
230 return codeReturn;
231 }
232
233 /**
234 * Returns the logical bounds of this <code>GlyphVector</code>.
235 * This method is used when positioning this <code>GlyphVector</code>
236 * in relation to visually adjacent <code>GlyphVector</code> objects.
237 * @return a {@link Rectangle2D} that is the logical bounds of this
238 * <code>GlyphVector</code>.
239 */
240 public abstract Rectangle2D getLogicalBounds();
241
242 /**
243 * Returns the visual bounds of this <code>GlyphVector</code>
244 * The visual bounds is the bounding box of the outline of this
245 * <code>GlyphVector</code>. Because of rasterization and
246 * alignment of pixels, it is possible that this box does not
247 * enclose all pixels affected by rendering this <code>GlyphVector</code>.
248 * @return a <code>Rectangle2D</code> that is the bounding box
249 * of this <code>GlyphVector</code>.
250 */
251 public abstract Rectangle2D getVisualBounds();
252
253 /**
254 * Returns the pixel bounds of this <code>GlyphVector</code> when
255 * rendered in a graphics with the given
256 * <code>FontRenderContext</code> at the given location. The
257 * renderFRC need not be the same as the
258 * <code>FontRenderContext</code> of this
259 * <code>GlyphVector</code>, and can be null. If it is null, the
260 * <code>FontRenderContext</code> of this <code>GlyphVector</code>
261 * is used. The default implementation returns the visual bounds,
262 * offset to x, y and rounded out to the next integer value (i.e. returns an
263 * integer rectangle which encloses the visual bounds) and
264 * ignores the FRC. Subclassers should override this method.
265 * @param renderFRC the <code>FontRenderContext</code> of the <code>Graphics</code>.
266 * @param x the x-coordinate at which to render this <code>GlyphVector</code>.
267 * @param y the y-coordinate at which to render this <code>GlyphVector</code>.
268 * @return a <code>Rectangle</code> bounding the pixels that would be affected.
269 * @since 1.4
270 */
271 public Rectangle getPixelBounds(FontRenderContext renderFRC, float x, float y) {
272 Rectangle2D rect = getVisualBounds();
273 int l = (int)Math.floor(rect.getX() + x);
274 int t = (int)Math.floor(rect.getY() + y);
275 int r = (int)Math.ceil(rect.getMaxX() + x);
276 int b = (int)Math.ceil(rect.getMaxY() + y);
277 return new Rectangle(l, t, r - l, b - t);
278 }
279
280
281 /**
282 * Returns a <code>Shape</code> whose interior corresponds to the
283 * visual representation of this <code>GlyphVector</code>.
284 * @return a <code>Shape</code> that is the outline of this
285 * <code>GlyphVector</code>.
286 */
287 public abstract Shape getOutline();
288
289 /**
290 * Returns a <code>Shape</code> whose interior corresponds to the
291 * visual representation of this <code>GlyphVector</code> when
292 * rendered at x,&nbsp;y.
293 * @param x the X coordinate of this <code>GlyphVector</code>.
294 * @param y the Y coordinate of this <code>GlyphVector</code>.
295 * @return a <code>Shape</code> that is the outline of this
296 * <code>GlyphVector</code> when rendered at the specified
297 * coordinates.
298 */
299 public abstract Shape getOutline(float x, float y);
300
301 /**
302 * Returns a <code>Shape</code> whose interior corresponds to the
303 * visual representation of the specified glyph
304 * within this <code>GlyphVector</code>.
305 * The outline returned by this method is positioned around the
306 * origin of each individual glyph.
307 * @param glyphIndex the index into this <code>GlyphVector</code>
308 * @return a <code>Shape</code> that is the outline of the glyph
309 * at the specified <code>glyphIndex</code> of this
310 * <code>GlyphVector</code>.
311 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
312 * is less than 0 or greater than or equal to the number
313 * of glyphs in this <code>GlyphVector</code>
314 */
315 public abstract Shape getGlyphOutline(int glyphIndex);
316
317 /**
318 * Returns a <code>Shape</code> whose interior corresponds to the
319 * visual representation of the specified glyph
320 * within this <code>GlyphVector</code>, offset to x,&nbsp;y.
321 * The outline returned by this method is positioned around the
322 * origin of each individual glyph.
323 * @param glyphIndex the index into this <code>GlyphVector</code>
324 * @param x the X coordinate of the location of this {@code GlyphVector}
325 * @param y the Y coordinate of the location of this {@code GlyphVector}
326 * @return a <code>Shape</code> that is the outline of the glyph
327 * at the specified <code>glyphIndex</code> of this
328 * <code>GlyphVector</code> when rendered at the specified
329 * coordinates.
330 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
331 * is less than 0 or greater than or equal to the number
332 * of glyphs in this <code>GlyphVector</code>
333 * @since 1.4
334 */
335 public Shape getGlyphOutline(int glyphIndex, float x, float y) {
336 Shape s = getGlyphOutline(glyphIndex);
337 AffineTransform at = AffineTransform.getTranslateInstance(x,y);
338 return at.createTransformedShape(s);
339 }
340
341 /**
342 * Returns the position of the specified glyph relative to the
343 * origin of this <code>GlyphVector</code>.
344 * If <code>glyphIndex</code> equals the number of of glyphs in
345 * this <code>GlyphVector</code>, this method returns the position after
346 * the last glyph. This position is used to define the advance of
347 * the entire <code>GlyphVector</code>.
348 * @param glyphIndex the index into this <code>GlyphVector</code>
349 * @return a {@link Point2D} object that is the position of the glyph
350 * at the specified <code>glyphIndex</code>.
351 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
352 * is less than 0 or greater than the number of glyphs
353 * in this <code>GlyphVector</code>
354 * @see #setGlyphPosition
355 */
356 public abstract Point2D getGlyphPosition(int glyphIndex);
357
358 /**
359 * Sets the position of the specified glyph within this
360 * <code>GlyphVector</code>.
361 * If <code>glyphIndex</code> equals the number of of glyphs in
362 * this <code>GlyphVector</code>, this method sets the position after
363 * the last glyph. This position is used to define the advance of
364 * the entire <code>GlyphVector</code>.
365 * @param glyphIndex the index into this <code>GlyphVector</code>
366 * @param newPos the <code>Point2D</code> at which to position the
367 * glyph at the specified <code>glyphIndex</code>
368 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
369 * is less than 0 or greater than the number of glyphs
370 * in this <code>GlyphVector</code>
371 * @see #getGlyphPosition
372 */
373 public abstract void setGlyphPosition(int glyphIndex, Point2D newPos);
374
375 /**
376 * Returns the transform of the specified glyph within this
377 * <code>GlyphVector</code>. The transform is relative to the
378 * glyph position. If no special transform has been applied,
379 * <code>null</code> can be returned. A null return indicates
380 * an identity transform.
381 * @param glyphIndex the index into this <code>GlyphVector</code>
382 * @return an {@link AffineTransform} that is the transform of
383 * the glyph at the specified <code>glyphIndex</code>.
384 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
385 * is less than 0 or greater than or equal to the number
386 * of glyphs in this <code>GlyphVector</code>
387 * @see #setGlyphTransform
388 */
389 public abstract AffineTransform getGlyphTransform(int glyphIndex);
390
391 /**
392 * Sets the transform of the specified glyph within this
393 * <code>GlyphVector</code>. The transform is relative to the glyph
394 * position. A <code>null</code> argument for <code>newTX</code>
395 * indicates that no special transform is applied for the specified
396 * glyph.
397 * This method can be used to rotate, mirror, translate and scale the
398 * glyph. Adding a transform can result in signifant performance changes.
399 * @param glyphIndex the index into this <code>GlyphVector</code>
400 * @param newTX the new transform of the glyph at <code>glyphIndex</code>
401 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
402 * is less than 0 or greater than or equal to the number
403 * of glyphs in this <code>GlyphVector</code>
404 * @see #getGlyphTransform
405 */
406 public abstract void setGlyphTransform(int glyphIndex, AffineTransform newTX);
407
408 /**
409 * Returns flags describing the global state of the GlyphVector.
410 * Flags not described below are reserved. The default
411 * implementation returns 0 (meaning false) for the position adjustments,
412 * transforms, rtl, and complex flags.
413 * Subclassers should override this method, and make sure
414 * it correctly describes the GlyphVector and corresponds
415 * to the results of related calls.
416 * @return an int containing the flags describing the state
417 * @see #FLAG_HAS_POSITION_ADJUSTMENTS
418 * @see #FLAG_HAS_TRANSFORMS
419 * @see #FLAG_RUN_RTL
420 * @see #FLAG_COMPLEX_GLYPHS
421 * @see #FLAG_MASK
422 * @since 1.4
423 */
424 public int getLayoutFlags() {
425 return 0;
426 }
427
428 /**
429 * A flag used with getLayoutFlags that indicates that this <code>GlyphVector</code> has
430 * per-glyph transforms.
431 * @since 1.4
432 */
433 public static final int FLAG_HAS_TRANSFORMS = 1;
434
435 /**
436 * A flag used with getLayoutFlags that indicates that this <code>GlyphVector</code> has
437 * position adjustments. When this is true, the glyph positions don't match the
438 * accumulated default advances of the glyphs (for example, if kerning has been done).
439 * @since 1.4
440 */
441 public static final int FLAG_HAS_POSITION_ADJUSTMENTS = 2;
442
443 /**
444 * A flag used with getLayoutFlags that indicates that this <code>GlyphVector</code> has
445 * a right-to-left run direction. This refers to the glyph-to-char mapping and does
446 * not imply that the visual locations of the glyphs are necessarily in this order,
447 * although generally they will be.
448 * @since 1.4
449 */
450 public static final int FLAG_RUN_RTL = 4;
451
452 /**
453 * A flag used with getLayoutFlags that indicates that this <code>GlyphVector</code> has
454 * a complex glyph-to-char mapping (one that does not map glyphs to chars one-to-one in
455 * strictly ascending or descending order matching the run direction).
456 * @since 1.4
457 */
458 public static final int FLAG_COMPLEX_GLYPHS = 8;
459
460 /**
461 * A mask for supported flags from getLayoutFlags. Only bits covered by the mask
462 * should be tested.
463 * @since 1.4
464 */
465 public static final int FLAG_MASK =
466 FLAG_HAS_TRANSFORMS |
467 FLAG_HAS_POSITION_ADJUSTMENTS |
468 FLAG_RUN_RTL |
469 FLAG_COMPLEX_GLYPHS;
470
471 /**
472 * Returns an array of glyph positions for the specified glyphs.
473 * This method is used for convenience and performance when
474 * processing glyph positions.
475 * If no array is passed in, a new array is created.
476 * Even numbered array entries beginning with position zero are the X
477 * coordinates of the glyph numbered <code>beginGlyphIndex + position/2</code>.
478 * Odd numbered array entries beginning with position one are the Y
479 * coordinates of the glyph numbered <code>beginGlyphIndex + (position-1)/2</code>.
480 * If <code>beginGlyphIndex</code> equals the number of of glyphs in
481 * this <code>GlyphVector</code>, this method gets the position after
482 * the last glyph and this position is used to define the advance of
483 * the entire <code>GlyphVector</code>.
484 * @param beginGlyphIndex the index at which to begin retrieving
485 * glyph positions
486 * @param numEntries the number of glyphs to retrieve
487 * @param positionReturn the array that receives the glyph positions
488 * and is then returned.
489 * @return an array of glyph positions specified by
490 * <code>beginGlyphIndex</code> and <code>numEntries</code>.
491 * @throws IllegalArgumentException if <code>numEntries</code> is
492 * less than 0
493 * @throws IndexOutOfBoundsException if <code>beginGlyphIndex</code>
494 * is less than 0
495 * @throws IndexOutOfBoundsException if the sum of
496 * <code>beginGlyphIndex</code> and <code>numEntries</code>
497 * is greater than the number of glyphs in this
498 * <code>GlyphVector</code> plus one
499 */
500 public abstract float[] getGlyphPositions(int beginGlyphIndex, int numEntries,
501 float[] positionReturn);
502
503 /**
504 * Returns the logical bounds of the specified glyph within this
505 * <code>GlyphVector</code>.
506 * These logical bounds have a total of four edges, with two edges
507 * parallel to the baseline under the glyph's transform and the other two
508 * edges are shared with adjacent glyphs if they are present. This
509 * method is useful for hit-testing of the specified glyph,
510 * positioning of a caret at the leading or trailing edge of a glyph,
511 * and for drawing a highlight region around the specified glyph.
512 * @param glyphIndex the index into this <code>GlyphVector</code>
513 * that corresponds to the glyph from which to retrieve its logical
514 * bounds
515 * @return a <code>Shape</code> that is the logical bounds of the
516 * glyph at the specified <code>glyphIndex</code>.
517 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
518 * is less than 0 or greater than or equal to the number
519 * of glyphs in this <code>GlyphVector</code>
520 * @see #getGlyphVisualBounds
521 */
522 public abstract Shape getGlyphLogicalBounds(int glyphIndex);
523
524 /**
525 * Returns the visual bounds of the specified glyph within the
526 * <code>GlyphVector</code>.
527 * The bounds returned by this method is positioned around the
528 * origin of each individual glyph.
529 * @param glyphIndex the index into this <code>GlyphVector</code>
530 * that corresponds to the glyph from which to retrieve its visual
531 * bounds
532 * @return a <code>Shape</code> that is the visual bounds of the
533 * glyph at the specified <code>glyphIndex</code>.
534 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
535 * is less than 0 or greater than or equal to the number
536 * of glyphs in this <code>GlyphVector</code>
537 * @see #getGlyphLogicalBounds
538 */
539 public abstract Shape getGlyphVisualBounds(int glyphIndex);
540
541 /**
542 * Returns the pixel bounds of the glyph at index when this
543 * <code>GlyphVector</code> is rendered in a <code>Graphics</code> with the
544 * given <code>FontRenderContext</code> at the given location. The
545 * renderFRC need not be the same as the
546 * <code>FontRenderContext</code> of this
547 * <code>GlyphVector</code>, and can be null. If it is null, the
548 * <code>FontRenderContext</code> of this <code>GlyphVector</code>
549 * is used. The default implementation returns the visual bounds of the glyph,
550 * offset to x, y and rounded out to the next integer value, and
551 * ignores the FRC. Subclassers should override this method.
552 * @param index the index of the glyph.
553 * @param renderFRC the <code>FontRenderContext</code> of the <code>Graphics</code>.
554 * @param x the X position at which to render this <code>GlyphVector</code>.
555 * @param y the Y position at which to render this <code>GlyphVector</code>.
556 * @return a <code>Rectangle</code> bounding the pixels that would be affected.
557 * @since 1.4
558 */
559 public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, float x, float y) {
560 Rectangle2D rect = getGlyphVisualBounds(index).getBounds2D();
561 int l = (int)Math.floor(rect.getX() + x);
562 int t = (int)Math.floor(rect.getY() + y);
563 int r = (int)Math.ceil(rect.getMaxX() + x);
564 int b = (int)Math.ceil(rect.getMaxY() + y);
565 return new Rectangle(l, t, r - l, b - t);
566 }
567
568 /**
569 * Returns the metrics of the glyph at the specified index into
570 * this <code>GlyphVector</code>.
571 * @param glyphIndex the index into this <code>GlyphVector</code>
572 * that corresponds to the glyph from which to retrieve its metrics
573 * @return a {@link GlyphMetrics} object that represents the
574 * metrics of the glyph at the specified <code>glyphIndex</code>
575 * into this <code>GlyphVector</code>.
576 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
577 * is less than 0 or greater than or equal to the number
578 * of glyphs in this <code>GlyphVector</code>
579 */
580 public abstract GlyphMetrics getGlyphMetrics(int glyphIndex);
581
582 /**
583 * Returns the justification information for the glyph at
584 * the specified index into this <code>GlyphVector</code>.
585 * @param glyphIndex the index into this <code>GlyphVector</code>
586 * that corresponds to the glyph from which to retrieve its
587 * justification properties
588 * @return a {@link GlyphJustificationInfo} object that
589 * represents the justification properties of the glyph at the
590 * specified <code>glyphIndex</code> into this
591 * <code>GlyphVector</code>.
592 * @throws IndexOutOfBoundsException if <code>glyphIndex</code>
593 * is less than 0 or greater than or equal to the number
594 * of glyphs in this <code>GlyphVector</code>
595 */
596 public abstract GlyphJustificationInfo getGlyphJustificationInfo(int glyphIndex);
597
598 //
599 // general utility methods
600 //
601
602 /**
603 * Tests if the specified <code>GlyphVector</code> exactly
604 * equals this <code>GlyphVector</code>.
605 * @param set the specified <code>GlyphVector</code> to test
606 * @return <code>true</code> if the specified
607 * <code>GlyphVector</code> equals this <code>GlyphVector</code>;
608 * <code>false</code> otherwise.
609 */
610 public abstract boolean equals(GlyphVector set);
611}
java/pkg/java/awt/font/GraphicAttribute.class+0−0

Binary file not shown.

java/pkg/java/awt/font/GraphicAttribute.java+221−0
@@ -0,0 +1,221 @@
1/*
2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.awt.Graphics2D;
44import java.awt.Font;
45import java.awt.Shape;
46import java.awt.geom.AffineTransform;
47import java.awt.geom.Rectangle2D;
48
49/**
50 * This class is used with the CHAR_REPLACEMENT attribute.
51 * <p>
52 * The <code>GraphicAttribute</code> class represents a graphic embedded
53 * in text. Clients subclass this class to implement their own char
54 * replacement graphics. Clients wishing to embed shapes and images in
55 * text need not subclass this class. Instead, clients can use the
56 * {@link ShapeGraphicAttribute} and {@link ImageGraphicAttribute}
57 * classes.
58 * <p>
59 * Subclasses must ensure that their objects are immutable once they
60 * are constructed. Mutating a <code>GraphicAttribute</code> that
61 * is used in a {@link TextLayout} results in undefined behavior from the
62 * <code>TextLayout</code>.
63 */
64public abstract class GraphicAttribute {
65
66 private int fAlignment;
67
68 /**
69 * Aligns top of graphic to top of line.
70 */
71 public static final int TOP_ALIGNMENT = -1;
72
73 /**
74 * Aligns bottom of graphic to bottom of line.
75 */
76 public static final int BOTTOM_ALIGNMENT = -2;
77
78 /**
79 * Aligns origin of graphic to roman baseline of line.
80 */
81 public static final int ROMAN_BASELINE = Font.ROMAN_BASELINE;
82
83 /**
84 * Aligns origin of graphic to center baseline of line.
85 */
86 public static final int CENTER_BASELINE = Font.CENTER_BASELINE;
87
88 /**
89 * Aligns origin of graphic to hanging baseline of line.
90 */
91 public static final int HANGING_BASELINE = Font.HANGING_BASELINE;
92
93 /**
94 * Constructs a <code>GraphicAttribute</code>.
95 * Subclasses use this to define the alignment of the graphic.
96 * @param alignment an int representing one of the
97 * <code>GraphicAttribute</code> alignment fields
98 * @throws IllegalArgumentException if alignment is not one of the
99 * five defined values.
100 */
101 protected GraphicAttribute(int alignment) {
102 if (alignment < BOTTOM_ALIGNMENT || alignment > HANGING_BASELINE) {
103 throw new IllegalArgumentException("bad alignment");
104 }
105 fAlignment = alignment;
106 }
107
108 /**
109 * Returns the ascent of this <code>GraphicAttribute</code>. A
110 * graphic can be rendered above its ascent.
111 * @return the ascent of this <code>GraphicAttribute</code>.
112 * @see #getBounds()
113 */
114 public abstract float getAscent();
115
116
117 /**
118 * Returns the descent of this <code>GraphicAttribute</code>. A
119 * graphic can be rendered below its descent.
120 * @return the descent of this <code>GraphicAttribute</code>.
121 * @see #getBounds()
122 */
123 public abstract float getDescent();
124
125 /**
126 * Returns the advance of this <code>GraphicAttribute</code>. The
127 * <code>GraphicAttribute</code> object's advance is the distance
128 * from the point at which the graphic is rendered and the point where
129 * the next character or graphic is rendered. A graphic can be
130 * rendered beyond its advance
131 * @return the advance of this <code>GraphicAttribute</code>.
132 * @see #getBounds()
133 */
134 public abstract float getAdvance();
135
136 /**
137 * Returns a {@link Rectangle2D} that encloses all of the
138 * bits drawn by this <code>GraphicAttribute</code> relative to the
139 * rendering position.
140 * A graphic may be rendered beyond its origin, ascent, descent,
141 * or advance; but if it is, this method's implementation must
142 * indicate where the graphic is rendered.
143 * Default bounds is the rectangle (0, -ascent, advance, ascent+descent).
144 * @return a <code>Rectangle2D</code> that encloses all of the bits
145 * rendered by this <code>GraphicAttribute</code>.
146 */
147 public Rectangle2D getBounds() {
148 float ascent = getAscent();
149 return new Rectangle2D.Float(0, -ascent,
150 getAdvance(), ascent+getDescent());
151 }
152
153 /**
154 * Return a {@link java.awt.Shape} that represents the region that
155 * this <code>GraphicAttribute</code> renders. This is used when a
156 * {@link TextLayout} is requested to return the outline of the text.
157 * The (untransformed) shape must not extend outside the rectangular
158 * bounds returned by <code>getBounds</code>.
159 * The default implementation returns the rectangle returned by
160 * {@link #getBounds}, transformed by the provided {@link AffineTransform}
161 * if present.
162 * @param tx an optional {@link AffineTransform} to apply to the
163 * outline of this <code>GraphicAttribute</code>. This can be null.
164 * @return a <code>Shape</code> representing this graphic attribute,
165 * suitable for stroking or filling.
166 * @since 1.6
167 */
168 public Shape getOutline(AffineTransform tx) {
169 Shape b = getBounds();
170 if (tx != null) {
171 b = tx.createTransformedShape(b);
172 }
173 return b;
174 }
175
176 /**
177 * Renders this <code>GraphicAttribute</code> at the specified
178 * location.
179 * @param graphics the {@link Graphics2D} into which to render the
180 * graphic
181 * @param x the user-space X coordinate where the graphic is rendered
182 * @param y the user-space Y coordinate where the graphic is rendered
183 */
184 public abstract void draw(Graphics2D graphics, float x, float y);
185
186 /**
187 * Returns the alignment of this <code>GraphicAttribute</code>.
188 * Alignment can be to a particular baseline, or to the absolute top
189 * or bottom of a line.
190 * @return the alignment of this <code>GraphicAttribute</code>.
191 */
192 public final int getAlignment() {
193
194 return fAlignment;
195 }
196
197 /**
198 * Returns the justification information for this
199 * <code>GraphicAttribute</code>. Subclasses
200 * can override this method to provide different justification
201 * information.
202 * @return a {@link GlyphJustificationInfo} object that contains the
203 * justification information for this <code>GraphicAttribute</code>.
204 */
205 public GlyphJustificationInfo getJustificationInfo() {
206
207 // should we cache this?
208 float advance = getAdvance();
209
210 return new GlyphJustificationInfo(
211 advance, // weight
212 false, // growAbsorb
213 2, // growPriority
214 advance/3, // growLeftLimit
215 advance/3, // growRightLimit
216 false, // shrinkAbsorb
217 1, // shrinkPriority
218 0, // shrinkLeftLimit
219 0); // shrinkRightLimit
220 }
221}
java/pkg/java/awt/font/ImageGraphicAttribute.class+0−0

Binary file not shown.

java/pkg/java/awt/font/ImageGraphicAttribute.java+227−0
@@ -0,0 +1,227 @@
1/*
2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.awt.Image;
44import java.awt.Graphics2D;
45import java.awt.geom.Rectangle2D;
46
47/**
48 * The <code>ImageGraphicAttribute</code> class is an implementation of
49 * {@link GraphicAttribute} which draws images in
50 * a {@link TextLayout}.
51 * @see GraphicAttribute
52 */
53
54public final class ImageGraphicAttribute extends GraphicAttribute {
55
56 private Image fImage;
57 private float fImageWidth, fImageHeight;
58 private float fOriginX, fOriginY;
59
60 /**
61 * Constucts an <code>ImageGraphicAttribute</code> from the specified
62 * {@link Image}. The origin is at (0,&nbsp;0).
63 * @param image the <code>Image</code> rendered by this
64 * <code>ImageGraphicAttribute</code>.
65 * This object keeps a reference to <code>image</code>.
66 * @param alignment one of the alignments from this
67 * <code>ImageGraphicAttribute</code>
68 */
69 public ImageGraphicAttribute(Image image, int alignment) {
70
71 this(image, alignment, 0, 0);
72 }
73
74 /**
75 * Constructs an <code>ImageGraphicAttribute</code> from the specified
76 * <code>Image</code>. The point
77 * (<code>originX</code>,&nbsp;<code>originY</code>) in the
78 * <code>Image</code> appears at the origin of the
79 * <code>ImageGraphicAttribute</code> within the text.
80 * @param image the <code>Image</code> rendered by this
81 * <code>ImageGraphicAttribute</code>.
82 * This object keeps a reference to <code>image</code>.
83 * @param alignment one of the alignments from this
84 * <code>ImageGraphicAttribute</code>
85 * @param originX the X coordinate of the point within
86 * the <code>Image</code> that appears at the origin of the
87 * <code>ImageGraphicAttribute</code> in the text line.
88 * @param originY the Y coordinate of the point within
89 * the <code>Image</code> that appears at the origin of the
90 * <code>ImageGraphicAttribute</code> in the text line.
91 */
92 public ImageGraphicAttribute(Image image,
93 int alignment,
94 float originX,
95 float originY) {
96
97 super(alignment);
98
99 // Can't clone image
100 // fImage = (Image) image.clone();
101 fImage = image;
102
103 fImageWidth = image.getWidth(null);
104 fImageHeight = image.getHeight(null);
105
106 // ensure origin is in Image?
107 fOriginX = originX;
108 fOriginY = originY;
109 }
110
111 /**
112 * Returns the ascent of this <code>ImageGraphicAttribute</code>. The
113 * ascent of an <code>ImageGraphicAttribute</code> is the distance
114 * from the top of the image to the origin.
115 * @return the ascent of this <code>ImageGraphicAttribute</code>.
116 */
117 public float getAscent() {
118
119 return Math.max(0, fOriginY);
120 }
121
122 /**
123 * Returns the descent of this <code>ImageGraphicAttribute</code>.
124 * The descent of an <code>ImageGraphicAttribute</code> is the
125 * distance from the origin to the bottom of the image.
126 * @return the descent of this <code>ImageGraphicAttribute</code>.
127 */
128 public float getDescent() {
129
130 return Math.max(0, fImageHeight-fOriginY);
131 }
132
133 /**
134 * Returns the advance of this <code>ImageGraphicAttribute</code>.
135 * The advance of an <code>ImageGraphicAttribute</code> is the
136 * distance from the origin to the right edge of the image.
137 * @return the advance of this <code>ImageGraphicAttribute</code>.
138 */
139 public float getAdvance() {
140
141 return Math.max(0, fImageWidth-fOriginX);
142 }
143
144 /**
145 * Returns a {@link Rectangle2D} that encloses all of the
146 * bits rendered by this <code>ImageGraphicAttribute</code>, relative
147 * to the rendering position. A graphic can be rendered beyond its
148 * origin, ascent, descent, or advance; but if it is, this
149 * method's implementation must indicate where the graphic is rendered.
150 * @return a <code>Rectangle2D</code> that encloses all of the bits
151 * rendered by this <code>ImageGraphicAttribute</code>.
152 */
153 public Rectangle2D getBounds() {
154
155 return new Rectangle2D.Float(
156 -fOriginX, -fOriginY, fImageWidth, fImageHeight);
157 }
158
159 /**
160 * {@inheritDoc}
161 */
162 public void draw(Graphics2D graphics, float x, float y) {
163
164 graphics.drawImage(fImage, (int) (x-fOriginX), (int) (y-fOriginY), null);
165 }
166
167 /**
168 * Returns a hashcode for this <code>ImageGraphicAttribute</code>.
169 * @return a hash code value for this object.
170 */
171 public int hashCode() {
172
173 return fImage.hashCode();
174 }
175
176 /**
177 * Compares this <code>ImageGraphicAttribute</code> to the specified
178 * {@link Object}.
179 * @param rhs the <code>Object</code> to compare for equality
180 * @return <code>true</code> if this
181 * <code>ImageGraphicAttribute</code> equals <code>rhs</code>;
182 * <code>false</code> otherwise.
183 */
184 public boolean equals(Object rhs) {
185
186 try {
187 return equals((ImageGraphicAttribute) rhs);
188 }
189 catch(ClassCastException e) {
190 return false;
191 }
192 }
193
194 /**
195 * Compares this <code>ImageGraphicAttribute</code> to the specified
196 * <code>ImageGraphicAttribute</code>.
197 * @param rhs the <code>ImageGraphicAttribute</code> to compare for
198 * equality
199 * @return <code>true</code> if this
200 * <code>ImageGraphicAttribute</code> equals <code>rhs</code>;
201 * <code>false</code> otherwise.
202 */
203 public boolean equals(ImageGraphicAttribute rhs) {
204
205 if (rhs == null) {
206 return false;
207 }
208
209 if (this == rhs) {
210 return true;
211 }
212
213 if (fOriginX != rhs.fOriginX || fOriginY != rhs.fOriginY) {
214 return false;
215 }
216
217 if (getAlignment() != rhs.getAlignment()) {
218 return false;
219 }
220
221 if (!fImage.equals(rhs.fImage)) {
222 return false;
223 }
224
225 return true;
226 }
227}
java/pkg/java/awt/font/LayoutPath.class+0−0

Binary file not shown.

java/pkg/java/awt/font/LayoutPath.java+84−0
@@ -0,0 +1,84 @@
1/*
2 * Copyright (c) 2005, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * (C) Copyright IBM Corp. 2005, All Rights Reserved.
27 */
28package java.awt.font;
29
30import java.awt.geom.Point2D;
31
32/**
33 * LayoutPath provides a mapping between locations relative to the
34 * baseline and points in user space. Locations consist of an advance
35 * along the baseline, and an offset perpendicular to the baseline at
36 * the advance. Positive values along the perpendicular are in the
37 * direction that is 90 degrees clockwise from the baseline vector.
38 * Locations are represented as a <code>Point2D</code>, where x is the advance and
39 * y is the offset.
40 *
41 * @since 1.6
42 */
43public abstract class LayoutPath {
44 /**
45 * Convert a point in user space to a location relative to the
46 * path. The location is chosen so as to minimize the distance
47 * from the point to the path (e.g., the magnitude of the offset
48 * will be smallest). If there is more than one such location,
49 * the location with the smallest advance is chosen.
50 * @param point the point to convert. If it is not the same
51 * object as location, point will remain unmodified by this call.
52 * @param location a <code>Point2D</code> to hold the returned location.
53 * It can be the same object as point.
54 * @return true if the point is associated with the portion of the
55 * path preceding the location, false if it is associated with
56 * the portion following. The default, if the location is not at
57 * a break or sharp bend in the path, is to return true.
58 * @throws NullPointerException if point or location is null
59 * @since 1.6
60 */
61 public abstract boolean pointToPath(Point2D point, Point2D location);
62
63 /**
64 * Convert a location relative to the path to a point in user
65 * coordinates. The path might bend abruptly or be disjoint at
66 * the location's advance. If this is the case, the value of
67 * 'preceding' is used to disambiguate the portion of the path
68 * whose location and slope is to be used to interpret the offset.
69 * @param location a <code>Point2D</code> representing the advance (in x) and
70 * offset (in y) of a location relative to the path. If location
71 * is not the same object as point, location will remain
72 * unmodified by this call.
73 * @param preceding if true, the portion preceding the advance
74 * should be used, if false the portion after should be used.
75 * This has no effect if the path does not break or bend sharply
76 * at the advance.
77 * @param point a <code>Point2D</code> to hold the returned point. It can be
78 * the same object as location.
79 * @throws NullPointerException if location or point is null
80 * @since 1.6
81 */
82 public abstract void pathToPoint(Point2D location, boolean preceding,
83 Point2D point);
84}
java/pkg/java/awt/font/LineBreakMeasurer.class+0−0

Binary file not shown.

java/pkg/java/awt/font/LineBreakMeasurer.java+533−0
@@ -0,0 +1,533 @@
1/*
2 * Copyright (c) 1998, 2001, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.text.BreakIterator;
44import java.text.CharacterIterator;
45import java.text.AttributedCharacterIterator;
46import java.awt.font.FontRenderContext;
47
48/**
49 * The <code>LineBreakMeasurer</code> class allows styled text to be
50 * broken into lines (or segments) that fit within a particular visual
51 * advance. This is useful for clients who wish to display a paragraph of
52 * text that fits within a specific width, called the <b>wrapping
53 * width</b>.
54 * <p>
55 * <code>LineBreakMeasurer</code> is constructed with an iterator over
56 * styled text. The iterator's range should be a single paragraph in the
57 * text.
58 * <code>LineBreakMeasurer</code> maintains a position in the text for the
59 * start of the next text segment. Initially, this position is the
60 * start of text. Paragraphs are assigned an overall direction (either
61 * left-to-right or right-to-left) according to the bidirectional
62 * formatting rules. All segments obtained from a paragraph have the
63 * same direction as the paragraph.
64 * <p>
65 * Segments of text are obtained by calling the method
66 * <code>nextLayout</code>, which returns a {@link TextLayout}
67 * representing the text that fits within the wrapping width.
68 * The <code>nextLayout</code> method moves the current position
69 * to the end of the layout returned from <code>nextLayout</code>.
70 * <p>
71 * <code>LineBreakMeasurer</code> implements the most commonly used
72 * line-breaking policy: Every word that fits within the wrapping
73 * width is placed on the line. If the first word does not fit, then all
74 * of the characters that fit within the wrapping width are placed on the
75 * line. At least one character is placed on each line.
76 * <p>
77 * The <code>TextLayout</code> instances returned by
78 * <code>LineBreakMeasurer</code> treat tabs like 0-width spaces. Clients
79 * who wish to obtain tab-delimited segments for positioning should use
80 * the overload of <code>nextLayout</code> which takes a limiting offset
81 * in the text.
82 * The limiting offset should be the first character after the tab.
83 * The <code>TextLayout</code> objects returned from this method end
84 * at the limit provided (or before, if the text between the current
85 * position and the limit won't fit entirely within the wrapping
86 * width).
87 * <p>
88 * Clients who are laying out tab-delimited text need a slightly
89 * different line-breaking policy after the first segment has been
90 * placed on a line. Instead of fitting partial words in the
91 * remaining space, they should place words which don't fit in the
92 * remaining space entirely on the next line. This change of policy
93 * can be requested in the overload of <code>nextLayout</code> which
94 * takes a <code>boolean</code> parameter. If this parameter is
95 * <code>true</code>, <code>nextLayout</code> returns
96 * <code>null</code> if the first word won't fit in
97 * the given space. See the tab sample below.
98 * <p>
99 * In general, if the text used to construct the
100 * <code>LineBreakMeasurer</code> changes, a new
101 * <code>LineBreakMeasurer</code> must be constructed to reflect
102 * the change. (The old <code>LineBreakMeasurer</code> continues to
103 * function properly, but it won't be aware of the text change.)
104 * Nevertheless, if the text change is the insertion or deletion of a
105 * single character, an existing <code>LineBreakMeasurer</code> can be
106 * 'updated' by calling <code>insertChar</code> or
107 * <code>deleteChar</code>. Updating an existing
108 * <code>LineBreakMeasurer</code> is much faster than creating a new one.
109 * Clients who modify text based on user typing should take advantage
110 * of these methods.
111 * <p>
112 * <strong>Examples</strong>:<p>
113 * Rendering a paragraph in a component
114 * <blockquote>
115 * <pre>
116 * public void paint(Graphics graphics) {
117 *
118 * Point2D pen = new Point2D(10, 20);
119 * Graphics2D g2d = (Graphics2D)graphics;
120 * FontRenderContext frc = g2d.getFontRenderContext();
121 *
122 * // let styledText be an AttributedCharacterIterator containing at least
123 * // one character
124 *
125 * LineBreakMeasurer measurer = new LineBreakMeasurer(styledText, frc);
126 * float wrappingWidth = getSize().width - 15;
127 *
128 * while (measurer.getPosition() < fStyledText.length()) {
129 *
130 * TextLayout layout = measurer.nextLayout(wrappingWidth);
131 *
132 * pen.y += (layout.getAscent());
133 * float dx = layout.isLeftToRight() ?
134 * 0 : (wrappingWidth - layout.getAdvance());
135 *
136 * layout.draw(graphics, pen.x + dx, pen.y);
137 * pen.y += layout.getDescent() + layout.getLeading();
138 * }
139 * }
140 * </pre>
141 * </blockquote>
142 * <p>
143 * Rendering text with tabs. For simplicity, the overall text
144 * direction is assumed to be left-to-right
145 * <blockquote>
146 * <pre>
147 * public void paint(Graphics graphics) {
148 *
149 * float leftMargin = 10, rightMargin = 310;
150 * float[] tabStops = { 100, 250 };
151 *
152 * // assume styledText is an AttributedCharacterIterator, and the number
153 * // of tabs in styledText is tabCount
154 *
155 * int[] tabLocations = new int[tabCount+1];
156 *
157 * int i = 0;
158 * for (char c = styledText.first(); c != styledText.DONE; c = styledText.next()) {
159 * if (c == '\t') {
160 * tabLocations[i++] = styledText.getIndex();
161 * }
162 * }
163 * tabLocations[tabCount] = styledText.getEndIndex() - 1;
164 *
165 * // Now tabLocations has an entry for every tab's offset in
166 * // the text. For convenience, the last entry is tabLocations
167 * // is the offset of the last character in the text.
168 *
169 * LineBreakMeasurer measurer = new LineBreakMeasurer(styledText);
170 * int currentTab = 0;
171 * float verticalPos = 20;
172 *
173 * while (measurer.getPosition() < styledText.getEndIndex()) {
174 *
175 * // Lay out and draw each line. All segments on a line
176 * // must be computed before any drawing can occur, since
177 * // we must know the largest ascent on the line.
178 * // TextLayouts are computed and stored in a Vector;
179 * // their horizontal positions are stored in a parallel
180 * // Vector.
181 *
182 * // lineContainsText is true after first segment is drawn
183 * boolean lineContainsText = false;
184 * boolean lineComplete = false;
185 * float maxAscent = 0, maxDescent = 0;
186 * float horizontalPos = leftMargin;
187 * Vector layouts = new Vector(1);
188 * Vector penPositions = new Vector(1);
189 *
190 * while (!lineComplete) {
191 * float wrappingWidth = rightMargin - horizontalPos;
192 * TextLayout layout =
193 * measurer.nextLayout(wrappingWidth,
194 * tabLocations[currentTab]+1,
195 * lineContainsText);
196 *
197 * // layout can be null if lineContainsText is true
198 * if (layout != null) {
199 * layouts.addElement(layout);
200 * penPositions.addElement(new Float(horizontalPos));
201 * horizontalPos += layout.getAdvance();
202 * maxAscent = Math.max(maxAscent, layout.getAscent());
203 * maxDescent = Math.max(maxDescent,
204 * layout.getDescent() + layout.getLeading());
205 * } else {
206 * lineComplete = true;
207 * }
208 *
209 * lineContainsText = true;
210 *
211 * if (measurer.getPosition() == tabLocations[currentTab]+1) {
212 * currentTab++;
213 * }
214 *
215 * if (measurer.getPosition() == styledText.getEndIndex())
216 * lineComplete = true;
217 * else if (horizontalPos >= tabStops[tabStops.length-1])
218 * lineComplete = true;
219 *
220 * if (!lineComplete) {
221 * // move to next tab stop
222 * int j;
223 * for (j=0; horizontalPos >= tabStops[j]; j++) {}
224 * horizontalPos = tabStops[j];
225 * }
226 * }
227 *
228 * verticalPos += maxAscent;
229 *
230 * Enumeration layoutEnum = layouts.elements();
231 * Enumeration positionEnum = penPositions.elements();
232 *
233 * // now iterate through layouts and draw them
234 * while (layoutEnum.hasMoreElements()) {
235 * TextLayout nextLayout = (TextLayout) layoutEnum.nextElement();
236 * Float nextPosition = (Float) positionEnum.nextElement();
237 * nextLayout.draw(graphics, nextPosition.floatValue(), verticalPos);
238 * }
239 *
240 * verticalPos += maxDescent;
241 * }
242 * }
243 * </pre>
244 * </blockquote>
245 * @see TextLayout
246 */
247
248public final class LineBreakMeasurer {
249
250 private BreakIterator breakIter;
251 private int start;
252 private int pos;
253 private int limit;
254 private TextMeasurer measurer;
255 private CharArrayIterator charIter;
256
257 /**
258 * Constructs a <code>LineBreakMeasurer</code> for the specified text.
259 *
260 * @param text the text for which this <code>LineBreakMeasurer</code>
261 * produces <code>TextLayout</code> objects; the text must contain
262 * at least one character; if the text available through
263 * <code>iter</code> changes, further calls to this
264 * <code>LineBreakMeasurer</code> instance are undefined (except,
265 * in some cases, when <code>insertChar</code> or
266 * <code>deleteChar</code> are invoked afterward - see below)
267 * @param frc contains information about a graphics device which is
268 * needed to measure the text correctly;
269 * text measurements can vary slightly depending on the
270 * device resolution, and attributes such as antialiasing; this
271 * parameter does not specify a translation between the
272 * <code>LineBreakMeasurer</code> and user space
273 * @see LineBreakMeasurer#insertChar
274 * @see LineBreakMeasurer#deleteChar
275 */
276 public LineBreakMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
277 this(text, BreakIterator.getLineInstance(), frc);
278 }
279
280 /**
281 * Constructs a <code>LineBreakMeasurer</code> for the specified text.
282 *
283 * @param text the text for which this <code>LineBreakMeasurer</code>
284 * produces <code>TextLayout</code> objects; the text must contain
285 * at least one character; if the text available through
286 * <code>iter</code> changes, further calls to this
287 * <code>LineBreakMeasurer</code> instance are undefined (except,
288 * in some cases, when <code>insertChar</code> or
289 * <code>deleteChar</code> are invoked afterward - see below)
290 * @param breakIter the {@link BreakIterator} which defines line
291 * breaks
292 * @param frc contains information about a graphics device which is
293 * needed to measure the text correctly;
294 * text measurements can vary slightly depending on the
295 * device resolution, and attributes such as antialiasing; this
296 * parameter does not specify a translation between the
297 * <code>LineBreakMeasurer</code> and user space
298 * @throws IllegalArgumentException if the text has less than one character
299 * @see LineBreakMeasurer#insertChar
300 * @see LineBreakMeasurer#deleteChar
301 */
302 public LineBreakMeasurer(AttributedCharacterIterator text,
303 BreakIterator breakIter,
304 FontRenderContext frc) {
305 if (text.getEndIndex() - text.getBeginIndex() < 1) {
306 throw new IllegalArgumentException("Text must contain at least one character.");
307 }
308
309 this.breakIter = breakIter;
310 this.measurer = new TextMeasurer(text, frc);
311 this.limit = text.getEndIndex();
312 this.pos = this.start = text.getBeginIndex();
313
314 charIter = new CharArrayIterator(measurer.getChars(), this.start);
315 this.breakIter.setText(charIter);
316 }
317
318 /**
319 * Returns the position at the end of the next layout. Does NOT
320 * update the current position of this <code>LineBreakMeasurer</code>.
321 *
322 * @param wrappingWidth the maximum visible advance permitted for
323 * the text in the next layout
324 * @return an offset in the text representing the limit of the
325 * next <code>TextLayout</code>.
326 */
327 public int nextOffset(float wrappingWidth) {
328 return nextOffset(wrappingWidth, limit, false);
329 }
330
331 /**
332 * Returns the position at the end of the next layout. Does NOT
333 * update the current position of this <code>LineBreakMeasurer</code>.
334 *
335 * @param wrappingWidth the maximum visible advance permitted for
336 * the text in the next layout
337 * @param offsetLimit the first character that can not be included
338 * in the next layout, even if the text after the limit would fit
339 * within the wrapping width; <code>offsetLimit</code> must be
340 * greater than the current position
341 * @param requireNextWord if <code>true</code>, the current position
342 * that is returned if the entire next word does not fit within
343 * <code>wrappingWidth</code>; if <code>false</code>, the offset
344 * returned is at least one greater than the current position
345 * @return an offset in the text representing the limit of the
346 * next <code>TextLayout</code>
347 */
348 public int nextOffset(float wrappingWidth, int offsetLimit,
349 boolean requireNextWord) {
350
351 int nextOffset = pos;
352
353 if (pos < limit) {
354 if (offsetLimit <= pos) {
355 throw new IllegalArgumentException("offsetLimit must be after current position");
356 }
357
358 int charAtMaxAdvance =
359 measurer.getLineBreakIndex(pos, wrappingWidth);
360
361 if (charAtMaxAdvance == limit) {
362 nextOffset = limit;
363 }
364 else if (Character.isWhitespace(measurer.getChars()[charAtMaxAdvance-start])) {
365 nextOffset = breakIter.following(charAtMaxAdvance);
366 }
367 else {
368 // Break is in a word; back up to previous break.
369
370 // NOTE: I think that breakIter.preceding(limit) should be
371 // equivalent to breakIter.last(), breakIter.previous() but
372 // the authors of BreakIterator thought otherwise...
373 // If they were equivalent then the first branch would be
374 // unnecessary.
375 int testPos = charAtMaxAdvance + 1;
376 if (testPos == limit) {
377 breakIter.last();
378 nextOffset = breakIter.previous();
379 }
380 else {
381 nextOffset = breakIter.preceding(testPos);
382 }
383
384 if (nextOffset <= pos) {
385 // first word doesn't fit on line
386 if (requireNextWord) {
387 nextOffset = pos;
388 }
389 else {
390 nextOffset = Math.max(pos+1, charAtMaxAdvance);
391 }
392 }
393 }
394 }
395
396 if (nextOffset > offsetLimit) {
397 nextOffset = offsetLimit;
398 }
399
400 return nextOffset;
401 }
402
403 /**
404 * Returns the next layout, and updates the current position.
405 *
406 * @param wrappingWidth the maximum visible advance permitted for
407 * the text in the next layout
408 * @return a <code>TextLayout</code>, beginning at the current
409 * position, which represents the next line fitting within
410 * <code>wrappingWidth</code>
411 */
412 public TextLayout nextLayout(float wrappingWidth) {
413 return nextLayout(wrappingWidth, limit, false);
414 }
415
416 /**
417 * Returns the next layout, and updates the current position.
418 *
419 * @param wrappingWidth the maximum visible advance permitted
420 * for the text in the next layout
421 * @param offsetLimit the first character that can not be
422 * included in the next layout, even if the text after the limit
423 * would fit within the wrapping width; <code>offsetLimit</code>
424 * must be greater than the current position
425 * @param requireNextWord if <code>true</code>, and if the entire word
426 * at the current position does not fit within the wrapping width,
427 * <code>null</code> is returned. If <code>false</code>, a valid
428 * layout is returned that includes at least the character at the
429 * current position
430 * @return a <code>TextLayout</code>, beginning at the current
431 * position, that represents the next line fitting within
432 * <code>wrappingWidth</code>. If the current position is at the end
433 * of the text used by this <code>LineBreakMeasurer</code>,
434 * <code>null</code> is returned
435 */
436 public TextLayout nextLayout(float wrappingWidth, int offsetLimit,
437 boolean requireNextWord) {
438
439 if (pos < limit) {
440 int layoutLimit = nextOffset(wrappingWidth, offsetLimit, requireNextWord);
441 if (layoutLimit == pos) {
442 return null;
443 }
444
445 TextLayout result = measurer.getLayout(pos, layoutLimit);
446 pos = layoutLimit;
447
448 return result;
449 } else {
450 return null;
451 }
452 }
453
454 /**
455 * Returns the current position of this <code>LineBreakMeasurer</code>.
456 *
457 * @return the current position of this <code>LineBreakMeasurer</code>
458 * @see #setPosition
459 */
460 public int getPosition() {
461 return pos;
462 }
463
464 /**
465 * Sets the current position of this <code>LineBreakMeasurer</code>.
466 *
467 * @param newPosition the current position of this
468 * <code>LineBreakMeasurer</code>; the position should be within the
469 * text used to construct this <code>LineBreakMeasurer</code> (or in
470 * the text most recently passed to <code>insertChar</code>
471 * or <code>deleteChar</code>
472 * @see #getPosition
473 */
474 public void setPosition(int newPosition) {
475 if (newPosition < start || newPosition > limit) {
476 throw new IllegalArgumentException("position is out of range");
477 }
478 pos = newPosition;
479 }
480
481 /**
482 * Updates this <code>LineBreakMeasurer</code> after a single
483 * character is inserted into the text, and sets the current
484 * position to the beginning of the paragraph.
485 *
486 * @param newParagraph the text after the insertion
487 * @param insertPos the position in the text at which the character
488 * is inserted
489 * @throws IndexOutOfBoundsException if <code>insertPos</code> is less
490 * than the start of <code>newParagraph</code> or greater than
491 * or equal to the end of <code>newParagraph</code>
492 * @throws NullPointerException if <code>newParagraph</code> is
493 * <code>null</code>
494 * @see #deleteChar
495 */
496 public void insertChar(AttributedCharacterIterator newParagraph,
497 int insertPos) {
498
499 measurer.insertChar(newParagraph, insertPos);
500
501 limit = newParagraph.getEndIndex();
502 pos = start = newParagraph.getBeginIndex();
503
504 charIter.reset(measurer.getChars(), newParagraph.getBeginIndex());
505 breakIter.setText(charIter);
506 }
507
508 /**
509 * Updates this <code>LineBreakMeasurer</code> after a single
510 * character is deleted from the text, and sets the current
511 * position to the beginning of the paragraph.
512 * @param newParagraph the text after the deletion
513 * @param deletePos the position in the text at which the character
514 * is deleted
515 * @throws IndexOutOfBoundsException if <code>deletePos</code> is
516 * less than the start of <code>newParagraph</code> or greater
517 * than the end of <code>newParagraph</code>
518 * @throws NullPointerException if <code>newParagraph</code> is
519 * <code>null</code>
520 * @see #insertChar
521 */
522 public void deleteChar(AttributedCharacterIterator newParagraph,
523 int deletePos) {
524
525 measurer.deleteChar(newParagraph, deletePos);
526
527 limit = newParagraph.getEndIndex();
528 pos = start = newParagraph.getBeginIndex();
529
530 charIter.reset(measurer.getChars(), start);
531 breakIter.setText(charIter);
532 }
533}
java/pkg/java/awt/font/LineMetrics.class+0−0

Binary file not shown.

java/pkg/java/awt/font/LineMetrics.java+143−0
@@ -0,0 +1,143 @@
1/*
2 * Copyright (c) 1998, 2003, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.awt.font;
27
28/**
29* The <code>LineMetrics</code> class allows access to the
30* metrics needed to layout characters along a line
31* and to layout of a set of lines. A <code>LineMetrics</code>
32* object encapsulates the measurement information associated
33* with a run of text.
34* <p>
35* Fonts can have different metrics for different ranges of
36* characters. The <code>getLineMetrics</code> methods of
37* {@link java.awt.Font Font} take some text as an argument
38* and return a <code>LineMetrics</code> object describing the
39* metrics of the initial number of characters in that text, as
40* returned by {@link #getNumChars}.
41*/
42
43
44public abstract class LineMetrics {
45
46
47 /**
48 * Returns the number of characters (<code>char</code> values) in the text whose
49 * metrics are encapsulated by this <code>LineMetrics</code>
50 * object.
51 * @return the number of characters (<code>char</code> values) in the text with which
52 * this <code>LineMetrics</code> was created.
53 */
54 public abstract int getNumChars();
55
56 /**
57 * Returns the ascent of the text. The ascent
58 * is the distance from the baseline
59 * to the ascender line. The ascent usually represents the
60 * the height of the capital letters of the text. Some characters
61 * can extend above the ascender line.
62 * @return the ascent of the text.
63 */
64 public abstract float getAscent();
65
66 /**
67 * Returns the descent of the text. The descent
68 * is the distance from the baseline
69 * to the descender line. The descent usually represents
70 * the distance to the bottom of lower case letters like
71 * 'p'. Some characters can extend below the descender
72 * line.
73 * @return the descent of the text.
74 */
75 public abstract float getDescent();
76
77 /**
78 * Returns the leading of the text. The
79 * leading is the recommended
80 * distance from the bottom of the descender line to the
81 * top of the next line.
82 * @return the leading of the text.
83 */
84 public abstract float getLeading();
85
86 /**
87 * Returns the height of the text. The
88 * height is equal to the sum of the ascent, the
89 * descent and the leading.
90 * @return the height of the text.
91 */
92 public abstract float getHeight();
93
94 /**
95 * Returns the baseline index of the text.
96 * The index is one of
97 * {@link java.awt.Font#ROMAN_BASELINE ROMAN_BASELINE},
98 * {@link java.awt.Font#CENTER_BASELINE CENTER_BASELINE},
99 * {@link java.awt.Font#HANGING_BASELINE HANGING_BASELINE}.
100 * @return the baseline of the text.
101 */
102 public abstract int getBaselineIndex();
103
104 /**
105 * Returns the baseline offsets of the text,
106 * relative to the baseline of the text. The
107 * offsets are indexed by baseline index. For
108 * example, if the baseline index is
109 * <code>CENTER_BASELINE</code> then
110 * <code>offsets[HANGING_BASELINE]</code> is usually
111 * negative, <code>offsets[CENTER_BASELINE]</code>
112 * is zero, and <code>offsets[ROMAN_BASELINE]</code>
113 * is usually positive.
114 * @return the baseline offsets of the text.
115 */
116 public abstract float[] getBaselineOffsets();
117
118 /**
119 * Returns the position of the strike-through line
120 * relative to the baseline.
121 * @return the position of the strike-through line.
122 */
123 public abstract float getStrikethroughOffset();
124
125 /**
126 * Returns the thickness of the strike-through line.
127 * @return the thickness of the strike-through line.
128 */
129 public abstract float getStrikethroughThickness();
130
131 /**
132 * Returns the position of the underline relative to
133 * the baseline.
134 * @return the position of the underline.
135 */
136 public abstract float getUnderlineOffset();
137
138 /**
139 * Returns the thickness of the underline.
140 * @return the thickness of the underline.
141 */
142 public abstract float getUnderlineThickness();
143}
java/pkg/java/awt/font/MultipleMaster.class+0−0

Binary file not shown.

java/pkg/java/awt/font/MultipleMaster.java+106−0
@@ -0,0 +1,106 @@
1/*
2 * Copyright (c) 1997, 1998, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package java.awt.font;
26
27import java.awt.Font;
28
29/**
30 * The <code>MultipleMaster</code> interface represents Type 1
31 * Multiple Master fonts.
32 * A particular {@link Font} object can implement this interface.
33 */
34public interface MultipleMaster {
35
36 /**
37 * Returns the number of multiple master design controls.
38 * Design axes include things like width, weight and optical scaling.
39 * @return the number of multiple master design controls
40 */
41 public int getNumDesignAxes();
42
43 /**
44 * Returns an array of design limits interleaved in the form [from->to]
45 * for each axis. For example,
46 * design limits for weight could be from 0.1 to 1.0. The values are
47 * returned in the same order returned by
48 * <code>getDesignAxisNames</code>.
49 * @return an array of design limits for each axis.
50 */
51 public float[] getDesignAxisRanges();
52
53 /**
54 * Returns an array of default design values for each axis. For example,
55 * the default value for weight could be 1.6. The values are returned
56 * in the same order returned by <code>getDesignAxisNames</code>.
57 * @return an array of default design values for each axis.
58 */
59 public float[] getDesignAxisDefaults();
60
61 /**
62 * Returns the name for each design axis. This also determines the order in
63 * which the values for each axis are returned.
64 * @return an array containing the names of each design axis.
65 */
66 public String[] getDesignAxisNames();
67
68 /**
69 * Creates a new instance of a multiple master font based on the design
70 * axis values contained in the specified array. The size of the array
71 * must correspond to the value returned from
72 * <code>getNumDesignAxes</code> and the values of the array elements
73 * must fall within limits specified by
74 * <code>getDesignAxesLimits</code>. In case of an error,
75 * <code>null</code> is returned.
76 * @param axes an array containing axis values
77 * @return a {@link Font} object that is an instance of
78 * <code>MultipleMaster</code> and is based on the design axis values
79 * provided by <code>axes</code>.
80 */
81 public Font deriveMMFont(float[] axes);
82
83 /**
84 * Creates a new instance of a multiple master font based on detailed metric
85 * information. In case of an error, <code>null</code> is returned.
86 * @param glyphWidths an array of floats representing the desired width
87 * of each glyph in font space
88 * @param avgStemWidth the average stem width for the overall font in
89 * font space
90 * @param typicalCapHeight the height of a typical upper case char
91 * @param typicalXHeight the height of a typical lower case char
92 * @param italicAngle the angle at which the italics lean, in degrees
93 * counterclockwise from vertical
94 * @return a <code>Font</code> object that is an instance of
95 * <code>MultipleMaster</code> and is based on the specified metric
96 * information.
97 */
98 public Font deriveMMFont(
99 float[] glyphWidths,
100 float avgStemWidth,
101 float typicalCapHeight,
102 float typicalXHeight,
103 float italicAngle);
104
105
106}
java/pkg/java/awt/font/NumericShaper$1.class+0−0

Binary file not shown.

java/pkg/java/awt/font/NumericShaper$Range$1.class+0−0

Binary file not shown.

java/pkg/java/awt/font/NumericShaper$Range.class+0−0

Binary file not shown.

java/pkg/java/awt/font/NumericShaper.class+0−0

Binary file not shown.

java/pkg/java/awt/font/NumericShaper.java+1507−0
@@ -0,0 +1,1507 @@
1/*
2 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.awt.font;
27
28import java.io.IOException;
29import java.io.ObjectOutputStream;
30import java.util.Arrays;
31import java.util.Comparator;
32import java.util.EnumSet;
33import java.util.Set;
34
35/**
36 * The <code>NumericShaper</code> class is used to convert Latin-1 (European)
37 * digits to other Unicode decimal digits. Users of this class will
38 * primarily be people who wish to present data using
39 * national digit shapes, but find it more convenient to represent the
40 * data internally using Latin-1 (European) digits. This does not
41 * interpret the deprecated numeric shape selector character (U+206E).
42 * <p>
43 * Instances of <code>NumericShaper</code> are typically applied
44 * as attributes to text with the
45 * {@link TextAttribute#NUMERIC_SHAPING NUMERIC_SHAPING} attribute
46 * of the <code>TextAttribute</code> class.
47 * For example, this code snippet causes a <code>TextLayout</code> to
48 * shape European digits to Arabic in an Arabic context:<br>
49 * <blockquote><pre>
50 * Map map = new HashMap();
51 * map.put(TextAttribute.NUMERIC_SHAPING,
52 * NumericShaper.getContextualShaper(NumericShaper.ARABIC));
53 * FontRenderContext frc = ...;
54 * TextLayout layout = new TextLayout(text, map, frc);
55 * layout.draw(g2d, x, y);
56 * </pre></blockquote>
57 * <br>
58 * It is also possible to perform numeric shaping explicitly using instances
59 * of <code>NumericShaper</code>, as this code snippet demonstrates:<br>
60 * <blockquote><pre>
61 * char[] text = ...;
62 * // shape all EUROPEAN digits (except zero) to ARABIC digits
63 * NumericShaper shaper = NumericShaper.getShaper(NumericShaper.ARABIC);
64 * shaper.shape(text, start, count);
65 *
66 * // shape European digits to ARABIC digits if preceding text is Arabic, or
67 * // shape European digits to TAMIL digits if preceding text is Tamil, or
68 * // leave European digits alone if there is no preceding text, or
69 * // preceding text is neither Arabic nor Tamil
70 * NumericShaper shaper =
71 * NumericShaper.getContextualShaper(NumericShaper.ARABIC |
72 * NumericShaper.TAMIL,
73 * NumericShaper.EUROPEAN);
74 * shaper.shape(text, start, count);
75 * </pre></blockquote>
76 *
77 * <p><b>Bit mask- and enum-based Unicode ranges</b></p>
78 *
79 * <p>This class supports two different programming interfaces to
80 * represent Unicode ranges for script-specific digits: bit
81 * mask-based ones, such as {@link #ARABIC NumericShaper.ARABIC}, and
82 * enum-based ones, such as {@link NumericShaper.Range#ARABIC}.
83 * Multiple ranges can be specified by ORing bit mask-based constants,
84 * such as:
85 * <blockquote><pre>
86 * NumericShaper.ARABIC | NumericShaper.TAMIL
87 * </pre></blockquote>
88 * or creating a {@code Set} with the {@link NumericShaper.Range}
89 * constants, such as:
90 * <blockquote><pre>
91 * EnumSet.of(NumericShaper.Scirpt.ARABIC, NumericShaper.Range.TAMIL)
92 * </pre></blockquote>
93 * The enum-based ranges are a super set of the bit mask-based ones.
94 *
95 * <p>If the two interfaces are mixed (including serialization),
96 * Unicode range values are mapped to their counterparts where such
97 * mapping is possible, such as {@code NumericShaper.Range.ARABIC}
98 * from/to {@code NumericShaper.ARABIC}. If any unmappable range
99 * values are specified, such as {@code NumericShaper.Range.BALINESE},
100 * those ranges are ignored.
101 *
102 * <p><b>Decimal Digits Precedence</b></p>
103 *
104 * <p>A Unicode range may have more than one set of decimal digits. If
105 * multiple decimal digits sets are specified for the same Unicode
106 * range, one of the sets will take precedence as follows.
107 *
108 * <table border=1 cellspacing=3 cellpadding=0 summary="NumericShaper constants precedence.">
109 * <tr>
110 * <th class="TableHeadingColor">Unicode Range</th>
111 * <th class="TableHeadingColor"><code>NumericShaper</code> Constants</th>
112 * <th class="TableHeadingColor">Precedence</th>
113 * </tr>
114 * <tr>
115 * <td rowspan="2">Arabic</td>
116 * <td>{@link NumericShaper#ARABIC NumericShaper.ARABIC}<br>
117 * {@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td>
118 * <td>{@link NumericShaper#EASTERN_ARABIC NumericShaper.EASTERN_ARABIC}</td>
119 * </tr>
120 * <tr>
121 * <td>{@link NumericShaper.Range#ARABIC}<br>
122 * {@link NumericShaper.Range#EASTERN_ARABIC}</td>
123 * <td>{@link NumericShaper.Range#EASTERN_ARABIC}</td>
124 * </tr>
125 * <tr>
126 * <td>Tai Tham</td>
127 * <td>{@link NumericShaper.Range#TAI_THAM_HORA}<br>
128 * {@link NumericShaper.Range#TAI_THAM_THAM}</td>
129 * <td>{@link NumericShaper.Range#TAI_THAM_THAM}</td>
130 * </tr>
131 * </table>
132 *
133 * @since 1.4
134 */
135
136public final class NumericShaper implements java.io.Serializable {
137 /**
138 * A {@code NumericShaper.Range} represents a Unicode range of a
139 * script having its own decimal digits. For example, the {@link
140 * NumericShaper.Range#THAI} range has the Thai digits, THAI DIGIT
141 * ZERO (U+0E50) to THAI DIGIT NINE (U+0E59).
142 *
143 * <p>The <code>Range</code> enum replaces the traditional bit
144 * mask-based values (e.g., {@link NumericShaper#ARABIC}), and
145 * supports more Unicode ranges than the bit mask-based ones. For
146 * example, the following code using the bit mask:
147 * <blockquote><pre>
148 * NumericShaper.getContextualShaper(NumericShaper.ARABIC |
149 * NumericShaper.TAMIL,
150 * NumericShaper.EUROPEAN);
151 * </pre></blockquote>
152 * can be written using this enum as:
153 * <blockquote><pre>
154 * NumericShaper.getContextualShaper(EnumSet.of(
155 * NumericShaper.Range.ARABIC,
156 * NumericShaper.Range.TAMIL),
157 * NumericShaper.Range.EUROPEAN);
158 * </pre></blockquote>
159 *
160 * @since 1.7
161 */
162 public static enum Range {
163 // The order of EUROPEAN to MOGOLIAN must be consistent
164 // with the bitmask-based constants.
165 /**
166 * The Latin (European) range with the Latin (ASCII) digits.
167 */
168 EUROPEAN ('\u0030', '\u0000', '\u0300'),
169 /**
170 * The Arabic range with the Arabic-Indic digits.
171 */
172 ARABIC ('\u0660', '\u0600', '\u0780'),
173 /**
174 * The Arabic range with the Eastern Arabic-Indic digits.
175 */
176 EASTERN_ARABIC ('\u06f0', '\u0600', '\u0780'),
177 /**
178 * The Devanagari range with the Devanagari digits.
179 */
180 DEVANAGARI ('\u0966', '\u0900', '\u0980'),
181 /**
182 * The Bengali range with the Bengali digits.
183 */
184 BENGALI ('\u09e6', '\u0980', '\u0a00'),
185 /**
186 * The Gurmukhi range with the Gurmukhi digits.
187 */
188 GURMUKHI ('\u0a66', '\u0a00', '\u0a80'),
189 /**
190 * The Gujarati range with the Gujarati digits.
191 */
192 GUJARATI ('\u0ae6', '\u0b00', '\u0b80'),
193 /**
194 * The Oriya range with the Oriya digits.
195 */
196 ORIYA ('\u0b66', '\u0b00', '\u0b80'),
197 /**
198 * The Tamil range with the Tamil digits.
199 */
200 TAMIL ('\u0be6', '\u0b80', '\u0c00'),
201 /**
202 * The Telugu range with the Telugu digits.
203 */
204 TELUGU ('\u0c66', '\u0c00', '\u0c80'),
205 /**
206 * The Kannada range with the Kannada digits.
207 */
208 KANNADA ('\u0ce6', '\u0c80', '\u0d00'),
209 /**
210 * The Malayalam range with the Malayalam digits.
211 */
212 MALAYALAM ('\u0d66', '\u0d00', '\u0d80'),
213 /**
214 * The Thai range with the Thai digits.
215 */
216 THAI ('\u0e50', '\u0e00', '\u0e80'),
217 /**
218 * The Lao range with the Lao digits.
219 */
220 LAO ('\u0ed0', '\u0e80', '\u0f00'),
221 /**
222 * The Tibetan range with the Tibetan digits.
223 */
224 TIBETAN ('\u0f20', '\u0f00', '\u1000'),
225 /**
226 * The Myanmar range with the Myanmar digits.
227 */
228 MYANMAR ('\u1040', '\u1000', '\u1080'),
229 /**
230 * The Ethiopic range with the Ethiopic digits. Ethiopic
231 * does not have a decimal digit 0 so Latin (European) 0 is
232 * used.
233 */
234 ETHIOPIC ('\u1369', '\u1200', '\u1380') {
235 @Override
236 char getNumericBase() { return 1; }
237 },
238 /**
239 * The Khmer range with the Khmer digits.
240 */
241 KHMER ('\u17e0', '\u1780', '\u1800'),
242 /**
243 * The Mongolian range with the Mongolian digits.
244 */
245 MONGOLIAN ('\u1810', '\u1800', '\u1900'),
246 // The order of EUROPEAN to MOGOLIAN must be consistent
247 // with the bitmask-based constants.
248
249 /**
250 * The N'Ko range with the N'Ko digits.
251 */
252 NKO ('\u07c0', '\u07c0', '\u0800'),
253 /**
254 * The Myanmar range with the Myanmar Shan digits.
255 */
256 MYANMAR_SHAN ('\u1090', '\u1000', '\u10a0'),
257 /**
258 * The Limbu range with the Limbu digits.
259 */
260 LIMBU ('\u1946', '\u1900', '\u1950'),
261 /**
262 * The New Tai Lue range with the New Tai Lue digits.
263 */
264 NEW_TAI_LUE ('\u19d0', '\u1980', '\u19e0'),
265 /**
266 * The Balinese range with the Balinese digits.
267 */
268 BALINESE ('\u1b50', '\u1b00', '\u1b80'),
269 /**
270 * The Sundanese range with the Sundanese digits.
271 */
272 SUNDANESE ('\u1bb0', '\u1b80', '\u1bc0'),
273 /**
274 * The Lepcha range with the Lepcha digits.
275 */
276 LEPCHA ('\u1c40', '\u1c00', '\u1c50'),
277 /**
278 * The Ol Chiki range with the Ol Chiki digits.
279 */
280 OL_CHIKI ('\u1c50', '\u1c50', '\u1c80'),
281 /**
282 * The Vai range with the Vai digits.
283 */
284 VAI ('\ua620', '\ua500', '\ua640'),
285 /**
286 * The Saurashtra range with the Saurashtra digits.
287 */
288 SAURASHTRA ('\ua8d0', '\ua880', '\ua8e0'),
289 /**
290 * The Kayah Li range with the Kayah Li digits.
291 */
292 KAYAH_LI ('\ua900', '\ua900', '\ua930'),
293 /**
294 * The Cham range with the Cham digits.
295 */
296 CHAM ('\uaa50', '\uaa00', '\uaa60'),
297 /**
298 * The Tai Tham Hora range with the Tai Tham Hora digits.
299 */
300 TAI_THAM_HORA ('\u1a80', '\u1a20', '\u1ab0'),
301 /**
302 * The Tai Tham Tham range with the Tai Tham Tham digits.
303 */
304 TAI_THAM_THAM ('\u1a90', '\u1a20', '\u1ab0'),
305 /**
306 * The Javanese range with the Javanese digits.
307 */
308 JAVANESE ('\ua9d0', '\ua980', '\ua9e0'),
309 /**
310 * The Meetei Mayek range with the Meetei Mayek digits.
311 */
312 MEETEI_MAYEK ('\uabf0', '\uabc0', '\uac00');
313
314 private static int toRangeIndex(Range script) {
315 int index = script.ordinal();
316 return index < NUM_KEYS ? index : -1;
317 }
318
319 private static Range indexToRange(int index) {
320 return index < NUM_KEYS ? Range.values()[index] : null;
321 }
322
323 private static int toRangeMask(Set<Range> ranges) {
324 int m = 0;
325 for (Range range : ranges) {
326 int index = range.ordinal();
327 if (index < NUM_KEYS) {
328 m |= 1 << index;
329 }
330 }
331 return m;
332 }
333
334 private static Set<Range> maskToRangeSet(int mask) {
335 Set<Range> set = EnumSet.noneOf(Range.class);
336 Range[] a = Range.values();
337 for (int i = 0; i < NUM_KEYS; i++) {
338 if ((mask & (1 << i)) != 0) {
339 set.add(a[i]);
340 }
341 }
342 return set;
343 }
344
345 // base character of range digits
346 private final int base;
347 // Unicode range
348 private final int start, // inclusive
349 end; // exclusive
350
351 private Range(int base, int start, int end) {
352 this.base = base - ('0' + getNumericBase());
353 this.start = start;
354 this.end = end;
355 }
356
357 private int getDigitBase() {
358 return base;
359 }
360
361 char getNumericBase() {
362 return 0;
363 }
364
365 private boolean inRange(int c) {
366 return start <= c && c < end;
367 }
368 }
369
370 /** index of context for contextual shaping - values range from 0 to 18 */
371 private int key;
372
373 /** flag indicating whether to shape contextually (high bit) and which
374 * digit ranges to shape (bits 0-18)
375 */
376 private int mask;
377
378 /**
379 * The context {@code Range} for contextual shaping or the {@code
380 * Range} for non-contextual shaping. {@code null} for the bit
381 * mask-based API.
382 *
383 * @since 1.7
384 */
385 private Range shapingRange;
386
387 /**
388 * {@code Set<Range>} indicating which Unicode ranges to
389 * shape. {@code null} for the bit mask-based API.
390 */
391 private transient Set<Range> rangeSet;
392
393 /**
394 * rangeSet.toArray() value. Sorted by Range.base when the number
395 * of elements is greater then BSEARCH_THRESHOLD.
396 */
397 private transient Range[] rangeArray;
398
399 /**
400 * If more than BSEARCH_THRESHOLD ranges are specified, binary search is used.
401 */
402 private static final int BSEARCH_THRESHOLD = 3;
403
404 private static final long serialVersionUID = -8022764705923730308L;
405
406 /** Identifies the Latin-1 (European) and extended range, and
407 * Latin-1 (European) decimal base.
408 */
409 public static final int EUROPEAN = 1<<0;
410
411 /** Identifies the ARABIC range and decimal base. */
412 public static final int ARABIC = 1<<1;
413
414 /** Identifies the ARABIC range and ARABIC_EXTENDED decimal base. */
415 public static final int EASTERN_ARABIC = 1<<2;
416
417 /** Identifies the DEVANAGARI range and decimal base. */
418 public static final int DEVANAGARI = 1<<3;
419
420 /** Identifies the BENGALI range and decimal base. */
421 public static final int BENGALI = 1<<4;
422
423 /** Identifies the GURMUKHI range and decimal base. */
424 public static final int GURMUKHI = 1<<5;
425
426 /** Identifies the GUJARATI range and decimal base. */
427 public static final int GUJARATI = 1<<6;
428
429 /** Identifies the ORIYA range and decimal base. */
430 public static final int ORIYA = 1<<7;
431
432 /** Identifies the TAMIL range and decimal base. */
433 // TAMIL DIGIT ZERO was added in Unicode 4.1
434 public static final int TAMIL = 1<<8;
435
436 /** Identifies the TELUGU range and decimal base. */
437 public static final int TELUGU = 1<<9;
438
439 /** Identifies the KANNADA range and decimal base. */
440 public static final int KANNADA = 1<<10;
441
442 /** Identifies the MALAYALAM range and decimal base. */
443 public static final int MALAYALAM = 1<<11;
444
445 /** Identifies the THAI range and decimal base. */
446 public static final int THAI = 1<<12;
447
448 /** Identifies the LAO range and decimal base. */
449 public static final int LAO = 1<<13;
450
451 /** Identifies the TIBETAN range and decimal base. */
452 public static final int TIBETAN = 1<<14;
453
454 /** Identifies the MYANMAR range and decimal base. */
455 public static final int MYANMAR = 1<<15;
456
457 /** Identifies the ETHIOPIC range and decimal base. */
458 public static final int ETHIOPIC = 1<<16;
459
460 /** Identifies the KHMER range and decimal base. */
461 public static final int KHMER = 1<<17;
462
463 /** Identifies the MONGOLIAN range and decimal base. */
464 public static final int MONGOLIAN = 1<<18;
465
466 /** Identifies all ranges, for full contextual shaping.
467 *
468 * <p>This constant specifies all of the bit mask-based
469 * ranges. Use {@code EmunSet.allOf(NumericShaper.Range.class)} to
470 * specify all of the enum-based ranges.
471 */
472 public static final int ALL_RANGES = 0x0007ffff;
473
474 private static final int EUROPEAN_KEY = 0;
475 private static final int ARABIC_KEY = 1;
476 private static final int EASTERN_ARABIC_KEY = 2;
477 private static final int DEVANAGARI_KEY = 3;
478 private static final int BENGALI_KEY = 4;
479 private static final int GURMUKHI_KEY = 5;
480 private static final int GUJARATI_KEY = 6;
481 private static final int ORIYA_KEY = 7;
482 private static final int TAMIL_KEY = 8;
483 private static final int TELUGU_KEY = 9;
484 private static final int KANNADA_KEY = 10;
485 private static final int MALAYALAM_KEY = 11;
486 private static final int THAI_KEY = 12;
487 private static final int LAO_KEY = 13;
488 private static final int TIBETAN_KEY = 14;
489 private static final int MYANMAR_KEY = 15;
490 private static final int ETHIOPIC_KEY = 16;
491 private static final int KHMER_KEY = 17;
492 private static final int MONGOLIAN_KEY = 18;
493
494 private static final int NUM_KEYS = MONGOLIAN_KEY + 1; // fixed
495
496 private static final int CONTEXTUAL_MASK = 1<<31;
497
498 private static final char[] bases = {
499 '\u0030' - '\u0030', // EUROPEAN
500 '\u0660' - '\u0030', // ARABIC-INDIC
501 '\u06f0' - '\u0030', // EXTENDED ARABIC-INDIC (EASTERN_ARABIC)
502 '\u0966' - '\u0030', // DEVANAGARI
503 '\u09e6' - '\u0030', // BENGALI
504 '\u0a66' - '\u0030', // GURMUKHI
505 '\u0ae6' - '\u0030', // GUJARATI
506 '\u0b66' - '\u0030', // ORIYA
507 '\u0be6' - '\u0030', // TAMIL - zero was added in Unicode 4.1
508 '\u0c66' - '\u0030', // TELUGU
509 '\u0ce6' - '\u0030', // KANNADA
510 '\u0d66' - '\u0030', // MALAYALAM
511 '\u0e50' - '\u0030', // THAI
512 '\u0ed0' - '\u0030', // LAO
513 '\u0f20' - '\u0030', // TIBETAN
514 '\u1040' - '\u0030', // MYANMAR
515 '\u1369' - '\u0031', // ETHIOPIC - no zero
516 '\u17e0' - '\u0030', // KHMER
517 '\u1810' - '\u0030', // MONGOLIAN
518 };
519
520 // some ranges adjoin or overlap, rethink if we want to do a binary search on this
521
522 private static final char[] contexts = {
523 '\u0000', '\u0300', // 'EUROPEAN' (really latin-1 and extended)
524 '\u0600', '\u0780', // ARABIC
525 '\u0600', '\u0780', // EASTERN_ARABIC -- note overlap with arabic
526 '\u0900', '\u0980', // DEVANAGARI
527 '\u0980', '\u0a00', // BENGALI
528 '\u0a00', '\u0a80', // GURMUKHI
529 '\u0a80', '\u0b00', // GUJARATI
530 '\u0b00', '\u0b80', // ORIYA
531 '\u0b80', '\u0c00', // TAMIL
532 '\u0c00', '\u0c80', // TELUGU
533 '\u0c80', '\u0d00', // KANNADA
534 '\u0d00', '\u0d80', // MALAYALAM
535 '\u0e00', '\u0e80', // THAI
536 '\u0e80', '\u0f00', // LAO
537 '\u0f00', '\u1000', // TIBETAN
538 '\u1000', '\u1080', // MYANMAR
539 '\u1200', '\u1380', // ETHIOPIC - note missing zero
540 '\u1780', '\u1800', // KHMER
541 '\u1800', '\u1900', // MONGOLIAN
542 '\uffff',
543 };
544
545 // assume most characters are near each other so probing the cache is infrequent,
546 // and a linear probe is ok.
547
548 private static int ctCache = 0;
549 private static int ctCacheLimit = contexts.length - 2;
550
551 // warning, synchronize access to this as it modifies state
552 private static int getContextKey(char c) {
553 if (c < contexts[ctCache]) {
554 while (ctCache > 0 && c < contexts[ctCache]) --ctCache;
555 } else if (c >= contexts[ctCache + 1]) {
556 while (ctCache < ctCacheLimit && c >= contexts[ctCache + 1]) ++ctCache;
557 }
558
559 // if we're not in a known range, then return EUROPEAN as the range key
560 return (ctCache & 0x1) == 0 ? (ctCache / 2) : EUROPEAN_KEY;
561 }
562
563 // cache for the NumericShaper.Range version
564 private transient volatile Range currentRange = Range.EUROPEAN;
565
566 private Range rangeForCodePoint(final int codepoint) {
567 if (currentRange.inRange(codepoint)) {
568 return currentRange;
569 }
570
571 final Range[] ranges = rangeArray;
572 if (ranges.length > BSEARCH_THRESHOLD) {
573 int lo = 0;
574 int hi = ranges.length - 1;
575 while (lo <= hi) {
576 int mid = (lo + hi) / 2;
577 Range range = ranges[mid];
578 if (codepoint < range.start) {
579 hi = mid - 1;
580 } else if (codepoint >= range.end) {
581 lo = mid + 1;
582 } else {
583 currentRange = range;
584 return range;
585 }
586 }
587 } else {
588 for (int i = 0; i < ranges.length; i++) {
589 if (ranges[i].inRange(codepoint)) {
590 return ranges[i];
591 }
592 }
593 }
594 return Range.EUROPEAN;
595 }
596
597 /*
598 * A range table of strong directional characters (types L, R, AL).
599 * Even (left) indexes are starts of ranges of non-strong-directional (or undefined)
600 * characters, odd (right) indexes are starts of ranges of strong directional
601 * characters.
602 */
603 private static int[] strongTable = {
604 0x0000, 0x0041,
605 0x005b, 0x0061,
606 0x007b, 0x00aa,
607 0x00ab, 0x00b5,
608 0x00b6, 0x00ba,
609 0x00bb, 0x00c0,
610 0x00d7, 0x00d8,
611 0x00f7, 0x00f8,
612 0x02b9, 0x02bb,
613 0x02c2, 0x02d0,
614 0x02d2, 0x02e0,
615 0x02e5, 0x02ee,
616 0x02ef, 0x0370,
617 0x0374, 0x0376,
618 0x037e, 0x0386,
619 0x0387, 0x0388,
620 0x03f6, 0x03f7,
621 0x0483, 0x048a,
622 0x058a, 0x05be,
623 0x05bf, 0x05c0,
624 0x05c1, 0x05c3,
625 0x05c4, 0x05c6,
626 0x05c7, 0x05d0,
627 0x0600, 0x0608,
628 0x0609, 0x060b,
629 0x060c, 0x060d,
630 0x060e, 0x061b,
631 0x064b, 0x066d,
632 0x0670, 0x0671,
633 0x06d6, 0x06e5,
634 0x06e7, 0x06ee,
635 0x06f0, 0x06fa,
636 0x070f, 0x0710,
637 0x0711, 0x0712,
638 0x0730, 0x074d,
639 0x07a6, 0x07b1,
640 0x07eb, 0x07f4,
641 0x07f6, 0x07fa,
642 0x0816, 0x081a,
643 0x081b, 0x0824,
644 0x0825, 0x0828,
645 0x0829, 0x0830,
646 0x0859, 0x085e,
647 0x0900, 0x0903,
648 0x093a, 0x093b,
649 0x093c, 0x093d,
650 0x0941, 0x0949,
651 0x094d, 0x094e,
652 0x0951, 0x0958,
653 0x0962, 0x0964,
654 0x0981, 0x0982,
655 0x09bc, 0x09bd,
656 0x09c1, 0x09c7,
657 0x09cd, 0x09ce,
658 0x09e2, 0x09e6,
659 0x09f2, 0x09f4,
660 0x09fb, 0x0a03,
661 0x0a3c, 0x0a3e,
662 0x0a41, 0x0a59,
663 0x0a70, 0x0a72,
664 0x0a75, 0x0a83,
665 0x0abc, 0x0abd,
666 0x0ac1, 0x0ac9,
667 0x0acd, 0x0ad0,
668 0x0ae2, 0x0ae6,
669 0x0af1, 0x0b02,
670 0x0b3c, 0x0b3d,
671 0x0b3f, 0x0b40,
672 0x0b41, 0x0b47,
673 0x0b4d, 0x0b57,
674 0x0b62, 0x0b66,
675 0x0b82, 0x0b83,
676 0x0bc0, 0x0bc1,
677 0x0bcd, 0x0bd0,
678 0x0bf3, 0x0c01,
679 0x0c3e, 0x0c41,
680 0x0c46, 0x0c58,
681 0x0c62, 0x0c66,
682 0x0c78, 0x0c7f,
683 0x0cbc, 0x0cbd,
684 0x0ccc, 0x0cd5,
685 0x0ce2, 0x0ce6,
686 0x0d41, 0x0d46,
687 0x0d4d, 0x0d4e,
688 0x0d62, 0x0d66,
689 0x0dca, 0x0dcf,
690 0x0dd2, 0x0dd8,
691 0x0e31, 0x0e32,
692 0x0e34, 0x0e40,
693 0x0e47, 0x0e4f,
694 0x0eb1, 0x0eb2,
695 0x0eb4, 0x0ebd,
696 0x0ec8, 0x0ed0,
697 0x0f18, 0x0f1a,
698 0x0f35, 0x0f36,
699 0x0f37, 0x0f38,
700 0x0f39, 0x0f3e,
701 0x0f71, 0x0f7f,
702 0x0f80, 0x0f85,
703 0x0f86, 0x0f88,
704 0x0f8d, 0x0fbe,
705 0x0fc6, 0x0fc7,
706 0x102d, 0x1031,
707 0x1032, 0x1038,
708 0x1039, 0x103b,
709 0x103d, 0x103f,
710 0x1058, 0x105a,
711 0x105e, 0x1061,
712 0x1071, 0x1075,
713 0x1082, 0x1083,
714 0x1085, 0x1087,
715 0x108d, 0x108e,
716 0x109d, 0x109e,
717 0x135d, 0x1360,
718 0x1390, 0x13a0,
719 0x1400, 0x1401,
720 0x1680, 0x1681,
721 0x169b, 0x16a0,
722 0x1712, 0x1720,
723 0x1732, 0x1735,
724 0x1752, 0x1760,
725 0x1772, 0x1780,
726 0x17b7, 0x17be,
727 0x17c6, 0x17c7,
728 0x17c9, 0x17d4,
729 0x17db, 0x17dc,
730 0x17dd, 0x17e0,
731 0x17f0, 0x1810,
732 0x18a9, 0x18aa,
733 0x1920, 0x1923,
734 0x1927, 0x1929,
735 0x1932, 0x1933,
736 0x1939, 0x1946,
737 0x19de, 0x1a00,
738 0x1a17, 0x1a19,
739 0x1a56, 0x1a57,
740 0x1a58, 0x1a61,
741 0x1a62, 0x1a63,
742 0x1a65, 0x1a6d,
743 0x1a73, 0x1a80,
744 0x1b00, 0x1b04,
745 0x1b34, 0x1b35,
746 0x1b36, 0x1b3b,
747 0x1b3c, 0x1b3d,
748 0x1b42, 0x1b43,
749 0x1b6b, 0x1b74,
750 0x1b80, 0x1b82,
751 0x1ba2, 0x1ba6,
752 0x1ba8, 0x1baa,
753 0x1be6, 0x1be7,
754 0x1be8, 0x1bea,
755 0x1bed, 0x1bee,
756 0x1bef, 0x1bf2,
757 0x1c2c, 0x1c34,
758 0x1c36, 0x1c3b,
759 0x1cd0, 0x1cd3,
760 0x1cd4, 0x1ce1,
761 0x1ce2, 0x1ce9,
762 0x1ced, 0x1cee,
763 0x1dc0, 0x1e00,
764 0x1fbd, 0x1fbe,
765 0x1fbf, 0x1fc2,
766 0x1fcd, 0x1fd0,
767 0x1fdd, 0x1fe0,
768 0x1fed, 0x1ff2,
769 0x1ffd, 0x200e,
770 0x2010, 0x2071,
771 0x2074, 0x207f,
772 0x2080, 0x2090,
773 0x20a0, 0x2102,
774 0x2103, 0x2107,
775 0x2108, 0x210a,
776 0x2114, 0x2115,
777 0x2116, 0x2119,
778 0x211e, 0x2124,
779 0x2125, 0x2126,
780 0x2127, 0x2128,
781 0x2129, 0x212a,
782 0x212e, 0x212f,
783 0x213a, 0x213c,
784 0x2140, 0x2145,
785 0x214a, 0x214e,
786 0x2150, 0x2160,
787 0x2189, 0x2336,
788 0x237b, 0x2395,
789 0x2396, 0x249c,
790 0x24ea, 0x26ac,
791 0x26ad, 0x2800,
792 0x2900, 0x2c00,
793 0x2ce5, 0x2ceb,
794 0x2cef, 0x2d00,
795 0x2d7f, 0x2d80,
796 0x2de0, 0x3005,
797 0x3008, 0x3021,
798 0x302a, 0x3031,
799 0x3036, 0x3038,
800 0x303d, 0x3041,
801 0x3099, 0x309d,
802 0x30a0, 0x30a1,
803 0x30fb, 0x30fc,
804 0x31c0, 0x31f0,
805 0x321d, 0x3220,
806 0x3250, 0x3260,
807 0x327c, 0x327f,
808 0x32b1, 0x32c0,
809 0x32cc, 0x32d0,
810 0x3377, 0x337b,
811 0x33de, 0x33e0,
812 0x33ff, 0x3400,
813 0x4dc0, 0x4e00,
814 0xa490, 0xa4d0,
815 0xa60d, 0xa610,
816 0xa66f, 0xa680,
817 0xa6f0, 0xa6f2,
818 0xa700, 0xa722,
819 0xa788, 0xa789,
820 0xa802, 0xa803,
821 0xa806, 0xa807,
822 0xa80b, 0xa80c,
823 0xa825, 0xa827,
824 0xa828, 0xa830,
825 0xa838, 0xa840,
826 0xa874, 0xa880,
827 0xa8c4, 0xa8ce,
828 0xa8e0, 0xa8f2,
829 0xa926, 0xa92e,
830 0xa947, 0xa952,
831 0xa980, 0xa983,
832 0xa9b3, 0xa9b4,
833 0xa9b6, 0xa9ba,
834 0xa9bc, 0xa9bd,
835 0xaa29, 0xaa2f,
836 0xaa31, 0xaa33,
837 0xaa35, 0xaa40,
838 0xaa43, 0xaa44,
839 0xaa4c, 0xaa4d,
840 0xaab0, 0xaab1,
841 0xaab2, 0xaab5,
842 0xaab7, 0xaab9,
843 0xaabe, 0xaac0,
844 0xaac1, 0xaac2,
845 0xabe5, 0xabe6,
846 0xabe8, 0xabe9,
847 0xabed, 0xabf0,
848 0xfb1e, 0xfb1f,
849 0xfb29, 0xfb2a,
850 0xfd3e, 0xfd50,
851 0xfdfd, 0xfe70,
852 0xfeff, 0xff21,
853 0xff3b, 0xff41,
854 0xff5b, 0xff66,
855 0xffe0, 0x10000,
856 0x10101, 0x10102,
857 0x10140, 0x101d0,
858 0x101fd, 0x10280,
859 0x1091f, 0x10920,
860 0x10a01, 0x10a10,
861 0x10a38, 0x10a40,
862 0x10b39, 0x10b40,
863 0x10e60, 0x11000,
864 0x11001, 0x11002,
865 0x11038, 0x11047,
866 0x11052, 0x11066,
867 0x11080, 0x11082,
868 0x110b3, 0x110b7,
869 0x110b9, 0x110bb,
870 0x1d167, 0x1d16a,
871 0x1d173, 0x1d183,
872 0x1d185, 0x1d18c,
873 0x1d1aa, 0x1d1ae,
874 0x1d200, 0x1d360,
875 0x1d6db, 0x1d6dc,
876 0x1d715, 0x1d716,
877 0x1d74f, 0x1d750,
878 0x1d789, 0x1d78a,
879 0x1d7c3, 0x1d7c4,
880 0x1d7ce, 0x1f110,
881 0x1f300, 0x1f48c,
882 0x1f48d, 0x1f524,
883 0x1f525, 0x20000,
884 0xe0001, 0xf0000,
885 0x10fffe, 0x10ffff // sentinel
886 };
887
888
889 // use a binary search with a cache
890
891 private transient volatile int stCache = 0;
892
893 private boolean isStrongDirectional(char c) {
894 int cachedIndex = stCache;
895 if (c < strongTable[cachedIndex]) {
896 cachedIndex = search(c, strongTable, 0, cachedIndex);
897 } else if (c >= strongTable[cachedIndex + 1]) {
898 cachedIndex = search(c, strongTable, cachedIndex + 1,
899 strongTable.length - cachedIndex - 1);
900 }
901 boolean val = (cachedIndex & 0x1) == 1;
902 stCache = cachedIndex;
903 return val;
904 }
905
906 private static int getKeyFromMask(int mask) {
907 int key = 0;
908 while (key < NUM_KEYS && ((mask & (1<<key)) == 0)) {
909 ++key;
910 }
911 if (key == NUM_KEYS || ((mask & ~(1<<key)) != 0)) {
912 throw new IllegalArgumentException("invalid shaper: " + Integer.toHexString(mask));
913 }
914 return key;
915 }
916
917 /**
918 * Returns a shaper for the provided unicode range. All
919 * Latin-1 (EUROPEAN) digits are converted
920 * to the corresponding decimal unicode digits.
921 * @param singleRange the specified Unicode range
922 * @return a non-contextual numeric shaper
923 * @throws IllegalArgumentException if the range is not a single range
924 */
925 public static NumericShaper getShaper(int singleRange) {
926 int key = getKeyFromMask(singleRange);
927 return new NumericShaper(key, singleRange);
928 }
929
930 /**
931 * Returns a shaper for the provided Unicode
932 * range. All Latin-1 (EUROPEAN) digits are converted to the
933 * corresponding decimal digits of the specified Unicode range.
934 *
935 * @param singleRange the Unicode range given by a {@link
936 * NumericShaper.Range} constant.
937 * @return a non-contextual {@code NumericShaper}.
938 * @throws NullPointerException if {@code singleRange} is {@code null}
939 * @since 1.7
940 */
941 public static NumericShaper getShaper(Range singleRange) {
942 return new NumericShaper(singleRange, EnumSet.of(singleRange));
943 }
944
945 /**
946 * Returns a contextual shaper for the provided unicode range(s).
947 * Latin-1 (EUROPEAN) digits are converted to the decimal digits
948 * corresponding to the range of the preceding text, if the
949 * range is one of the provided ranges. Multiple ranges are
950 * represented by or-ing the values together, such as,
951 * <code>NumericShaper.ARABIC | NumericShaper.THAI</code>. The
952 * shaper assumes EUROPEAN as the starting context, that is, if
953 * EUROPEAN digits are encountered before any strong directional
954 * text in the string, the context is presumed to be EUROPEAN, and
955 * so the digits will not shape.
956 * @param ranges the specified Unicode ranges
957 * @return a shaper for the specified ranges
958 */
959 public static NumericShaper getContextualShaper(int ranges) {
960 ranges |= CONTEXTUAL_MASK;
961 return new NumericShaper(EUROPEAN_KEY, ranges);
962 }
963
964 /**
965 * Returns a contextual shaper for the provided Unicode
966 * range(s). The Latin-1 (EUROPEAN) digits are converted to the
967 * decimal digits corresponding to the range of the preceding
968 * text, if the range is one of the provided ranges.
969 *
970 * <p>The shaper assumes EUROPEAN as the starting context, that
971 * is, if EUROPEAN digits are encountered before any strong
972 * directional text in the string, the context is presumed to be
973 * EUROPEAN, and so the digits will not shape.
974 *
975 * @param ranges the specified Unicode ranges
976 * @return a contextual shaper for the specified ranges
977 * @throws NullPointerException if {@code ranges} is {@code null}.
978 * @since 1.7
979 */
980 public static NumericShaper getContextualShaper(Set<Range> ranges) {
981 NumericShaper shaper = new NumericShaper(Range.EUROPEAN, ranges);
982 shaper.mask = CONTEXTUAL_MASK;
983 return shaper;
984 }
985
986 /**
987 * Returns a contextual shaper for the provided unicode range(s).
988 * Latin-1 (EUROPEAN) digits will be converted to the decimal digits
989 * corresponding to the range of the preceding text, if the
990 * range is one of the provided ranges. Multiple ranges are
991 * represented by or-ing the values together, for example,
992 * <code>NumericShaper.ARABIC | NumericShaper.THAI</code>. The
993 * shaper uses defaultContext as the starting context.
994 * @param ranges the specified Unicode ranges
995 * @param defaultContext the starting context, such as
996 * <code>NumericShaper.EUROPEAN</code>
997 * @return a shaper for the specified Unicode ranges.
998 * @throws IllegalArgumentException if the specified
999 * <code>defaultContext</code> is not a single valid range.
1000 */
1001 public static NumericShaper getContextualShaper(int ranges, int defaultContext) {
1002 int key = getKeyFromMask(defaultContext);
1003 ranges |= CONTEXTUAL_MASK;
1004 return new NumericShaper(key, ranges);
1005 }
1006
1007 /**
1008 * Returns a contextual shaper for the provided Unicode range(s).
1009 * The Latin-1 (EUROPEAN) digits will be converted to the decimal
1010 * digits corresponding to the range of the preceding text, if the
1011 * range is one of the provided ranges. The shaper uses {@code
1012 * defaultContext} as the starting context.
1013 *
1014 * @param ranges the specified Unicode ranges
1015 * @param defaultContext the starting context, such as
1016 * {@code NumericShaper.Range.EUROPEAN}
1017 * @return a contextual shaper for the specified Unicode ranges.
1018 * @throws NullPointerException
1019 * if {@code ranges} or {@code defaultContext} is {@code null}
1020 * @since 1.7
1021 */
1022 public static NumericShaper getContextualShaper(Set<Range> ranges,
1023 Range defaultContext) {
1024 if (defaultContext == null) {
1025 throw new NullPointerException();
1026 }
1027 NumericShaper shaper = new NumericShaper(defaultContext, ranges);
1028 shaper.mask = CONTEXTUAL_MASK;
1029 return shaper;
1030 }
1031
1032 /**
1033 * Private constructor.
1034 */
1035 private NumericShaper(int key, int mask) {
1036 this.key = key;
1037 this.mask = mask;
1038 }
1039
1040 private NumericShaper(Range defaultContext, Set<Range> ranges) {
1041 shapingRange = defaultContext;
1042 rangeSet = EnumSet.copyOf(ranges); // throws NPE if ranges is null.
1043
1044 // Give precedance to EASTERN_ARABIC if both ARABIC and
1045 // EASTERN_ARABIC are specified.
1046 if (rangeSet.contains(Range.EASTERN_ARABIC)
1047 && rangeSet.contains(Range.ARABIC)) {
1048 rangeSet.remove(Range.ARABIC);
1049 }
1050
1051 // As well as the above case, give precedance to TAI_THAM_THAM if both
1052 // TAI_THAM_HORA and TAI_THAM_THAM are specified.
1053 if (rangeSet.contains(Range.TAI_THAM_THAM)
1054 && rangeSet.contains(Range.TAI_THAM_HORA)) {
1055 rangeSet.remove(Range.TAI_THAM_HORA);
1056 }
1057
1058 rangeArray = rangeSet.toArray(new Range[rangeSet.size()]);
1059 if (rangeArray.length > BSEARCH_THRESHOLD) {
1060 // sort rangeArray for binary search
1061 Arrays.sort(rangeArray,
1062 new Comparator<Range>() {
1063 public int compare(Range s1, Range s2) {
1064 return s1.base > s2.base ? 1 : s1.base == s2.base ? 0 : -1;
1065 }
1066 });
1067 }
1068 }
1069
1070 /**
1071 * Converts the digits in the text that occur between start and
1072 * start + count.
1073 * @param text an array of characters to convert
1074 * @param start the index into <code>text</code> to start
1075 * converting
1076 * @param count the number of characters in <code>text</code>
1077 * to convert
1078 * @throws IndexOutOfBoundsException if start or start + count is
1079 * out of bounds
1080 * @throws NullPointerException if text is null
1081 */
1082 public void shape(char[] text, int start, int count) {
1083 checkParams(text, start, count);
1084 if (isContextual()) {
1085 if (rangeSet == null) {
1086 shapeContextually(text, start, count, key);
1087 } else {
1088 shapeContextually(text, start, count, shapingRange);
1089 }
1090 } else {
1091 shapeNonContextually(text, start, count);
1092 }
1093 }
1094
1095 /**
1096 * Converts the digits in the text that occur between start and
1097 * start + count, using the provided context.
1098 * Context is ignored if the shaper is not a contextual shaper.
1099 * @param text an array of characters
1100 * @param start the index into <code>text</code> to start
1101 * converting
1102 * @param count the number of characters in <code>text</code>
1103 * to convert
1104 * @param context the context to which to convert the
1105 * characters, such as <code>NumericShaper.EUROPEAN</code>
1106 * @throws IndexOutOfBoundsException if start or start + count is
1107 * out of bounds
1108 * @throws NullPointerException if text is null
1109 * @throws IllegalArgumentException if this is a contextual shaper
1110 * and the specified <code>context</code> is not a single valid
1111 * range.
1112 */
1113 public void shape(char[] text, int start, int count, int context) {
1114 checkParams(text, start, count);
1115 if (isContextual()) {
1116 int ctxKey = getKeyFromMask(context);
1117 if (rangeSet == null) {
1118 shapeContextually(text, start, count, ctxKey);
1119 } else {
1120 shapeContextually(text, start, count, Range.values()[ctxKey]);
1121 }
1122 } else {
1123 shapeNonContextually(text, start, count);
1124 }
1125 }
1126
1127 /**
1128 * Converts the digits in the text that occur between {@code
1129 * start} and {@code start + count}, using the provided {@code
1130 * context}. {@code Context} is ignored if the shaper is not a
1131 * contextual shaper.
1132 *
1133 * @param text a {@code char} array
1134 * @param start the index into {@code text} to start converting
1135 * @param count the number of {@code char}s in {@code text}
1136 * to convert
1137 * @param context the context to which to convert the characters,
1138 * such as {@code NumericShaper.Range.EUROPEAN}
1139 * @throws IndexOutOfBoundsException
1140 * if {@code start} or {@code start + count} is out of bounds
1141 * @throws NullPointerException
1142 * if {@code text} or {@code context} is null
1143 * @since 1.7
1144 */
1145 public void shape(char[] text, int start, int count, Range context) {
1146 checkParams(text, start, count);
1147 if (context == null) {
1148 throw new NullPointerException("context is null");
1149 }
1150
1151 if (isContextual()) {
1152 if (rangeSet != null) {
1153 shapeContextually(text, start, count, context);
1154 } else {
1155 int key = Range.toRangeIndex(context);
1156 if (key >= 0) {
1157 shapeContextually(text, start, count, key);
1158 } else {
1159 shapeContextually(text, start, count, shapingRange);
1160 }
1161 }
1162 } else {
1163 shapeNonContextually(text, start, count);
1164 }
1165 }
1166
1167 private void checkParams(char[] text, int start, int count) {
1168 if (text == null) {
1169 throw new NullPointerException("text is null");
1170 }
1171 if ((start < 0)
1172 || (start > text.length)
1173 || ((start + count) < 0)
1174 || ((start + count) > text.length)) {
1175 throw new IndexOutOfBoundsException(
1176 "bad start or count for text of length " + text.length);
1177 }
1178 }
1179
1180 /**
1181 * Returns a <code>boolean</code> indicating whether or not
1182 * this shaper shapes contextually.
1183 * @return <code>true</code> if this shaper is contextual;
1184 * <code>false</code> otherwise.
1185 */
1186 public boolean isContextual() {
1187 return (mask & CONTEXTUAL_MASK) != 0;
1188 }
1189
1190 /**
1191 * Returns an <code>int</code> that ORs together the values for
1192 * all the ranges that will be shaped.
1193 * <p>
1194 * For example, to check if a shaper shapes to Arabic, you would use the
1195 * following:
1196 * <blockquote>
1197 * <code>if ((shaper.getRanges() & shaper.ARABIC) != 0) { ... </code>
1198 * </blockquote>
1199 *
1200 * <p>Note that this method supports only the bit mask-based
1201 * ranges. Call {@link #getRangeSet()} for the enum-based ranges.
1202 *
1203 * @return the values for all the ranges to be shaped.
1204 */
1205 public int getRanges() {
1206 return mask & ~CONTEXTUAL_MASK;
1207 }
1208
1209 /**
1210 * Returns a {@code Set} representing all the Unicode ranges in
1211 * this {@code NumericShaper} that will be shaped.
1212 *
1213 * @return all the Unicode ranges to be shaped.
1214 * @since 1.7
1215 */
1216 public Set<Range> getRangeSet() {
1217 if (rangeSet != null) {
1218 return EnumSet.copyOf(rangeSet);
1219 }
1220 return Range.maskToRangeSet(mask);
1221 }
1222
1223 /**
1224 * Perform non-contextual shaping.
1225 */
1226 private void shapeNonContextually(char[] text, int start, int count) {
1227 int base;
1228 char minDigit = '0';
1229 if (shapingRange != null) {
1230 base = shapingRange.getDigitBase();
1231 minDigit += shapingRange.getNumericBase();
1232 } else {
1233 base = bases[key];
1234 if (key == ETHIOPIC_KEY) {
1235 minDigit++; // Ethiopic doesn't use decimal zero
1236 }
1237 }
1238 for (int i = start, e = start + count; i < e; ++i) {
1239 char c = text[i];
1240 if (c >= minDigit && c <= '\u0039') {
1241 text[i] = (char)(c + base);
1242 }
1243 }
1244 }
1245
1246 /**
1247 * Perform contextual shaping.
1248 * Synchronized to protect caches used in getContextKey.
1249 */
1250 private synchronized void shapeContextually(char[] text, int start, int count, int ctxKey) {
1251
1252 // if we don't support this context, then don't shape
1253 if ((mask & (1<<ctxKey)) == 0) {
1254 ctxKey = EUROPEAN_KEY;
1255 }
1256 int lastkey = ctxKey;
1257
1258 int base = bases[ctxKey];
1259 char minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero
1260
1261 synchronized (NumericShaper.class) {
1262 for (int i = start, e = start + count; i < e; ++i) {
1263 char c = text[i];
1264 if (c >= minDigit && c <= '\u0039') {
1265 text[i] = (char)(c + base);
1266 }
1267
1268 if (isStrongDirectional(c)) {
1269 int newkey = getContextKey(c);
1270 if (newkey != lastkey) {
1271 lastkey = newkey;
1272
1273 ctxKey = newkey;
1274 if (((mask & EASTERN_ARABIC) != 0) &&
1275 (ctxKey == ARABIC_KEY ||
1276 ctxKey == EASTERN_ARABIC_KEY)) {
1277 ctxKey = EASTERN_ARABIC_KEY;
1278 } else if (((mask & ARABIC) != 0) &&
1279 (ctxKey == ARABIC_KEY ||
1280 ctxKey == EASTERN_ARABIC_KEY)) {
1281 ctxKey = ARABIC_KEY;
1282 } else if ((mask & (1<<ctxKey)) == 0) {
1283 ctxKey = EUROPEAN_KEY;
1284 }
1285
1286 base = bases[ctxKey];
1287
1288 minDigit = ctxKey == ETHIOPIC_KEY ? '1' : '0'; // Ethiopic doesn't use decimal zero
1289 }
1290 }
1291 }
1292 }
1293 }
1294
1295 private void shapeContextually(char[] text, int start, int count, Range ctxKey) {
1296 // if we don't support the specified context, then don't shape.
1297 if (ctxKey == null || !rangeSet.contains(ctxKey)) {
1298 ctxKey = Range.EUROPEAN;
1299 }
1300
1301 Range lastKey = ctxKey;
1302 int base = ctxKey.getDigitBase();
1303 char minDigit = (char)('0' + ctxKey.getNumericBase());
1304 final int end = start + count;
1305 for (int i = start; i < end; ++i) {
1306 char c = text[i];
1307 if (c >= minDigit && c <= '9') {
1308 text[i] = (char)(c + base);
1309 continue;
1310 }
1311 if (isStrongDirectional(c)) {
1312 ctxKey = rangeForCodePoint(c);
1313 if (ctxKey != lastKey) {
1314 lastKey = ctxKey;
1315 base = ctxKey.getDigitBase();
1316 minDigit = (char)('0' + ctxKey.getNumericBase());
1317 }
1318 }
1319 }
1320 }
1321
1322 /**
1323 * Returns a hash code for this shaper.
1324 * @return this shaper's hash code.
1325 * @see java.lang.Object#hashCode
1326 */
1327 public int hashCode() {
1328 int hash = mask;
1329 if (rangeSet != null) {
1330 // Use the CONTEXTUAL_MASK bit only for the enum-based
1331 // NumericShaper. A deserialized NumericShaper might have
1332 // bit masks.
1333 hash &= CONTEXTUAL_MASK;
1334 hash ^= rangeSet.hashCode();
1335 }
1336 return hash;
1337 }
1338
1339 /**
1340 * Returns {@code true} if the specified object is an instance of
1341 * <code>NumericShaper</code> and shapes identically to this one,
1342 * regardless of the range representations, the bit mask or the
1343 * enum. For example, the following code produces {@code "true"}.
1344 * <blockquote><pre>
1345 * NumericShaper ns1 = NumericShaper.getShaper(NumericShaper.ARABIC);
1346 * NumericShaper ns2 = NumericShaper.getShaper(NumericShaper.Range.ARABIC);
1347 * System.out.println(ns1.equals(ns2));
1348 * </pre></blockquote>
1349 *
1350 * @param o the specified object to compare to this
1351 * <code>NumericShaper</code>
1352 * @return <code>true</code> if <code>o</code> is an instance
1353 * of <code>NumericShaper</code> and shapes in the same way;
1354 * <code>false</code> otherwise.
1355 * @see java.lang.Object#equals(java.lang.Object)
1356 */
1357 public boolean equals(Object o) {
1358 if (o != null) {
1359 try {
1360 NumericShaper rhs = (NumericShaper)o;
1361 if (rangeSet != null) {
1362 if (rhs.rangeSet != null) {
1363 return isContextual() == rhs.isContextual()
1364 && rangeSet.equals(rhs.rangeSet)
1365 && shapingRange == rhs.shapingRange;
1366 }
1367 return isContextual() == rhs.isContextual()
1368 && rangeSet.equals(Range.maskToRangeSet(rhs.mask))
1369 && shapingRange == Range.indexToRange(rhs.key);
1370 } else if (rhs.rangeSet != null) {
1371 Set<Range> rset = Range.maskToRangeSet(mask);
1372 Range srange = Range.indexToRange(key);
1373 return isContextual() == rhs.isContextual()
1374 && rset.equals(rhs.rangeSet)
1375 && srange == rhs.shapingRange;
1376 }
1377 return rhs.mask == mask && rhs.key == key;
1378 }
1379 catch (ClassCastException e) {
1380 }
1381 }
1382 return false;
1383 }
1384
1385 /**
1386 * Returns a <code>String</code> that describes this shaper. This method
1387 * is used for debugging purposes only.
1388 * @return a <code>String</code> describing this shaper.
1389 */
1390 public String toString() {
1391 StringBuilder buf = new StringBuilder(super.toString());
1392
1393 buf.append("[contextual:").append(isContextual());
1394
1395 String[] keyNames = null;
1396 if (isContextual()) {
1397 buf.append(", context:");
1398 buf.append(shapingRange == null ? Range.values()[key] : shapingRange);
1399 }
1400
1401 if (rangeSet == null) {
1402 buf.append(", range(s): ");
1403 boolean first = true;
1404 for (int i = 0; i < NUM_KEYS; ++i) {
1405 if ((mask & (1 << i)) != 0) {
1406 if (first) {
1407 first = false;
1408 } else {
1409 buf.append(", ");
1410 }
1411 buf.append(Range.values()[i]);
1412 }
1413 }
1414 } else {
1415 buf.append(", range set: ").append(rangeSet);
1416 }
1417 buf.append(']');
1418
1419 return buf.toString();
1420 }
1421
1422 /**
1423 * Returns the index of the high bit in value (assuming le, actually
1424 * power of 2 >= value). value must be positive.
1425 */
1426 private static int getHighBit(int value) {
1427 if (value <= 0) {
1428 return -32;
1429 }
1430
1431 int bit = 0;
1432
1433 if (value >= 1 << 16) {
1434 value >>= 16;
1435 bit += 16;
1436 }
1437
1438 if (value >= 1 << 8) {
1439 value >>= 8;
1440 bit += 8;
1441 }
1442
1443 if (value >= 1 << 4) {
1444 value >>= 4;
1445 bit += 4;
1446 }
1447
1448 if (value >= 1 << 2) {
1449 value >>= 2;
1450 bit += 2;
1451 }
1452
1453 if (value >= 1 << 1) {
1454 bit += 1;
1455 }
1456
1457 return bit;
1458 }
1459
1460 /**
1461 * fast binary search over subrange of array.
1462 */
1463 private static int search(int value, int[] array, int start, int length)
1464 {
1465 int power = 1 << getHighBit(length);
1466 int extra = length - power;
1467 int probe = power;
1468 int index = start;
1469
1470 if (value >= array[index + extra]) {
1471 index += extra;
1472 }
1473
1474 while (probe > 1) {
1475 probe >>= 1;
1476
1477 if (value >= array[index + probe]) {
1478 index += probe;
1479 }
1480 }
1481
1482 return index;
1483 }
1484
1485 /**
1486 * Converts the {@code NumericShaper.Range} enum-based parameters,
1487 * if any, to the bit mask-based counterparts and writes this
1488 * object to the {@code stream}. Any enum constants that have no
1489 * bit mask-based counterparts are ignored in the conversion.
1490 *
1491 * @param stream the output stream to write to
1492 * @throws IOException if an I/O error occurs while writing to {@code stream}
1493 * @since 1.7
1494 */
1495 private void writeObject(ObjectOutputStream stream) throws IOException {
1496 if (shapingRange != null) {
1497 int index = Range.toRangeIndex(shapingRange);
1498 if (index >= 0) {
1499 key = index;
1500 }
1501 }
1502 if (rangeSet != null) {
1503 mask |= Range.toRangeMask(rangeSet);
1504 }
1505 stream.defaultWriteObject();
1506 }
1507}
java/pkg/java/awt/font/OpenType.class+0−0

Binary file not shown.

java/pkg/java/awt/font/OpenType.java+432−0
@@ -0,0 +1,432 @@
1/*
2 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.awt.font;
27
28/**
29 * The <code>OpenType</code> interface represents OpenType and
30 * TrueType fonts. This interface makes it possible to obtain
31 * <i>sfnt</i> tables from the font. A particular
32 * <code>Font</code> object can implement this interface.
33 * <p>
34 * For more information on TrueType and OpenType fonts, see the
35 * OpenType specification.
36 * ( <a href=http://www.microsoft.com/typography/otspec/">http://www.microsoft.com/typography/otspec/l</a> ).
37 */
38public interface OpenType {
39
40 /* 51 tag types so far */
41
42 /**
43 * Character to glyph mapping. Table tag "cmap" in the Open
44 * Type Specification.
45 */
46 public final static int TAG_CMAP = 0x636d6170;
47
48 /**
49 * Font header. Table tag "head" in the Open
50 * Type Specification.
51 */
52 public final static int TAG_HEAD = 0x68656164;
53
54 /**
55 * Naming table. Table tag "name" in the Open
56 * Type Specification.
57 */
58 public final static int TAG_NAME = 0x6e616d65;
59
60 /**
61 * Glyph data. Table tag "glyf" in the Open
62 * Type Specification.
63 */
64 public final static int TAG_GLYF = 0x676c7966;
65
66 /**
67 * Maximum profile. Table tag "maxp" in the Open
68 * Type Specification.
69 */
70 public final static int TAG_MAXP = 0x6d617870;
71
72 /**
73 * CVT preprogram. Table tag "prep" in the Open
74 * Type Specification.
75 */
76 public final static int TAG_PREP = 0x70726570;
77
78 /**
79 * Horizontal metrics. Table tag "hmtx" in the Open
80 * Type Specification.
81 */
82 public final static int TAG_HMTX = 0x686d7478;
83
84 /**
85 * Kerning. Table tag "kern" in the Open
86 * Type Specification.
87 */
88 public final static int TAG_KERN = 0x6b65726e;
89
90 /**
91 * Horizontal device metrics. Table tag "hdmx" in the Open
92 * Type Specification.
93 */
94 public final static int TAG_HDMX = 0x68646d78;
95
96 /**
97 * Index to location. Table tag "loca" in the Open
98 * Type Specification.
99 */
100 public final static int TAG_LOCA = 0x6c6f6361;
101
102 /**
103 * PostScript Information. Table tag "post" in the Open
104 * Type Specification.
105 */
106 public final static int TAG_POST = 0x706f7374;
107
108 /**
109 * OS/2 and Windows specific metrics. Table tag "OS/2"
110 * in the Open Type Specification.
111 */
112 public final static int TAG_OS2 = 0x4f532f32;
113
114 /**
115 * Control value table. Table tag "cvt "
116 * in the Open Type Specification.
117 */
118 public final static int TAG_CVT = 0x63767420;
119
120 /**
121 * Grid-fitting and scan conversion procedure. Table tag
122 * "gasp" in the Open Type Specification.
123 */
124 public final static int TAG_GASP = 0x67617370;
125
126 /**
127 * Vertical device metrics. Table tag "VDMX" in the Open
128 * Type Specification.
129 */
130 public final static int TAG_VDMX = 0x56444d58;
131
132 /**
133 * Vertical metrics. Table tag "vmtx" in the Open
134 * Type Specification.
135 */
136 public final static int TAG_VMTX = 0x766d7478;
137
138 /**
139 * Vertical metrics header. Table tag "vhea" in the Open
140 * Type Specification.
141 */
142 public final static int TAG_VHEA = 0x76686561;
143
144 /**
145 * Horizontal metrics header. Table tag "hhea" in the Open
146 * Type Specification.
147 */
148 public final static int TAG_HHEA = 0x68686561;
149
150 /**
151 * Adobe Type 1 font data. Table tag "typ1" in the Open
152 * Type Specification.
153 */
154 public final static int TAG_TYP1 = 0x74797031;
155
156 /**
157 * Baseline table. Table tag "bsln" in the Open
158 * Type Specification.
159 */
160 public final static int TAG_BSLN = 0x62736c6e;
161
162 /**
163 * Glyph substitution. Table tag "GSUB" in the Open
164 * Type Specification.
165 */
166 public final static int TAG_GSUB = 0x47535542;
167
168 /**
169 * Digital signature. Table tag "DSIG" in the Open
170 * Type Specification.
171 */
172 public final static int TAG_DSIG = 0x44534947;
173
174 /**
175 * Font program. Table tag "fpgm" in the Open
176 * Type Specification.
177 */
178 public final static int TAG_FPGM = 0x6670676d;
179
180 /**
181 * Font variation. Table tag "fvar" in the Open
182 * Type Specification.
183 */
184 public final static int TAG_FVAR = 0x66766172;
185
186 /**
187 * Glyph variation. Table tag "gvar" in the Open
188 * Type Specification.
189 */
190 public final static int TAG_GVAR = 0x67766172;
191
192 /**
193 * Compact font format (Type1 font). Table tag
194 * "CFF " in the Open Type Specification.
195 */
196 public final static int TAG_CFF = 0x43464620;
197
198 /**
199 * Multiple master supplementary data. Table tag
200 * "MMSD" in the Open Type Specification.
201 */
202 public final static int TAG_MMSD = 0x4d4d5344;
203
204 /**
205 * Multiple master font metrics. Table tag
206 * "MMFX" in the Open Type Specification.
207 */
208 public final static int TAG_MMFX = 0x4d4d4658;
209
210 /**
211 * Baseline data. Table tag "BASE" in the Open
212 * Type Specification.
213 */
214 public final static int TAG_BASE = 0x42415345;
215
216 /**
217 * Glyph definition. Table tag "GDEF" in the Open
218 * Type Specification.
219 */
220 public final static int TAG_GDEF = 0x47444546;
221
222 /**
223 * Glyph positioning. Table tag "GPOS" in the Open
224 * Type Specification.
225 */
226 public final static int TAG_GPOS = 0x47504f53;
227
228 /**
229 * Justification. Table tag "JSTF" in the Open
230 * Type Specification.
231 */
232 public final static int TAG_JSTF = 0x4a535446;
233
234 /**
235 * Embedded bitmap data. Table tag "EBDT" in the Open
236 * Type Specification.
237 */
238 public final static int TAG_EBDT = 0x45424454;
239
240 /**
241 * Embedded bitmap location. Table tag "EBLC" in the Open
242 * Type Specification.
243 */
244 public final static int TAG_EBLC = 0x45424c43;
245
246 /**
247 * Embedded bitmap scaling. Table tag "EBSC" in the Open
248 * Type Specification.
249 */
250 public final static int TAG_EBSC = 0x45425343;
251
252 /**
253 * Linear threshold. Table tag "LTSH" in the Open
254 * Type Specification.
255 */
256 public final static int TAG_LTSH = 0x4c545348;
257
258 /**
259 * PCL 5 data. Table tag "PCLT" in the Open
260 * Type Specification.
261 */
262 public final static int TAG_PCLT = 0x50434c54;
263
264 /**
265 * Accent attachment. Table tag "acnt" in the Open
266 * Type Specification.
267 */
268 public final static int TAG_ACNT = 0x61636e74;
269
270 /**
271 * Axis variaiton. Table tag "avar" in the Open
272 * Type Specification.
273 */
274 public final static int TAG_AVAR = 0x61766172;
275
276 /**
277 * Bitmap data. Table tag "bdat" in the Open
278 * Type Specification.
279 */
280 public final static int TAG_BDAT = 0x62646174;
281
282 /**
283 * Bitmap location. Table tag "bloc" in the Open
284 * Type Specification.
285 */
286 public final static int TAG_BLOC = 0x626c6f63;
287
288 /**
289 * CVT variation. Table tag "cvar" in the Open
290 * Type Specification.
291 */
292 public final static int TAG_CVAR = 0x63766172;
293
294 /**
295 * Feature name. Table tag "feat" in the Open
296 * Type Specification.
297 */
298 public final static int TAG_FEAT = 0x66656174;
299
300 /**
301 * Font descriptors. Table tag "fdsc" in the Open
302 * Type Specification.
303 */
304 public final static int TAG_FDSC = 0x66647363;
305
306 /**
307 * Font metrics. Table tag "fmtx" in the Open
308 * Type Specification.
309 */
310 public final static int TAG_FMTX = 0x666d7478;
311
312 /**
313 * Justification. Table tag "just" in the Open
314 * Type Specification.
315 */
316 public final static int TAG_JUST = 0x6a757374;
317
318 /**
319 * Ligature caret. Table tag "lcar" in the Open
320 * Type Specification.
321 */
322 public final static int TAG_LCAR = 0x6c636172;
323
324 /**
325 * Glyph metamorphosis. Table tag "mort" in the Open
326 * Type Specification.
327 */
328 public final static int TAG_MORT = 0x6d6f7274;
329
330 /**
331 * Optical bounds. Table tag "opbd" in the Open
332 * Type Specification.
333 */
334 public final static int TAG_OPBD = 0x6d6f7274;
335
336 /**
337 * Glyph properties. Table tag "prop" in the Open
338 * Type Specification.
339 */
340 public final static int TAG_PROP = 0x70726f70;
341
342 /**
343 * Tracking. Table tag "trak" in the Open
344 * Type Specification.
345 */
346 public final static int TAG_TRAK = 0x7472616b;
347
348 /**
349 * Returns the version of the <code>OpenType</code> font.
350 * 1.0 is represented as 0x00010000.
351 * @return the version of the <code>OpenType</code> font.
352 */
353 public int getVersion();
354
355 /**
356 * Returns the table as an array of bytes for a specified tag.
357 * Tags for sfnt tables include items like <i>cmap</i>,
358 * <i>name</i> and <i>head</i>. The <code>byte</code> array
359 * returned is a copy of the font data in memory.
360 * @param sfntTag a four-character code as a 32-bit integer
361 * @return a <code>byte</code> array that is the table that
362 * contains the font data corresponding to the specified
363 * tag.
364 */
365 public byte[] getFontTable(int sfntTag);
366
367 /**
368 * Returns the table as an array of bytes for a specified tag.
369 * Tags for sfnt tables include items like <i>cmap</i>,
370 * <i>name</i> and <i>head</i>. The byte array returned is a
371 * copy of the font data in memory.
372 * @param strSfntTag a four-character code as a
373 * <code>String</code>
374 * @return a <code>byte</code> array that is the table that
375 * contains the font data corresponding to the specified
376 * tag.
377 */
378 public byte[] getFontTable(String strSfntTag);
379
380 /**
381 * Returns a subset of the table as an array of bytes
382 * for a specified tag. Tags for sfnt tables include
383 * items like <i>cmap</i>, <i>name</i> and <i>head</i>.
384 * The byte array returned is a copy of the font data in
385 * memory.
386 * @param sfntTag a four-character code as a 32-bit integer
387 * @param offset index of first byte to return from table
388 * @param count number of bytes to return from table
389 * @return a subset of the table corresponding to
390 * <code>sfntTag</code> and containing the bytes
391 * starting at <code>offset</code> byte and including
392 * <code>count</code> bytes.
393 */
394 public byte[] getFontTable(int sfntTag, int offset, int count);
395
396 /**
397 * Returns a subset of the table as an array of bytes
398 * for a specified tag. Tags for sfnt tables include items
399 * like <i>cmap</i>, <i>name</i> and <i>head</i>. The
400 * <code>byte</code> array returned is a copy of the font
401 * data in memory.
402 * @param strSfntTag a four-character code as a
403 * <code>String</code>
404 * @param offset index of first byte to return from table
405 * @param count number of bytes to return from table
406 * @return a subset of the table corresponding to
407 * <code>strSfntTag</code> and containing the bytes
408 * starting at <code>offset</code> byte and including
409 * <code>count</code> bytes.
410 */
411 public byte[] getFontTable(String strSfntTag, int offset, int count);
412
413 /**
414 * Returns the size of the table for a specified tag. Tags for sfnt
415 * tables include items like <i>cmap</i>, <i>name</i> and <i>head</i>.
416 * @param sfntTag a four-character code as a 32-bit integer
417 * @return the size of the table corresponding to the specified
418 * tag.
419 */
420 public int getFontTableSize(int sfntTag);
421
422 /**
423 * Returns the size of the table for a specified tag. Tags for sfnt
424 * tables include items like <i>cmap</i>, <i>name</i> and <i>head</i>.
425 * @param strSfntTag a four-character code as a
426 * <code>String</code>
427 * @return the size of the table corresponding to the specified tag.
428 */
429 public int getFontTableSize(String strSfntTag);
430
431
432}
java/pkg/java/awt/font/ShapeGraphicAttribute.class+0−0

Binary file not shown.

java/pkg/java/awt/font/ShapeGraphicAttribute.java+256−0
@@ -0,0 +1,256 @@
1/*
2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.awt.Shape;
44import java.awt.Graphics;
45import java.awt.Rectangle;
46import java.awt.Graphics2D;
47import java.awt.Shape;
48import java.awt.geom.AffineTransform;
49import java.awt.geom.Rectangle2D;
50
51/**
52 * The <code>ShapeGraphicAttribute</code> class is an implementation of
53 * {@link GraphicAttribute} that draws shapes in a {@link TextLayout}.
54 * @see GraphicAttribute
55 */
56public final class ShapeGraphicAttribute extends GraphicAttribute {
57
58 private Shape fShape;
59 private boolean fStroke;
60
61 /**
62 * A key indicating the shape should be stroked with a 1-pixel wide stroke.
63 */
64 public static final boolean STROKE = true;
65
66 /**
67 * A key indicating the shape should be filled.
68 */
69 public static final boolean FILL = false;
70
71 // cache shape bounds, since GeneralPath doesn't
72 private Rectangle2D fShapeBounds;
73
74 /**
75 * Constructs a <code>ShapeGraphicAttribute</code> for the specified
76 * {@link Shape}.
77 * @param shape the <code>Shape</code> to render. The
78 * <code>Shape</code> is rendered with its origin at the origin of
79 * this <code>ShapeGraphicAttribute</code> in the
80 * host <code>TextLayout</code>. This object maintains a reference to
81 * <code>shape</code>.
82 * @param alignment one of the alignments from this
83 * <code>ShapeGraphicAttribute</code>.
84 * @param stroke <code>true</code> if the <code>Shape</code> should be
85 * stroked; <code>false</code> if the <code>Shape</code> should be
86 * filled.
87 */
88 public ShapeGraphicAttribute(Shape shape,
89 int alignment,
90 boolean stroke) {
91
92 super(alignment);
93
94 fShape = shape;
95 fStroke = stroke;
96 fShapeBounds = fShape.getBounds2D();
97 }
98
99 /**
100 * Returns the ascent of this <code>ShapeGraphicAttribute</code>. The
101 * ascent of a <code>ShapeGraphicAttribute</code> is the positive
102 * distance from the origin of its <code>Shape</code> to the top of
103 * bounds of its <code>Shape</code>.
104 * @return the ascent of this <code>ShapeGraphicAttribute</code>.
105 */
106 public float getAscent() {
107
108 return (float) Math.max(0, -fShapeBounds.getMinY());
109 }
110
111 /**
112 * Returns the descent of this <code>ShapeGraphicAttribute</code>.
113 * The descent of a <code>ShapeGraphicAttribute</code> is the distance
114 * from the origin of its <code>Shape</code> to the bottom of the
115 * bounds of its <code>Shape</code>.
116 * @return the descent of this <code>ShapeGraphicAttribute</code>.
117 */
118 public float getDescent() {
119
120 return (float) Math.max(0, fShapeBounds.getMaxY());
121 }
122
123 /**
124 * Returns the advance of this <code>ShapeGraphicAttribute</code>.
125 * The advance of a <code>ShapeGraphicAttribute</code> is the distance
126 * from the origin of its <code>Shape</code> to the right side of the
127 * bounds of its <code>Shape</code>.
128 * @return the advance of this <code>ShapeGraphicAttribute</code>.
129 */
130 public float getAdvance() {
131
132 return (float) Math.max(0, fShapeBounds.getMaxX());
133 }
134
135 /**
136 * {@inheritDoc}
137 */
138 public void draw(Graphics2D graphics, float x, float y) {
139
140 // translating graphics to draw Shape !!!
141 graphics.translate((int)x, (int)y);
142
143 try {
144 if (fStroke == STROKE) {
145 // REMIND: set stroke to correct size
146 graphics.draw(fShape);
147 }
148 else {
149 graphics.fill(fShape);
150 }
151 }
152 finally {
153 graphics.translate(-(int)x, -(int)y);
154 }
155 }
156
157 /**
158 * Returns a {@link Rectangle2D} that encloses all of the
159 * bits drawn by this <code>ShapeGraphicAttribute</code> relative to
160 * the rendering position. A graphic can be rendered beyond its
161 * origin, ascent, descent, or advance; but if it does, this method's
162 * implementation should indicate where the graphic is rendered.
163 * @return a <code>Rectangle2D</code> that encloses all of the bits
164 * rendered by this <code>ShapeGraphicAttribute</code>.
165 */
166 public Rectangle2D getBounds() {
167
168 Rectangle2D.Float bounds = new Rectangle2D.Float();
169 bounds.setRect(fShapeBounds);
170
171 if (fStroke == STROKE) {
172 ++bounds.width;
173 ++bounds.height;
174 }
175
176 return bounds;
177 }
178
179 /**
180 * Return a {@link java.awt.Shape} that represents the region that
181 * this <code>ShapeGraphicAttribute</code> renders. This is used when a
182 * {@link TextLayout} is requested to return the outline of the text.
183 * The (untransformed) shape must not extend outside the rectangular
184 * bounds returned by <code>getBounds</code>.
185 * @param tx an optional {@link AffineTransform} to apply to the
186 * this <code>ShapeGraphicAttribute</code>. This can be null.
187 * @return the <code>Shape</code> representing this graphic attribute,
188 * suitable for stroking or filling.
189 * @since 1.6
190 */
191 public Shape getOutline(AffineTransform tx) {
192 return tx == null ? fShape : tx.createTransformedShape(fShape);
193 }
194
195 /**
196 * Returns a hashcode for this <code>ShapeGraphicAttribute</code>.
197 * @return a hash code value for this
198 * <code>ShapeGraphicAttribute</code>.
199 */
200 public int hashCode() {
201
202 return fShape.hashCode();
203 }
204
205 /**
206 * Compares this <code>ShapeGraphicAttribute</code> to the specified
207 * <code>Object</code>.
208 * @param rhs the <code>Object</code> to compare for equality
209 * @return <code>true</code> if this
210 * <code>ShapeGraphicAttribute</code> equals <code>rhs</code>;
211 * <code>false</code> otherwise.
212 */
213 public boolean equals(Object rhs) {
214
215 try {
216 return equals((ShapeGraphicAttribute) rhs);
217 }
218 catch(ClassCastException e) {
219 return false;
220 }
221 }
222
223 /**
224 * Compares this <code>ShapeGraphicAttribute</code> to the specified
225 * <code>ShapeGraphicAttribute</code>.
226 * @param rhs the <code>ShapeGraphicAttribute</code> to compare for
227 * equality
228 * @return <code>true</code> if this
229 * <code>ShapeGraphicAttribute</code> equals <code>rhs</code>;
230 * <code>false</code> otherwise.
231 */
232 public boolean equals(ShapeGraphicAttribute rhs) {
233
234 if (rhs == null) {
235 return false;
236 }
237
238 if (this == rhs) {
239 return true;
240 }
241
242 if (fStroke != rhs.fStroke) {
243 return false;
244 }
245
246 if (getAlignment() != rhs.getAlignment()) {
247 return false;
248 }
249
250 if (!fShape.equals(rhs.fShape)) {
251 return false;
252 }
253
254 return true;
255 }
256}
java/pkg/java/awt/font/StyledParagraph.class+0−0

Binary file not shown.

java/pkg/java/awt/font/StyledParagraph.java+492−0
@@ -0,0 +1,492 @@
1/*
2 * Copyright (c) 1999, 2005, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 *
25 */
26
27/*
28 * (C) Copyright IBM Corp. 1999, All rights reserved.
29 */
30package java.awt.font;
31
32import java.awt.Font;
33import java.awt.Toolkit;
34import java.awt.im.InputMethodHighlight;
35import java.text.Annotation;
36import java.text.AttributedCharacterIterator;
37import java.util.Vector;
38import java.util.HashMap;
39import java.util.Map;
40import sun.font.Decoration;
41import sun.font.FontResolver;
42import sun.text.CodePointIterator;
43
44/**
45 * This class stores Font, GraphicAttribute, and Decoration intervals
46 * on a paragraph of styled text.
47 * <p>
48 * Currently, this class is optimized for a small number of intervals
49 * (preferrably 1).
50 */
51final class StyledParagraph {
52
53 // the length of the paragraph
54 private int length;
55
56 // If there is a single Decoration for the whole paragraph, it
57 // is stored here. Otherwise this field is ignored.
58
59 private Decoration decoration;
60
61 // If there is a single Font or GraphicAttribute for the whole
62 // paragraph, it is stored here. Otherwise this field is ignored.
63 private Object font;
64
65 // If there are multiple Decorations in the paragraph, they are
66 // stored in this Vector, in order. Otherwise this vector and
67 // the decorationStarts array are null.
68 private Vector decorations;
69 // If there are multiple Decorations in the paragraph,
70 // decorationStarts[i] contains the index where decoration i
71 // starts. For convenience, there is an extra entry at the
72 // end of this array with the length of the paragraph.
73 int[] decorationStarts;
74
75 // If there are multiple Fonts/GraphicAttributes in the paragraph,
76 // they are
77 // stored in this Vector, in order. Otherwise this vector and
78 // the fontStarts array are null.
79 private Vector fonts;
80 // If there are multiple Fonts/GraphicAttributes in the paragraph,
81 // fontStarts[i] contains the index where decoration i
82 // starts. For convenience, there is an extra entry at the
83 // end of this array with the length of the paragraph.
84 int[] fontStarts;
85
86 private static int INITIAL_SIZE = 8;
87
88 /**
89 * Create a new StyledParagraph over the given styled text.
90 * @param aci an iterator over the text
91 * @param chars the characters extracted from aci
92 */
93 public StyledParagraph(AttributedCharacterIterator aci,
94 char[] chars) {
95
96 int start = aci.getBeginIndex();
97 int end = aci.getEndIndex();
98 length = end - start;
99
100 int index = start;
101 aci.first();
102
103 do {
104 final int nextRunStart = aci.getRunLimit();
105 final int localIndex = index-start;
106
107 Map attributes = aci.getAttributes();
108 attributes = addInputMethodAttrs(attributes);
109 Decoration d = Decoration.getDecoration(attributes);
110 addDecoration(d, localIndex);
111
112 Object f = getGraphicOrFont(attributes);
113 if (f == null) {
114 addFonts(chars, attributes, localIndex, nextRunStart-start);
115 }
116 else {
117 addFont(f, localIndex);
118 }
119
120 aci.setIndex(nextRunStart);
121 index = nextRunStart;
122
123 } while (index < end);
124
125 // Add extra entries to starts arrays with the length
126 // of the paragraph. 'this' is used as a dummy value
127 // in the Vector.
128 if (decorations != null) {
129 decorationStarts = addToVector(this, length, decorations, decorationStarts);
130 }
131 if (fonts != null) {
132 fontStarts = addToVector(this, length, fonts, fontStarts);
133 }
134 }
135
136 /**
137 * Adjust indices in starts to reflect an insertion after pos.
138 * Any index in starts greater than pos will be increased by 1.
139 */
140 private static void insertInto(int pos, int[] starts, int numStarts) {
141
142 while (starts[--numStarts] > pos) {
143 starts[numStarts] += 1;
144 }
145 }
146
147 /**
148 * Return a StyledParagraph reflecting the insertion of a single character
149 * into the text. This method will attempt to reuse the given paragraph,
150 * but may create a new paragraph.
151 * @param aci an iterator over the text. The text should be the same as the
152 * text used to create (or most recently update) oldParagraph, with
153 * the exception of inserting a single character at insertPos.
154 * @param chars the characters in aci
155 * @param insertPos the index of the new character in aci
156 * @param oldParagraph a StyledParagraph for the text in aci before the
157 * insertion
158 */
159 public static StyledParagraph insertChar(AttributedCharacterIterator aci,
160 char[] chars,
161 int insertPos,
162 StyledParagraph oldParagraph) {
163
164 // If the styles at insertPos match those at insertPos-1,
165 // oldParagraph will be reused. Otherwise we create a new
166 // paragraph.
167
168 char ch = aci.setIndex(insertPos);
169 int relativePos = Math.max(insertPos - aci.getBeginIndex() - 1, 0);
170
171 Map attributes = addInputMethodAttrs(aci.getAttributes());
172 Decoration d = Decoration.getDecoration(attributes);
173 if (!oldParagraph.getDecorationAt(relativePos).equals(d)) {
174 return new StyledParagraph(aci, chars);
175 }
176 Object f = getGraphicOrFont(attributes);
177 if (f == null) {
178 FontResolver resolver = FontResolver.getInstance();
179 int fontIndex = resolver.getFontIndex(ch);
180 f = resolver.getFont(fontIndex, attributes);
181 }
182 if (!oldParagraph.getFontOrGraphicAt(relativePos).equals(f)) {
183 return new StyledParagraph(aci, chars);
184 }
185
186 // insert into existing paragraph
187 oldParagraph.length += 1;
188 if (oldParagraph.decorations != null) {
189 insertInto(relativePos,
190 oldParagraph.decorationStarts,
191 oldParagraph.decorations.size());
192 }
193 if (oldParagraph.fonts != null) {
194 insertInto(relativePos,
195 oldParagraph.fontStarts,
196 oldParagraph.fonts.size());
197 }
198 return oldParagraph;
199 }
200
201 /**
202 * Adjust indices in starts to reflect a deletion after deleteAt.
203 * Any index in starts greater than deleteAt will be increased by 1.
204 * It is the caller's responsibility to make sure that no 0-length
205 * runs result.
206 */
207 private static void deleteFrom(int deleteAt, int[] starts, int numStarts) {
208
209 while (starts[--numStarts] > deleteAt) {
210 starts[numStarts] -= 1;
211 }
212 }
213
214 /**
215 * Return a StyledParagraph reflecting the insertion of a single character
216 * into the text. This method will attempt to reuse the given paragraph,
217 * but may create a new paragraph.
218 * @param aci an iterator over the text. The text should be the same as the
219 * text used to create (or most recently update) oldParagraph, with
220 * the exception of deleting a single character at deletePos.
221 * @param chars the characters in aci
222 * @param deletePos the index where a character was removed
223 * @param oldParagraph a StyledParagraph for the text in aci before the
224 * insertion
225 */
226 public static StyledParagraph deleteChar(AttributedCharacterIterator aci,
227 char[] chars,
228 int deletePos,
229 StyledParagraph oldParagraph) {
230
231 // We will reuse oldParagraph unless there was a length-1 run
232 // at deletePos. We could do more work and check the individual
233 // Font and Decoration runs, but we don't right now...
234 deletePos -= aci.getBeginIndex();
235
236 if (oldParagraph.decorations == null && oldParagraph.fonts == null) {
237 oldParagraph.length -= 1;
238 return oldParagraph;
239 }
240
241 if (oldParagraph.getRunLimit(deletePos) == deletePos+1) {
242 if (deletePos == 0 || oldParagraph.getRunLimit(deletePos-1) == deletePos) {
243 return new StyledParagraph(aci, chars);
244 }
245 }
246
247 oldParagraph.length -= 1;
248 if (oldParagraph.decorations != null) {
249 deleteFrom(deletePos,
250 oldParagraph.decorationStarts,
251 oldParagraph.decorations.size());
252 }
253 if (oldParagraph.fonts != null) {
254 deleteFrom(deletePos,
255 oldParagraph.fontStarts,
256 oldParagraph.fonts.size());
257 }
258 return oldParagraph;
259 }
260
261 /**
262 * Return the index at which there is a different Font, GraphicAttribute, or
263 * Dcoration than at the given index.
264 * @param index a valid index in the paragraph
265 * @return the first index where there is a change in attributes from
266 * those at index
267 */
268 public int getRunLimit(int index) {
269
270 if (index < 0 || index >= length) {
271 throw new IllegalArgumentException("index out of range");
272 }
273 int limit1 = length;
274 if (decorations != null) {
275 int run = findRunContaining(index, decorationStarts);
276 limit1 = decorationStarts[run+1];
277 }
278 int limit2 = length;
279 if (fonts != null) {
280 int run = findRunContaining(index, fontStarts);
281 limit2 = fontStarts[run+1];
282 }
283 return Math.min(limit1, limit2);
284 }
285
286 /**
287 * Return the Decoration in effect at the given index.
288 * @param index a valid index in the paragraph
289 * @return the Decoration at index.
290 */
291 public Decoration getDecorationAt(int index) {
292
293 if (index < 0 || index >= length) {
294 throw new IllegalArgumentException("index out of range");
295 }
296 if (decorations == null) {
297 return decoration;
298 }
299 int run = findRunContaining(index, decorationStarts);
300 return (Decoration) decorations.elementAt(run);
301 }
302
303 /**
304 * Return the Font or GraphicAttribute in effect at the given index.
305 * The client must test the type of the return value to determine what
306 * it is.
307 * @param index a valid index in the paragraph
308 * @return the Font or GraphicAttribute at index.
309 */
310 public Object getFontOrGraphicAt(int index) {
311
312 if (index < 0 || index >= length) {
313 throw new IllegalArgumentException("index out of range");
314 }
315 if (fonts == null) {
316 return font;
317 }
318 int run = findRunContaining(index, fontStarts);
319 return fonts.elementAt(run);
320 }
321
322 /**
323 * Return i such that starts[i] <= index < starts[i+1]. starts
324 * must be in increasing order, with at least one element greater
325 * than index.
326 */
327 private static int findRunContaining(int index, int[] starts) {
328
329 for (int i=1; true; i++) {
330 if (starts[i] > index) {
331 return i-1;
332 }
333 }
334 }
335
336 /**
337 * Append the given Object to the given Vector. Add
338 * the given index to the given starts array. If the
339 * starts array does not have room for the index, a
340 * new array is created and returned.
341 */
342 private static int[] addToVector(Object obj,
343 int index,
344 Vector v,
345 int[] starts) {
346
347 if (!v.lastElement().equals(obj)) {
348 v.addElement(obj);
349 int count = v.size();
350 if (starts.length == count) {
351 int[] temp = new int[starts.length*2];
352 System.arraycopy(starts, 0, temp, 0, starts.length);
353 starts = temp;
354 }
355 starts[count-1] = index;
356 }
357 return starts;
358 }
359
360 /**
361 * Add a new Decoration run with the given Decoration at the
362 * given index.
363 */
364 private void addDecoration(Decoration d, int index) {
365
366 if (decorations != null) {
367 decorationStarts = addToVector(d,
368 index,
369 decorations,
370 decorationStarts);
371 }
372 else if (decoration == null) {
373 decoration = d;
374 }
375 else {
376 if (!decoration.equals(d)) {
377 decorations = new Vector(INITIAL_SIZE);
378 decorations.addElement(decoration);
379 decorations.addElement(d);
380 decorationStarts = new int[INITIAL_SIZE];
381 decorationStarts[0] = 0;
382 decorationStarts[1] = index;
383 }
384 }
385 }
386
387 /**
388 * Add a new Font/GraphicAttribute run with the given object at the
389 * given index.
390 */
391 private void addFont(Object f, int index) {
392
393 if (fonts != null) {
394 fontStarts = addToVector(f, index, fonts, fontStarts);
395 }
396 else if (font == null) {
397 font = f;
398 }
399 else {
400 if (!font.equals(f)) {
401 fonts = new Vector(INITIAL_SIZE);
402 fonts.addElement(font);
403 fonts.addElement(f);
404 fontStarts = new int[INITIAL_SIZE];
405 fontStarts[0] = 0;
406 fontStarts[1] = index;
407 }
408 }
409 }
410
411 /**
412 * Resolve the given chars into Fonts using FontResolver, then add
413 * font runs for each.
414 */
415 private void addFonts(char[] chars, Map attributes, int start, int limit) {
416
417 FontResolver resolver = FontResolver.getInstance();
418 CodePointIterator iter = CodePointIterator.create(chars, start, limit);
419 for (int runStart = iter.charIndex(); runStart < limit; runStart = iter.charIndex()) {
420 int fontIndex = resolver.nextFontRunIndex(iter);
421 addFont(resolver.getFont(fontIndex, attributes), runStart);
422 }
423 }
424
425 /**
426 * Return a Map with entries from oldStyles, as well as input
427 * method entries, if any.
428 */
429 static Map addInputMethodAttrs(Map oldStyles) {
430
431 Object value = oldStyles.get(TextAttribute.INPUT_METHOD_HIGHLIGHT);
432
433 try {
434 if (value != null) {
435 if (value instanceof Annotation) {
436 value = ((Annotation)value).getValue();
437 }
438
439 InputMethodHighlight hl;
440 hl = (InputMethodHighlight) value;
441
442 Map imStyles = null;
443 try {
444 imStyles = hl.getStyle();
445 } catch (NoSuchMethodError e) {
446 }
447
448 if (imStyles == null) {
449 Toolkit tk = Toolkit.getDefaultToolkit();
450 imStyles = tk.mapInputMethodHighlight(hl);
451 }
452
453 if (imStyles != null) {
454 HashMap newStyles = new HashMap(5, (float)0.9);
455 newStyles.putAll(oldStyles);
456
457 newStyles.putAll(imStyles);
458
459 return newStyles;
460 }
461 }
462 }
463 catch(ClassCastException e) {
464 }
465
466 return oldStyles;
467 }
468
469 /**
470 * Extract a GraphicAttribute or Font from the given attributes.
471 * If attributes does not contain a GraphicAttribute, Font, or
472 * Font family entry this method returns null.
473 */
474 private static Object getGraphicOrFont(Map attributes) {
475
476 Object value = attributes.get(TextAttribute.CHAR_REPLACEMENT);
477 if (value != null) {
478 return value;
479 }
480 value = attributes.get(TextAttribute.FONT);
481 if (value != null) {
482 return value;
483 }
484
485 if (attributes.get(TextAttribute.FAMILY) != null) {
486 return Font.getFont(attributes);
487 }
488 else {
489 return null;
490 }
491 }
492}
java/pkg/java/awt/font/TextAttribute.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextAttribute.java+1117−0
@@ -0,0 +1,1117 @@
1/*
2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.io.InvalidObjectException;
44import java.text.AttributedCharacterIterator.Attribute;
45import java.util.Map;
46import java.util.HashMap;
47
48/**
49 * The <code>TextAttribute</code> class defines attribute keys and
50 * attribute values used for text rendering.
51 * <p>
52 * <code>TextAttribute</code> instances are used as attribute keys to
53 * identify attributes in
54 * {@link java.awt.Font Font},
55 * {@link java.awt.font.TextLayout TextLayout},
56 * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator},
57 * and other classes handling text attributes. Other constants defined
58 * in this class can be used as attribute values.
59 * <p>
60 * For each text attribute, the documentation provides:
61 * <UL>
62 * <LI>the type of its value,
63 * <LI>the relevant predefined constants, if any
64 * <LI>the default effect if the attribute is absent
65 * <LI>the valid values if there are limitations
66 * <LI>a description of the effect.
67 * </UL>
68 * <p>
69 * <H4>Values</H4>
70 * <UL>
71 * <LI>The values of attributes must always be immutable.
72 * <LI>Where value limitations are given, any value outside of that
73 * set is reserved for future use; the value will be treated as
74 * the default.
75 * <LI>The value <code>null</code> is treated the same as the
76 * default value and results in the default behavior.
77 * <li>If the value is not of the proper type, the attribute
78 * will be ignored.
79 * <li>The identity of the value does not matter, only the actual
80 * value. For example, <code>TextAttribute.WEIGHT_BOLD</code> and
81 * <code>new Float(2.0)</code>
82 * indicate the same <code>WEIGHT</code>.
83 * <li>Attribute values of type <code>Number</code> (used for
84 * <code>WEIGHT</code>, <code>WIDTH</code>, <code>POSTURE</code>,
85 * <code>SIZE</code>, <code>JUSTIFICATION</code>, and
86 * <code>TRACKING</code>) can vary along their natural range and are
87 * not restricted to the predefined constants.
88 * <code>Number.floatValue()</code> is used to get the actual value
89 * from the <code>Number</code>.
90 * <li>The values for <code>WEIGHT</code>, <code>WIDTH</code>, and
91 * <code>POSTURE</code> are interpolated by the system, which
92 * can select the 'nearest available' font or use other techniques to
93 * approximate the user's request.
94 *
95 * </UL>
96 *
97 * <h4>Summary of attributes</h4>
98 * <p>
99 * <font size="-1">
100 * <table align="center" border="0" cellspacing="0" cellpadding="2" width="%95"
101 * summary="Key, value type, principal constants, and default value
102 * behavior of all TextAttributes">
103 * <tr bgcolor="#ccccff">
104 * <th valign="TOP" align="CENTER">Key</th>
105 * <th valign="TOP" align="CENTER">Value Type</th>
106 * <th valign="TOP" align="CENTER">Principal Constants</th>
107 * <th valign="TOP" align="CENTER">Default Value</th>
108 * </tr>
109 * <tr>
110 * <td valign="TOP">{@link #FAMILY}</td>
111 * <td valign="TOP">String</td>
112 * <td valign="TOP">See Font {@link java.awt.Font#DIALOG DIALOG},
113{@link java.awt.Font#DIALOG_INPUT DIALOG_INPUT},<br> {@link java.awt.Font#SERIF SERIF},
114{@link java.awt.Font#SANS_SERIF SANS_SERIF}, and {@link java.awt.Font#MONOSPACED MONOSPACED}.
115</td>
116 * <td valign="TOP">"Default" (use platform default)</td>
117 * </tr>
118 * <tr bgcolor="#eeeeff">
119 * <td valign="TOP">{@link #WEIGHT}</td>
120 * <td valign="TOP">Number</td>
121 * <td valign="TOP">WEIGHT_REGULAR, WEIGHT_BOLD</td>
122 * <td valign="TOP">WEIGHT_REGULAR</td>
123 * </tr>
124 * <tr>
125 * <td valign="TOP">{@link #WIDTH}</td>
126 * <td valign="TOP">Number</td>
127 * <td valign="TOP">WIDTH_CONDENSED, WIDTH_REGULAR,<br>WIDTH_EXTENDED</td>
128 * <td valign="TOP">WIDTH_REGULAR</td>
129 * </tr>
130 * <tr bgcolor="#eeeeff">
131 * <td valign="TOP">{@link #POSTURE}</td>
132 * <td valign="TOP">Number</td>
133 * <td valign="TOP">POSTURE_REGULAR, POSTURE_OBLIQUE</td>
134 * <td valign="TOP">POSTURE_REGULAR</td>
135 * </tr>
136 * <tr>
137 * <td valign="TOP">{@link #SIZE}</td>
138 * <td valign="TOP">Number</td>
139 * <td valign="TOP">none</td>
140 * <td valign="TOP">12.0</td>
141 * </tr>
142 * <tr bgcolor="#eeeeff">
143 * <td valign="TOP">{@link #TRANSFORM}</td>
144 * <td valign="TOP">{@link TransformAttribute}</td>
145 * <td valign="TOP">See TransformAttribute {@link TransformAttribute#IDENTITY IDENTITY}</td>
146 * <td valign="TOP">TransformAttribute.IDENTITY</td>
147 * </tr>
148 * <tr>
149 * <td valign="TOP">{@link #SUPERSCRIPT}</td>
150 * <td valign="TOP">Integer</td>
151 * <td valign="TOP">SUPERSCRIPT_SUPER, SUPERSCRIPT_SUB</td>
152 * <td valign="TOP">0 (use the standard glyphs and metrics)</td>
153 * </tr>
154 * <tr bgcolor="#eeeeff">
155 * <td valign="TOP">{@link #FONT}</td>
156 * <td valign="TOP">{@link java.awt.Font}</td>
157 * <td valign="TOP">none</td>
158 * <td valign="TOP">null (do not override font resolution)</td>
159 * </tr>
160 * <tr>
161 * <td valign="TOP">{@link #CHAR_REPLACEMENT}</td>
162 * <td valign="TOP">{@link GraphicAttribute}</td>
163 * <td valign="TOP">none</td>
164 * <td valign="TOP">null (draw text using font glyphs)</td>
165 * </tr>
166 * <tr bgcolor="#eeeeff">
167 * <td valign="TOP">{@link #FOREGROUND}</td>
168 * <td valign="TOP">{@link java.awt.Paint}</td>
169 * <td valign="TOP">none</td>
170 * <td valign="TOP">null (use current graphics paint)</td>
171 * </tr>
172 * <tr>
173 * <td valign="TOP">{@link #BACKGROUND}</td>
174 * <td valign="TOP">{@link java.awt.Paint}</td>
175 * <td valign="TOP">none</td>
176 * <td valign="TOP">null (do not render background)</td>
177 * </tr>
178 * <tr bgcolor="#eeeeff">
179 * <td valign="TOP">{@link #UNDERLINE}</td>
180 * <td valign="TOP">Integer</td>
181 * <td valign="TOP">UNDERLINE_ON</td>
182 * <td valign="TOP">-1 (do not render underline)</td>
183 * </tr>
184 * <tr>
185 * <td valign="TOP">{@link #STRIKETHROUGH}</td>
186 * <td valign="TOP">Boolean</td>
187 * <td valign="TOP">STRIKETHROUGH_ON</td>
188 * <td valign="TOP">false (do not render strikethrough)</td>
189 * </tr>
190 * <tr bgcolor="#eeeeff">
191 * <td valign="TOP">{@link #RUN_DIRECTION}</td>
192 * <td valign="TOP">Boolean</td>
193 * <td valign="TOP">RUN_DIRECTION_LTR<br>RUN_DIRECTION_RTL</td>
194 * <td valign="TOP">null (use {@link java.text.Bidi} standard default)</td>
195 * </tr>
196 * <tr>
197 * <td valign="TOP">{@link #BIDI_EMBEDDING}</td>
198 * <td valign="TOP">Integer</td>
199 * <td valign="TOP">none</td>
200 * <td valign="TOP">0 (use base line direction)</td>
201 * </tr>
202 * <tr bgcolor="#eeeeff">
203 * <td valign="TOP">{@link #JUSTIFICATION}</td>
204 * <td valign="TOP">Number</td>
205 * <td valign="TOP">JUSTIFICATION_FULL</td>
206 * <td valign="TOP">JUSTIFICATION_FULL</td>
207 * </tr>
208 * <tr>
209 * <td valign="TOP">{@link #INPUT_METHOD_HIGHLIGHT}</td>
210 * <td valign="TOP">{@link java.awt.im.InputMethodHighlight},<br>{@link java.text.Annotation}</td>
211 * <td valign="TOP">(see class)</td>
212 * <td valign="TOP">null (do not apply input highlighting)</td>
213 * </tr>
214 * <tr bgcolor="#eeeeff">
215 * <td valign="TOP">{@link #INPUT_METHOD_UNDERLINE}</td>
216 * <td valign="TOP">Integer</td>
217 * <td valign="TOP">UNDERLINE_LOW_ONE_PIXEL,<br>UNDERLINE_LOW_TWO_PIXEL</td>
218 * <td valign="TOP">-1 (do not render underline)</td>
219 * </tr>
220 * <tr>
221 * <td valign="TOP">{@link #SWAP_COLORS}</td>
222 * <td valign="TOP">Boolean</td>
223 * <td valign="TOP">SWAP_COLORS_ON</td>
224 * <td valign="TOP">false (do not swap colors)</td>
225 * </tr>
226 * <tr bgcolor="#eeeeff">
227 * <td valign="TOP">{@link #NUMERIC_SHAPING}</td>
228 * <td valign="TOP">{@link java.awt.font.NumericShaper}</td>
229 * <td valign="TOP">none</td>
230 * <td valign="TOP">null (do not shape digits)</td>
231 * </tr>
232 * <tr>
233 * <td valign="TOP">{@link #KERNING}</td>
234 * <td valign="TOP">Integer</td>
235 * <td valign="TOP">KERNING_ON</td>
236 * <td valign="TOP">0 (do not request kerning)</td>
237 * </tr>
238 * <tr bgcolor="#eeeeff">
239 * <td valign="TOP">{@link #LIGATURES}</td>
240 * <td valign="TOP">Integer</td>
241 * <td valign="TOP">LIGATURES_ON</td>
242 * <td valign="TOP">0 (do not form optional ligatures)</td>
243 * </tr>
244 * <tr>
245 * <td valign="TOP">{@link #TRACKING}</td>
246 * <td valign="TOP">Number</td>
247 * <td valign="TOP">TRACKING_LOOSE, TRACKING_TIGHT</td>
248 * <td valign="TOP">0 (do not add tracking)</td>
249 * </tr>
250 * </table>
251 * </font>
252 *
253 * @see java.awt.Font
254 * @see java.awt.font.TextLayout
255 * @see java.text.AttributedCharacterIterator
256 */
257public final class TextAttribute extends Attribute {
258
259 // table of all instances in this class, used by readResolve
260 private static final Map instanceMap = new HashMap(29);
261
262 /**
263 * Constructs a <code>TextAttribute</code> with the specified name.
264 * @param name the attribute name to assign to this
265 * <code>TextAttribute</code>
266 */
267 protected TextAttribute(String name) {
268 super(name);
269 if (this.getClass() == TextAttribute.class) {
270 instanceMap.put(name, this);
271 }
272 }
273
274 /**
275 * Resolves instances being deserialized to the predefined constants.
276 */
277 protected Object readResolve() throws InvalidObjectException {
278 if (this.getClass() != TextAttribute.class) {
279 throw new InvalidObjectException(
280 "subclass didn't correctly implement readResolve");
281 }
282
283 TextAttribute instance = (TextAttribute) instanceMap.get(getName());
284 if (instance != null) {
285 return instance;
286 } else {
287 throw new InvalidObjectException("unknown attribute name");
288 }
289 }
290
291 // Serialization compatibility with Java 2 platform v1.2.
292 // 1.2 will throw an InvalidObjectException if ever asked to
293 // deserialize INPUT_METHOD_UNDERLINE.
294 // This shouldn't happen in real life.
295 static final long serialVersionUID = 7744112784117861702L;
296
297 //
298 // For use with Font.
299 //
300
301 /**
302 * Attribute key for the font name. Values are instances of
303 * <b><code>String</code></b>. The default value is
304 * <code>"Default"</code>, which causes the platform default font
305 * family to be used.
306 *
307 * <p> The <code>Font</code> class defines constants for the logical
308 * font names
309 * {@link java.awt.Font#DIALOG DIALOG},
310 * {@link java.awt.Font#DIALOG_INPUT DIALOG_INPUT},
311 * {@link java.awt.Font#SANS_SERIF SANS_SERIF},
312 * {@link java.awt.Font#SERIF SERIF}, and
313 * {@link java.awt.Font#MONOSPACED MONOSPACED}.
314 *
315 * <p>This defines the value passed as <code>name</code> to the
316 * <code>Font</code> constructor. Both logical and physical
317 * font names are allowed. If a font with the requested name
318 * is not found, the default font is used.
319 *
320 * <p><em>Note:</em> This attribute is unfortunately misnamed, as
321 * it specifies the face name and not just the family. Thus
322 * values such as "Lucida Sans Bold" will select that face if it
323 * exists. Note, though, that if the requested face does not
324 * exist, the default will be used with <em>regular</em> weight.
325 * The "Bold" in the name is part of the face name, not a separate
326 * request that the font's weight be bold.</p>
327 */
328 public static final TextAttribute FAMILY =
329 new TextAttribute("family");
330
331 /**
332 * Attribute key for the weight of a font. Values are instances
333 * of <b><code>Number</code></b>. The default value is
334 * <code>WEIGHT_REGULAR</code>.
335 *
336 * <p>Several constant values are provided, see {@link
337 * #WEIGHT_EXTRA_LIGHT}, {@link #WEIGHT_LIGHT}, {@link
338 * #WEIGHT_DEMILIGHT}, {@link #WEIGHT_REGULAR}, {@link
339 * #WEIGHT_SEMIBOLD}, {@link #WEIGHT_MEDIUM}, {@link
340 * #WEIGHT_DEMIBOLD}, {@link #WEIGHT_BOLD}, {@link #WEIGHT_HEAVY},
341 * {@link #WEIGHT_EXTRABOLD}, and {@link #WEIGHT_ULTRABOLD}. The
342 * value <code>WEIGHT_BOLD</code> corresponds to the
343 * style value <code>Font.BOLD</code> as passed to the
344 * <code>Font</code> constructor.
345 *
346 * <p>The value is roughly the ratio of the stem width to that of
347 * the regular weight.
348 *
349 * <p>The system can interpolate the provided value.
350 */
351 public static final TextAttribute WEIGHT =
352 new TextAttribute("weight");
353
354 /**
355 * The lightest predefined weight.
356 * @see #WEIGHT
357 */
358 public static final Float WEIGHT_EXTRA_LIGHT =
359 Float.valueOf(0.5f);
360
361 /**
362 * The standard light weight.
363 * @see #WEIGHT
364 */
365 public static final Float WEIGHT_LIGHT =
366 Float.valueOf(0.75f);
367
368 /**
369 * An intermediate weight between <code>WEIGHT_LIGHT</code> and
370 * <code>WEIGHT_STANDARD</code>.
371 * @see #WEIGHT
372 */
373 public static final Float WEIGHT_DEMILIGHT =
374 Float.valueOf(0.875f);
375
376 /**
377 * The standard weight. This is the default value for <code>WEIGHT</code>.
378 * @see #WEIGHT
379 */
380 public static final Float WEIGHT_REGULAR =
381 Float.valueOf(1.0f);
382
383 /**
384 * A moderately heavier weight than <code>WEIGHT_REGULAR</code>.
385 * @see #WEIGHT
386 */
387 public static final Float WEIGHT_SEMIBOLD =
388 Float.valueOf(1.25f);
389
390 /**
391 * An intermediate weight between <code>WEIGHT_REGULAR</code> and
392 * <code>WEIGHT_BOLD</code>.
393 * @see #WEIGHT
394 */
395 public static final Float WEIGHT_MEDIUM =
396 Float.valueOf(1.5f);
397
398 /**
399 * A moderately lighter weight than <code>WEIGHT_BOLD</code>.
400 * @see #WEIGHT
401 */
402 public static final Float WEIGHT_DEMIBOLD =
403 Float.valueOf(1.75f);
404
405 /**
406 * The standard bold weight.
407 * @see #WEIGHT
408 */
409 public static final Float WEIGHT_BOLD =
410 Float.valueOf(2.0f);
411
412 /**
413 * A moderately heavier weight than <code>WEIGHT_BOLD</code>.
414 * @see #WEIGHT
415 */
416 public static final Float WEIGHT_HEAVY =
417 Float.valueOf(2.25f);
418
419 /**
420 * An extra heavy weight.
421 * @see #WEIGHT
422 */
423 public static final Float WEIGHT_EXTRABOLD =
424 Float.valueOf(2.5f);
425
426 /**
427 * The heaviest predefined weight.
428 * @see #WEIGHT
429 */
430 public static final Float WEIGHT_ULTRABOLD =
431 Float.valueOf(2.75f);
432
433 /**
434 * Attribute key for the width of a font. Values are instances of
435 * <b><code>Number</code></b>. The default value is
436 * <code>WIDTH_REGULAR</code>.
437 *
438 * <p>Several constant values are provided, see {@link
439 * #WIDTH_CONDENSED}, {@link #WIDTH_SEMI_CONDENSED}, {@link
440 * #WIDTH_REGULAR}, {@link #WIDTH_SEMI_EXTENDED}, {@link
441 * #WIDTH_EXTENDED}.
442 *
443 * <p>The value is roughly the ratio of the advance width to that
444 * of the regular width.
445 *
446 * <p>The system can interpolate the provided value.
447 */
448 public static final TextAttribute WIDTH =
449 new TextAttribute("width");
450
451 /**
452 * The most condensed predefined width.
453 * @see #WIDTH
454 */
455 public static final Float WIDTH_CONDENSED =
456 Float.valueOf(0.75f);
457
458 /**
459 * A moderately condensed width.
460 * @see #WIDTH
461 */
462 public static final Float WIDTH_SEMI_CONDENSED =
463 Float.valueOf(0.875f);
464
465 /**
466 * The standard width. This is the default value for
467 * <code>WIDTH</code>.
468 * @see #WIDTH
469 */
470 public static final Float WIDTH_REGULAR =
471 Float.valueOf(1.0f);
472
473 /**
474 * A moderately extended width.
475 * @see #WIDTH
476 */
477 public static final Float WIDTH_SEMI_EXTENDED =
478 Float.valueOf(1.25f);
479
480 /**
481 * The most extended predefined width.
482 * @see #WIDTH
483 */
484 public static final Float WIDTH_EXTENDED =
485 Float.valueOf(1.5f);
486
487 /**
488 * Attribute key for the posture of a font. Values are instances
489 * of <b><code>Number</code></b>. The default value is
490 * <code>POSTURE_REGULAR</code>.
491 *
492 * <p>Two constant values are provided, {@link #POSTURE_REGULAR}
493 * and {@link #POSTURE_OBLIQUE}. The value
494 * <code>POSTURE_OBLIQUE</code> corresponds to the style value
495 * <code>Font.ITALIC</code> as passed to the <code>Font</code>
496 * constructor.
497 *
498 * <p>The value is roughly the slope of the stems of the font,
499 * expressed as the run over the rise. Positive values lean right.
500 *
501 * <p>The system can interpolate the provided value.
502 *
503 * <p>This will affect the font's italic angle as returned by
504 * <code>Font.getItalicAngle</code>.
505 *
506 * @see java.awt.Font#getItalicAngle()
507 */
508 public static final TextAttribute POSTURE =
509 new TextAttribute("posture");
510
511 /**
512 * The standard posture, upright. This is the default value for
513 * <code>POSTURE</code>.
514 * @see #POSTURE
515 */
516 public static final Float POSTURE_REGULAR =
517 Float.valueOf(0.0f);
518
519 /**
520 * The standard italic posture.
521 * @see #POSTURE
522 */
523 public static final Float POSTURE_OBLIQUE =
524 Float.valueOf(0.20f);
525
526 /**
527 * Attribute key for the font size. Values are instances of
528 * <b><code>Number</code></b>. The default value is 12pt.
529 *
530 * <p>This corresponds to the <code>size</code> parameter to the
531 * <code>Font</code> constructor.
532 *
533 * <p>Very large or small sizes will impact rendering performance,
534 * and the rendering system might not render text at these sizes.
535 * Negative sizes are illegal and result in the default size.
536 *
537 * <p>Note that the appearance and metrics of a 12pt font with a
538 * 2x transform might be different than that of a 24 point font
539 * with no transform.
540 */
541 public static final TextAttribute SIZE =
542 new TextAttribute("size");
543
544 /**
545 * Attribute key for the transform of a font. Values are
546 * instances of <b><code>TransformAttribute</code></b>. The
547 * default value is <code>TransformAttribute.IDENTITY</code>.
548 *
549 * <p>The <code>TransformAttribute</code> class defines the
550 * constant {@link TransformAttribute#IDENTITY IDENTITY}.
551 *
552 * <p>This corresponds to the transform passed to
553 * <code>Font.deriveFont(AffineTransform)</code>. Since that
554 * transform is mutable and <code>TextAttribute</code> values must
555 * not be, the <code>TransformAttribute</code> wrapper class is
556 * used.
557 *
558 * <p>The primary intent is to support scaling and skewing, though
559 * other effects are possible.</p>
560 *
561 * <p>Some transforms will cause the baseline to be rotated and/or
562 * shifted. The text and the baseline are transformed together so
563 * that the text follows the new baseline. For example, with text
564 * on a horizontal baseline, the new baseline follows the
565 * direction of the unit x vector passed through the
566 * transform. Text metrics are measured against this new baseline.
567 * So, for example, with other things being equal, text rendered
568 * with a rotated TRANSFORM and an unrotated TRANSFORM will measure as
569 * having the same ascent, descent, and advance.</p>
570 *
571 * <p>In styled text, the baselines for each such run are aligned
572 * one after the other to potentially create a non-linear baseline
573 * for the entire run of text. For more information, see {@link
574 * TextLayout#getLayoutPath}.</p>
575 *
576 * @see TransformAttribute
577 * @see java.awt.geom.AffineTransform
578 */
579 public static final TextAttribute TRANSFORM =
580 new TextAttribute("transform");
581
582 /**
583 * Attribute key for superscripting and subscripting. Values are
584 * instances of <b><code>Integer</code></b>. The default value is
585 * 0, which means that no superscript or subscript is used.
586 *
587 * <p>Two constant values are provided, see {@link
588 * #SUPERSCRIPT_SUPER} and {@link #SUPERSCRIPT_SUB}. These have
589 * the values 1 and -1 respectively. Values of
590 * greater magnitude define greater levels of superscript or
591 * subscripting, for example, 2 corresponds to super-superscript,
592 * 3 to super-super-superscript, and similarly for negative values
593 * and subscript, up to a level of 7 (or -7). Values beyond this
594 * range are reserved; behavior is platform-dependent.
595 *
596 * <p><code>SUPERSCRIPT</code> can
597 * impact the ascent and descent of a font. The ascent
598 * and descent can never become negative, however.
599 */
600 public static final TextAttribute SUPERSCRIPT =
601 new TextAttribute("superscript");
602
603 /**
604 * Standard superscript.
605 * @see #SUPERSCRIPT
606 */
607 public static final Integer SUPERSCRIPT_SUPER =
608 Integer.valueOf(1);
609
610 /**
611 * Standard subscript.
612 * @see #SUPERSCRIPT
613 */
614 public static final Integer SUPERSCRIPT_SUB =
615 Integer.valueOf(-1);
616
617 /**
618 * Attribute key used to provide the font to use to render text.
619 * Values are instances of {@link java.awt.Font}. The default
620 * value is null, indicating that normal resolution of a
621 * <code>Font</code> from attributes should be performed.
622 *
623 * <p><code>TextLayout</code> and
624 * <code>AttributedCharacterIterator</code> work in terms of
625 * <code>Maps</code> of <code>TextAttributes</code>. Normally,
626 * all the attributes are examined and used to select and
627 * configure a <code>Font</code> instance. If a <code>FONT</code>
628 * attribute is present, though, its associated <code>Font</code>
629 * will be used. This provides a way for users to override the
630 * resolution of font attributes into a <code>Font</code>, or
631 * force use of a particular <code>Font</code> instance. This
632 * also allows users to specify subclasses of <code>Font</code> in
633 * cases where a <code>Font</code> can be subclassed.
634 *
635 * <p><code>FONT</code> is used for special situations where
636 * clients already have a <code>Font</code> instance but still
637 * need to use <code>Map</code>-based APIs. Typically, there will
638 * be no other attributes in the <code>Map</code> except the
639 * <code>FONT</code> attribute. With <code>Map</code>-based APIs
640 * the common case is to specify all attributes individually, so
641 * <code>FONT</code> is not needed or desireable.
642 *
643 * <p>However, if both <code>FONT</code> and other attributes are
644 * present in the <code>Map</code>, the rendering system will
645 * merge the attributes defined in the <code>Font</code> with the
646 * additional attributes. This merging process classifies
647 * <code>TextAttributes</code> into two groups. One group, the
648 * 'primary' group, is considered fundamental to the selection and
649 * metric behavior of a font. These attributes are
650 * <code>FAMILY</code>, <code>WEIGHT</code>, <code>WIDTH</code>,
651 * <code>POSTURE</code>, <code>SIZE</code>,
652 * <code>TRANSFORM</code>, <code>SUPERSCRIPT</code>, and
653 * <code>TRACKING</code>. The other group, the 'secondary' group,
654 * consists of all other defined attributes, with the exception of
655 * <code>FONT</code> itself.
656 *
657 * <p>To generate the new <code>Map</code>, first the
658 * <code>Font</code> is obtained from the <code>FONT</code>
659 * attribute, and <em>all</em> of its attributes extracted into a
660 * new <code>Map</code>. Then only the <em>secondary</em>
661 * attributes from the original <code>Map</code> are added to
662 * those in the new <code>Map</code>. Thus the values of primary
663 * attributes come solely from the <code>Font</code>, and the
664 * values of secondary attributes originate with the
665 * <code>Font</code> but can be overridden by other values in the
666 * <code>Map</code>.
667 *
668 * <p><em>Note:</em><code>Font's</code> <code>Map</code>-based
669 * constructor and <code>deriveFont</code> methods do not process
670 * the <code>FONT</code> attribute, as these are used to create
671 * new <code>Font</code> objects. Instead, {@link
672 * java.awt.Font#getFont(Map) Font.getFont(Map)} should be used to
673 * handle the <code>FONT</code> attribute.
674 *
675 * @see java.awt.Font
676 */
677 public static final TextAttribute FONT =
678 new TextAttribute("font");
679
680 /**
681 * Attribute key for a user-defined glyph to display in lieu
682 * of the font's standard glyph for a character. Values are
683 * intances of GraphicAttribute. The default value is null,
684 * indicating that the standard glyphs provided by the font
685 * should be used.
686 *
687 * <p>This attribute is used to reserve space for a graphic or
688 * other component embedded in a line of text. It is required for
689 * correct positioning of 'inline' components within a line when
690 * bidirectional reordering (see {@link java.text.Bidi}) is
691 * performed. Each character (Unicode code point) will be
692 * rendered using the provided GraphicAttribute. Typically, the
693 * characters to which this attribute is applied should be
694 * <code>&#92;uFFFC</code>.
695 *
696 * <p>The GraphicAttribute determines the logical and visual
697 * bounds of the text; the actual Font values are ignored.
698 *
699 * @see GraphicAttribute
700 */
701 public static final TextAttribute CHAR_REPLACEMENT =
702 new TextAttribute("char_replacement");
703
704 //
705 // Adornments added to text.
706 //
707
708 /**
709 * Attribute key for the paint used to render the text. Values are
710 * instances of <b><code>Paint</code></b>. The default value is
711 * null, indicating that the <code>Paint</code> set on the
712 * <code>Graphics2D</code> at the time of rendering is used.
713 *
714 * <p>Glyphs will be rendered using this
715 * <code>Paint</code> regardless of the <code>Paint</code> value
716 * set on the <code>Graphics</code> (but see {@link #SWAP_COLORS}).
717 *
718 * @see java.awt.Paint
719 * @see #SWAP_COLORS
720 */
721 public static final TextAttribute FOREGROUND =
722 new TextAttribute("foreground");
723
724 /**
725 * Attribute key for the paint used to render the background of
726 * the text. Values are instances of <b><code>Paint</code></b>.
727 * The default value is null, indicating that the background
728 * should not be rendered.
729 *
730 * <p>The logical bounds of the text will be filled using this
731 * <code>Paint</code>, and then the text will be rendered on top
732 * of it (but see {@link #SWAP_COLORS}).
733 *
734 * <p>The visual bounds of the text is extended to include the
735 * logical bounds, if necessary. The outline is not affected.
736 *
737 * @see java.awt.Paint
738 * @see #SWAP_COLORS
739 */
740 public static final TextAttribute BACKGROUND =
741 new TextAttribute("background");
742
743 /**
744 * Attribute key for underline. Values are instances of
745 * <b><code>Integer</code></b>. The default value is -1, which
746 * means no underline.
747 *
748 * <p>The constant value {@link #UNDERLINE_ON} is provided.
749 *
750 * <p>The underline affects both the visual bounds and the outline
751 * of the text.
752 */
753 public static final TextAttribute UNDERLINE =
754 new TextAttribute("underline");
755
756 /**
757 * Standard underline.
758 *
759 * @see #UNDERLINE
760 */
761 public static final Integer UNDERLINE_ON =
762 Integer.valueOf(0);
763
764 /**
765 * Attribute key for strikethrough. Values are instances of
766 * <b><code>Boolean</code></b>. The default value is
767 * <code>false</code>, which means no strikethrough.
768 *
769 * <p>The constant value {@link #STRIKETHROUGH_ON} is provided.
770 *
771 * <p>The strikethrough affects both the visual bounds and the
772 * outline of the text.
773 */
774 public static final TextAttribute STRIKETHROUGH =
775 new TextAttribute("strikethrough");
776
777 /**
778 * A single strikethrough.
779 *
780 * @see #STRIKETHROUGH
781 */
782 public static final Boolean STRIKETHROUGH_ON =
783 Boolean.TRUE;
784
785 //
786 // Attributes use to control layout of text on a line.
787 //
788
789 /**
790 * Attribute key for the run direction of the line. Values are
791 * instances of <b><code>Boolean</code></b>. The default value is
792 * null, which indicates that the standard Bidi algorithm for
793 * determining run direction should be used with the value {@link
794 * java.text.Bidi#DIRECTION_DEFAULT_LEFT_TO_RIGHT}.
795 *
796 * <p>The constants {@link #RUN_DIRECTION_RTL} and {@link
797 * #RUN_DIRECTION_LTR} are provided.
798 *
799 * <p>This determines the value passed to the {@link
800 * java.text.Bidi} constructor to select the primary direction of
801 * the text in the paragraph.
802 *
803 * <p><em>Note:</em> This attribute should have the same value for
804 * all the text in a paragraph, otherwise the behavior is
805 * undetermined.
806 *
807 * @see java.text.Bidi
808 */
809 public static final TextAttribute RUN_DIRECTION =
810 new TextAttribute("run_direction");
811
812 /**
813 * Left-to-right run direction.
814 * @see #RUN_DIRECTION
815 */
816 public static final Boolean RUN_DIRECTION_LTR =
817 Boolean.FALSE;
818
819 /**
820 * Right-to-left run direction.
821 * @see #RUN_DIRECTION
822 */
823 public static final Boolean RUN_DIRECTION_RTL =
824 Boolean.TRUE;
825
826 /**
827 * Attribute key for the embedding level of the text. Values are
828 * instances of <b><code>Integer</code></b>. The default value is
829 * <code>null</code>, indicating that the the Bidirectional
830 * algorithm should run without explicit embeddings.
831 *
832 * <p>Positive values 1 through 61 are <em>embedding</em> levels,
833 * negative values -1 through -61 are <em>override</em> levels.
834 * The value 0 means that the base line direction is used. These
835 * levels are passed in the embedding levels array to the {@link
836 * java.text.Bidi} constructor.
837 *
838 * <p><em>Note:</em> When this attribute is present anywhere in
839 * a paragraph, then any Unicode bidi control characters (RLO,
840 * LRO, RLE, LRE, and PDF) in the paragraph are
841 * disregarded, and runs of text where this attribute is not
842 * present are treated as though it were present and had the value
843 * 0.
844 *
845 * @see java.text.Bidi
846 */
847 public static final TextAttribute BIDI_EMBEDDING =
848 new TextAttribute("bidi_embedding");
849
850 /**
851 * Attribute key for the justification of a paragraph. Values are
852 * instances of <b><code>Number</code></b>. The default value is
853 * 1, indicating that justification should use the full width
854 * provided. Values are pinned to the range [0..1].
855 *
856 * <p>The constants {@link #JUSTIFICATION_FULL} and {@link
857 * #JUSTIFICATION_NONE} are provided.
858 *
859 * <p>Specifies the fraction of the extra space to use when
860 * justification is requested on a <code>TextLayout</code>. For
861 * example, if the line is 50 points wide and it is requested to
862 * justify to 70 points, a value of 0.75 will pad to use
863 * three-quarters of the remaining space, or 15 points, so that
864 * the resulting line will be 65 points in length.
865 *
866 * <p><em>Note:</em> This should have the same value for all the
867 * text in a paragraph, otherwise the behavior is undetermined.
868 *
869 * @see TextLayout#getJustifiedLayout
870 */
871 public static final TextAttribute JUSTIFICATION =
872 new TextAttribute("justification");
873
874 /**
875 * Justify the line to the full requested width. This is the
876 * default value for <code>JUSTIFICATION</code>.
877 * @see #JUSTIFICATION
878 */
879 public static final Float JUSTIFICATION_FULL =
880 Float.valueOf(1.0f);
881
882 /**
883 * Do not allow the line to be justified.
884 * @see #JUSTIFICATION
885 */
886 public static final Float JUSTIFICATION_NONE =
887 Float.valueOf(0.0f);
888
889 //
890 // For use by input method.
891 //
892
893 /**
894 * Attribute key for input method highlight styles.
895 *
896 * <p>Values are instances of {@link
897 * java.awt.im.InputMethodHighlight} or {@link
898 * java.text.Annotation}. The default value is <code>null</code>,
899 * which means that input method styles should not be applied
900 * before rendering.
901 *
902 * <p>If adjacent runs of text with the same
903 * <code>InputMethodHighlight</code> need to be rendered
904 * separately, the <code>InputMethodHighlights</code> should be
905 * wrapped in <code>Annotation</code> instances.
906 *
907 * <p>Input method highlights are used while text is being
908 * composed by an input method. Text editing components should
909 * retain them even if they generally only deal with unstyled
910 * text, and make them available to the drawing routines.
911 *
912 * @see java.awt.Font
913 * @see java.awt.im.InputMethodHighlight
914 * @see java.text.Annotation
915 */
916 public static final TextAttribute INPUT_METHOD_HIGHLIGHT =
917 new TextAttribute("input method highlight");
918
919 /**
920 * Attribute key for input method underlines. Values
921 * are instances of <b><code>Integer</code></b>. The default
922 * value is <code>-1</code>, which means no underline.
923 *
924 * <p>Several constant values are provided, see {@link
925 * #UNDERLINE_LOW_ONE_PIXEL}, {@link #UNDERLINE_LOW_TWO_PIXEL},
926 * {@link #UNDERLINE_LOW_DOTTED}, {@link #UNDERLINE_LOW_GRAY}, and
927 * {@link #UNDERLINE_LOW_DASHED}.
928 *
929 * <p>This may be used in conjunction with {@link #UNDERLINE} if
930 * desired. The primary purpose is for use by input methods.
931 * Other use of these underlines for simple ornamentation might
932 * confuse users.
933 *
934 * <p>The input method underline affects both the visual bounds and
935 * the outline of the text.
936 *
937 * @since 1.3
938 */
939 public static final TextAttribute INPUT_METHOD_UNDERLINE =
940 new TextAttribute("input method underline");
941
942 /**
943 * Single pixel solid low underline.
944 * @see #INPUT_METHOD_UNDERLINE
945 * @since 1.3
946 */
947 public static final Integer UNDERLINE_LOW_ONE_PIXEL =
948 Integer.valueOf(1);
949
950 /**
951 * Double pixel solid low underline.
952 * @see #INPUT_METHOD_UNDERLINE
953 * @since 1.3
954 */
955 public static final Integer UNDERLINE_LOW_TWO_PIXEL =
956 Integer.valueOf(2);
957
958 /**
959 * Single pixel dotted low underline.
960 * @see #INPUT_METHOD_UNDERLINE
961 * @since 1.3
962 */
963 public static final Integer UNDERLINE_LOW_DOTTED =
964 Integer.valueOf(3);
965
966 /**
967 * Double pixel gray low underline.
968 * @see #INPUT_METHOD_UNDERLINE
969 * @since 1.3
970 */
971 public static final Integer UNDERLINE_LOW_GRAY =
972 Integer.valueOf(4);
973
974 /**
975 * Single pixel dashed low underline.
976 * @see #INPUT_METHOD_UNDERLINE
977 * @since 1.3
978 */
979 public static final Integer UNDERLINE_LOW_DASHED =
980 Integer.valueOf(5);
981
982 /**
983 * Attribute key for swapping foreground and background
984 * <code>Paints</code>. Values are instances of
985 * <b><code>Boolean</code></b>. The default value is
986 * <code>false</code>, which means do not swap colors.
987 *
988 * <p>The constant value {@link #SWAP_COLORS_ON} is defined.
989 *
990 * <p>If the {@link #FOREGROUND} attribute is set, its
991 * <code>Paint</code> will be used as the background, otherwise
992 * the <code>Paint</code> currently on the <code>Graphics</code>
993 * will be used. If the {@link #BACKGROUND} attribute is set, its
994 * <code>Paint</code> will be used as the foreground, otherwise
995 * the system will find a contrasting color to the
996 * (resolved) background so that the text will be visible.
997 *
998 * @see #FOREGROUND
999 * @see #BACKGROUND
1000 */
1001 public static final TextAttribute SWAP_COLORS =
1002 new TextAttribute("swap_colors");
1003
1004 /**
1005 * Swap foreground and background.
1006 * @see #SWAP_COLORS
1007 * @since 1.3
1008 */
1009 public static final Boolean SWAP_COLORS_ON =
1010 Boolean.TRUE;
1011
1012 /**
1013 * Attribute key for converting ASCII decimal digits to other
1014 * decimal ranges. Values are instances of {@link NumericShaper}.
1015 * The default is <code>null</code>, which means do not perform
1016 * numeric shaping.
1017 *
1018 * <p>When a numeric shaper is defined, the text is first
1019 * processed by the shaper before any other analysis of the text
1020 * is performed.
1021 *
1022 * <p><em>Note:</em> This should have the same value for all the
1023 * text in the paragraph, otherwise the behavior is undetermined.
1024 *
1025 * @see NumericShaper
1026 * @since 1.4
1027 */
1028 public static final TextAttribute NUMERIC_SHAPING =
1029 new TextAttribute("numeric_shaping");
1030
1031 /**
1032 * Attribute key to request kerning. Values are instances of
1033 * <b><code>Integer</code></b>. The default value is
1034 * <code>0</code>, which does not request kerning.
1035 *
1036 * <p>The constant value {@link #KERNING_ON} is provided.
1037 *
1038 * <p>The default advances of single characters are not
1039 * appropriate for some character sequences, for example "To" or
1040 * "AWAY". Without kerning the adjacent characters appear to be
1041 * separated by too much space. Kerning causes selected sequences
1042 * of characters to be spaced differently for a more pleasing
1043 * visual appearance.
1044 *
1045 * @since 1.6
1046 */
1047 public static final TextAttribute KERNING =
1048 new TextAttribute("kerning");
1049
1050 /**
1051 * Request standard kerning.
1052 * @see #KERNING
1053 * @since 1.6
1054 */
1055 public static final Integer KERNING_ON =
1056 Integer.valueOf(1);
1057
1058
1059 /**
1060 * Attribute key for enabling optional ligatures. Values are
1061 * instances of <b><code>Integer</code></b>. The default value is
1062 * <code>0</code>, which means do not use optional ligatures.
1063 *
1064 * <p>The constant value {@link #LIGATURES_ON} is defined.
1065 *
1066 * <p>Ligatures required by the writing system are always enabled.
1067 *
1068 * @since 1.6
1069 */
1070 public static final TextAttribute LIGATURES =
1071 new TextAttribute("ligatures");
1072
1073 /**
1074 * Request standard optional ligatures.
1075 * @see #LIGATURES
1076 * @since 1.6
1077 */
1078 public static final Integer LIGATURES_ON =
1079 Integer.valueOf(1);
1080
1081 /**
1082 * Attribute key to control tracking. Values are instances of
1083 * <b><code>Number</code></b>. The default value is
1084 * <code>0</code>, which means no additional tracking.
1085 *
1086 * <p>The constant values {@link #TRACKING_TIGHT} and {@link
1087 * #TRACKING_LOOSE} are provided.
1088 *
1089 * <p>The tracking value is multiplied by the font point size and
1090 * passed through the font transform to determine an additional
1091 * amount to add to the advance of each glyph cluster. Positive
1092 * tracking values will inhibit formation of optional ligatures.
1093 * Tracking values are typically between <code>-0.1</code> and
1094 * <code>0.3</code>; values outside this range are generally not
1095 * desireable.
1096 *
1097 * @since 1.6
1098 */
1099 public static final TextAttribute TRACKING =
1100 new TextAttribute("tracking");
1101
1102 /**
1103 * Perform tight tracking.
1104 * @see #TRACKING
1105 * @since 1.6
1106 */
1107 public static final Float TRACKING_TIGHT =
1108 Float.valueOf(-.04f);
1109
1110 /**
1111 * Perform loose tracking.
1112 * @see #TRACKING
1113 * @since 1.6
1114 */
1115 public static final Float TRACKING_LOOSE =
1116 Float.valueOf(.04f);
1117}
java/pkg/java/awt/font/TextHitInfo.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextHitInfo.java+247−0
@@ -0,0 +1,247 @@
1/*
2 * Copyright (c) 1997, 1998, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42import java.lang.String;
43
44/**
45 * The <code>TextHitInfo</code> class represents a character position in a
46 * text model, and a <b>bias</b>, or "side," of the character. Biases are
47 * either <EM>leading</EM> (the left edge, for a left-to-right character)
48 * or <EM>trailing</EM> (the right edge, for a left-to-right character).
49 * Instances of <code>TextHitInfo</code> are used to specify caret and
50 * insertion positions within text.
51 * <p>
52 * For example, consider the text "abc". TextHitInfo.trailing(1)
53 * corresponds to the right side of the 'b' in the text.
54 * <p>
55 * <code>TextHitInfo</code> is used primarily by {@link TextLayout} and
56 * clients of <code>TextLayout</code>. Clients of <code>TextLayout</code>
57 * query <code>TextHitInfo</code> instances for an insertion offset, where
58 * new text is inserted into the text model. The insertion offset is equal
59 * to the character position in the <code>TextHitInfo</code> if the bias
60 * is leading, and one character after if the bias is trailing. The
61 * insertion offset for TextHitInfo.trailing(1) is 2.
62 * <p>
63 * Sometimes it is convenient to construct a <code>TextHitInfo</code> with
64 * the same insertion offset as an existing one, but on the opposite
65 * character. The <code>getOtherHit</code> method constructs a new
66 * <code>TextHitInfo</code> with the same insertion offset as an existing
67 * one, with a hit on the character on the other side of the insertion offset.
68 * Calling <code>getOtherHit</code> on trailing(1) would return leading(2).
69 * In general, <code>getOtherHit</code> for trailing(n) returns
70 * leading(n+1) and <code>getOtherHit</code> for leading(n)
71 * returns trailing(n-1).
72 * <p>
73 * <strong>Example</strong>:<p>
74 * Converting a graphical point to an insertion point within a text
75 * model
76 * <blockquote><pre>
77 * TextLayout layout = ...;
78 * Point2D.Float hitPoint = ...;
79 * TextHitInfo hitInfo = layout.hitTestChar(hitPoint.x, hitPoint.y);
80 * int insPoint = hitInfo.getInsertionIndex();
81 * // insPoint is relative to layout; may need to adjust for use
82 * // in a text model
83 * </pre></blockquote>
84 *
85 * @see TextLayout
86 */
87
88public final class TextHitInfo {
89 private int charIndex;
90 private boolean isLeadingEdge;
91
92 /**
93 * Constructs a new <code>TextHitInfo</code>.
94 * @param charIndex the index of the character hit
95 * @param isLeadingEdge <code>true</code> if the leading edge of the
96 * character was hit
97 */
98 private TextHitInfo(int charIndex, boolean isLeadingEdge) {
99 this.charIndex = charIndex;
100 this.isLeadingEdge = isLeadingEdge;
101 }
102
103 /**
104 * Returns the index of the character hit.
105 * @return the index of the character hit.
106 */
107 public int getCharIndex() {
108 return charIndex;
109 }
110
111 /**
112 * Returns <code>true</code> if the leading edge of the character was
113 * hit.
114 * @return <code>true</code> if the leading edge of the character was
115 * hit; <code>false</code> otherwise.
116 */
117 public boolean isLeadingEdge() {
118 return isLeadingEdge;
119 }
120
121 /**
122 * Returns the insertion index. This is the character index if
123 * the leading edge of the character was hit, and one greater
124 * than the character index if the trailing edge was hit.
125 * @return the insertion index.
126 */
127 public int getInsertionIndex() {
128 return isLeadingEdge ? charIndex : charIndex + 1;
129 }
130
131 /**
132 * Returns the hash code.
133 * @return the hash code of this <code>TextHitInfo</code>, which is
134 * also the <code>charIndex</code> of this <code>TextHitInfo</code>.
135 */
136 public int hashCode() {
137 return charIndex;
138 }
139
140 /**
141 * Returns <code>true</code> if the specified <code>Object</code> is a
142 * <code>TextHitInfo</code> and equals this <code>TextHitInfo</code>.
143 * @param obj the <code>Object</code> to test for equality
144 * @return <code>true</code> if the specified <code>Object</code>
145 * equals this <code>TextHitInfo</code>; <code>false</code> otherwise.
146 */
147 public boolean equals(Object obj) {
148 return (obj instanceof TextHitInfo) && equals((TextHitInfo)obj);
149 }
150
151 /**
152 * Returns <code>true</code> if the specified <code>TextHitInfo</code>
153 * has the same <code>charIndex</code> and <code>isLeadingEdge</code>
154 * as this <code>TextHitInfo</code>. This is not the same as having
155 * the same insertion offset.
156 * @param hitInfo a specified <code>TextHitInfo</code>
157 * @return <code>true</code> if the specified <code>TextHitInfo</code>
158 * has the same <code>charIndex</code> and <code>isLeadingEdge</code>
159 * as this <code>TextHitInfo</code>.
160 */
161 public boolean equals(TextHitInfo hitInfo) {
162 return hitInfo != null && charIndex == hitInfo.charIndex &&
163 isLeadingEdge == hitInfo.isLeadingEdge;
164 }
165
166 /**
167 * Returns a <code>String</code> representing the hit for debugging
168 * use only.
169 * @return a <code>String</code> representing this
170 * <code>TextHitInfo</code>.
171 */
172 public String toString() {
173 return "TextHitInfo[" + charIndex + (isLeadingEdge ? "L" : "T")+"]";
174 }
175
176 /**
177 * Creates a <code>TextHitInfo</code> on the leading edge of the
178 * character at the specified <code>charIndex</code>.
179 * @param charIndex the index of the character hit
180 * @return a <code>TextHitInfo</code> on the leading edge of the
181 * character at the specified <code>charIndex</code>.
182 */
183 public static TextHitInfo leading(int charIndex) {
184 return new TextHitInfo(charIndex, true);
185 }
186
187 /**
188 * Creates a hit on the trailing edge of the character at
189 * the specified <code>charIndex</code>.
190 * @param charIndex the index of the character hit
191 * @return a <code>TextHitInfo</code> on the trailing edge of the
192 * character at the specified <code>charIndex</code>.
193 */
194 public static TextHitInfo trailing(int charIndex) {
195 return new TextHitInfo(charIndex, false);
196 }
197
198 /**
199 * Creates a <code>TextHitInfo</code> at the specified offset,
200 * associated with the character before the offset.
201 * @param offset an offset associated with the character before
202 * the offset
203 * @return a <code>TextHitInfo</code> at the specified offset.
204 */
205 public static TextHitInfo beforeOffset(int offset) {
206 return new TextHitInfo(offset-1, false);
207 }
208
209 /**
210 * Creates a <code>TextHitInfo</code> at the specified offset,
211 * associated with the character after the offset.
212 * @param offset an offset associated with the character after
213 * the offset
214 * @return a <code>TextHitInfo</code> at the specified offset.
215 */
216 public static TextHitInfo afterOffset(int offset) {
217 return new TextHitInfo(offset, true);
218 }
219
220 /**
221 * Creates a <code>TextHitInfo</code> on the other side of the
222 * insertion point. This <code>TextHitInfo</code> remains unchanged.
223 * @return a <code>TextHitInfo</code> on the other side of the
224 * insertion point.
225 */
226 public TextHitInfo getOtherHit() {
227 if (isLeadingEdge) {
228 return trailing(charIndex - 1);
229 } else {
230 return leading(charIndex + 1);
231 }
232 }
233
234 /**
235 * Creates a <code>TextHitInfo</code> whose character index is offset
236 * by <code>delta</code> from the <code>charIndex</code> of this
237 * <code>TextHitInfo</code>. This <code>TextHitInfo</code> remains
238 * unchanged.
239 * @param delta the value to offset this <code>charIndex</code>
240 * @return a <code>TextHitInfo</code> whose <code>charIndex</code> is
241 * offset by <code>delta</code> from the <code>charIndex</code> of
242 * this <code>TextHitInfo</code>.
243 */
244 public TextHitInfo getOffsetHit(int delta) {
245 return new TextHitInfo(charIndex + delta, isLeadingEdge);
246 }
247}
java/pkg/java/awt/font/TextJustifier.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextJustifier.java+240−0
@@ -0,0 +1,240 @@
1/*
2 * Copyright (c) 1997, 1999, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43/*
44 * one info for each side of each glyph
45 * separate infos for grow and shrink case
46 * !!! this doesn't really need to be a separate class. If we keep it
47 * separate, probably the newJustify code from TextLayout belongs here as well.
48 */
49
50class TextJustifier {
51 private GlyphJustificationInfo[] info;
52 private int start;
53 private int limit;
54
55 static boolean DEBUG = false;
56
57 /**
58 * Initialize the justifier with an array of infos corresponding to each
59 * glyph. Start and limit indicate the range of the array to examine.
60 */
61 TextJustifier(GlyphJustificationInfo[] info, int start, int limit) {
62 this.info = info;
63 this.start = start;
64 this.limit = limit;
65
66 if (DEBUG) {
67 System.out.println("start: " + start + ", limit: " + limit);
68 for (int i = start; i < limit; i++) {
69 GlyphJustificationInfo gji = info[i];
70 System.out.println("w: " + gji.weight + ", gp: " +
71 gji.growPriority + ", gll: " +
72 gji.growLeftLimit + ", grl: " +
73 gji.growRightLimit);
74 }
75 }
76 }
77
78 public static final int MAX_PRIORITY = 3;
79
80 /**
81 * Return an array of deltas twice as long as the original info array,
82 * indicating the amount by which each side of each glyph should grow
83 * or shrink.
84 *
85 * Delta should be positive to expand the line, and negative to compress it.
86 */
87 public float[] justify(float delta) {
88 float[] deltas = new float[info.length * 2];
89
90 boolean grow = delta > 0;
91
92 if (DEBUG)
93 System.out.println("delta: " + delta);
94
95 // make separate passes through glyphs in order of decreasing priority
96 // until justifyDelta is zero or we run out of priorities.
97 int fallbackPriority = -1;
98 for (int p = 0; delta != 0; p++) {
99 /*
100 * special case 'fallback' iteration, set flag and recheck
101 * highest priority
102 */
103 boolean lastPass = p > MAX_PRIORITY;
104 if (lastPass)
105 p = fallbackPriority;
106
107 // pass through glyphs, first collecting weights and limits
108 float weight = 0;
109 float gslimit = 0;
110 float absorbweight = 0;
111 for (int i = start; i < limit; i++) {
112 GlyphJustificationInfo gi = info[i];
113 if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
114 if (fallbackPriority == -1) {
115 fallbackPriority = p;
116 }
117
118 if (i != start) { // ignore left of first character
119 weight += gi.weight;
120 if (grow) {
121 gslimit += gi.growLeftLimit;
122 if (gi.growAbsorb) {
123 absorbweight += gi.weight;
124 }
125 } else {
126 gslimit += gi.shrinkLeftLimit;
127 if (gi.shrinkAbsorb) {
128 absorbweight += gi.weight;
129 }
130 }
131 }
132
133 if (i + 1 != limit) { // ignore right of last character
134 weight += gi.weight;
135 if (grow) {
136 gslimit += gi.growRightLimit;
137 if (gi.growAbsorb) {
138 absorbweight += gi.weight;
139 }
140 } else {
141 gslimit += gi.shrinkRightLimit;
142 if (gi.shrinkAbsorb) {
143 absorbweight += gi.weight;
144 }
145 }
146 }
147 }
148 }
149
150 // did we hit the limit?
151 if (!grow) {
152 gslimit = -gslimit; // negative for negative deltas
153 }
154 boolean hitLimit = (weight == 0) || (!lastPass && ((delta < 0) == (delta < gslimit)));
155 boolean absorbing = hitLimit && absorbweight > 0;
156
157 // predivide delta by weight
158 float weightedDelta = delta / weight; // not used if weight == 0
159
160 float weightedAbsorb = 0;
161 if (hitLimit && absorbweight > 0) {
162 weightedAbsorb = (delta - gslimit) / absorbweight;
163 }
164
165 if (DEBUG) {
166 System.out.println("pass: " + p +
167 ", d: " + delta +
168 ", l: " + gslimit +
169 ", w: " + weight +
170 ", aw: " + absorbweight +
171 ", wd: " + weightedDelta +
172 ", wa: " + weightedAbsorb +
173 ", hit: " + (hitLimit ? "y" : "n"));
174 }
175
176 // now allocate this based on ratio of weight to total weight
177 int n = start * 2;
178 for (int i = start; i < limit; i++) {
179 GlyphJustificationInfo gi = info[i];
180 if ((grow ? gi.growPriority : gi.shrinkPriority) == p) {
181 if (i != start) { // ignore left
182 float d;
183 if (hitLimit) {
184 // factor in sign
185 d = grow ? gi.growLeftLimit : -gi.shrinkLeftLimit;
186 if (absorbing) {
187 // sign factored in already
188 d += gi.weight * weightedAbsorb;
189 }
190 } else {
191 // sign factored in already
192 d = gi.weight * weightedDelta;
193 }
194
195 deltas[n] += d;
196 }
197 n++;
198
199 if (i + 1 != limit) { // ignore right
200 float d;
201 if (hitLimit) {
202 d = grow ? gi.growRightLimit : -gi.shrinkRightLimit;
203 if (absorbing) {
204 d += gi.weight * weightedAbsorb;
205 }
206 } else {
207 d = gi.weight * weightedDelta;
208 }
209
210 deltas[n] += d;
211 }
212 n++;
213 } else {
214 n += 2;
215 }
216 }
217
218 if (!lastPass && hitLimit && !absorbing) {
219 delta -= gslimit;
220 } else {
221 delta = 0; // stop iteration
222 }
223 }
224
225 if (DEBUG) {
226 float total = 0;
227 for (int i = 0; i < deltas.length; i++) {
228 total += deltas[i];
229 System.out.print(deltas[i] + ", ");
230 if (i % 20 == 9) {
231 System.out.println();
232 }
233 }
234 System.out.println("\ntotal: " + total);
235 System.out.println();
236 }
237
238 return deltas;
239 }
240}
java/pkg/java/awt/font/TextLayout$CaretPolicy.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLayout.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLayout.java+2753−0
@@ -0,0 +1,2753 @@
1/*
2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996-2003, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.awt.Color;
44import java.awt.Font;
45import java.awt.Graphics2D;
46import java.awt.Rectangle;
47import java.awt.Shape;
48import java.awt.font.NumericShaper;
49import java.awt.font.TextLine.TextLineMetrics;
50import java.awt.geom.AffineTransform;
51import java.awt.geom.GeneralPath;
52import java.awt.geom.NoninvertibleTransformException;
53import java.awt.geom.Point2D;
54import java.awt.geom.Rectangle2D;
55import java.text.AttributedString;
56import java.text.AttributedCharacterIterator;
57import java.text.AttributedCharacterIterator.Attribute;
58import java.util.Map;
59import java.util.HashMap;
60import java.util.Hashtable;
61import sun.font.AttributeValues;
62import sun.font.CoreMetrics;
63import sun.font.Decoration;
64import sun.font.FontLineMetrics;
65import sun.font.FontResolver;
66import sun.font.GraphicComponent;
67import sun.font.LayoutPathImpl;
68import sun.text.CodePointIterator;
69
70/**
71 *
72 * <code>TextLayout</code> is an immutable graphical representation of styled
73 * character data.
74 * <p>
75 * It provides the following capabilities:
76 * <ul>
77 * <li>implicit bidirectional analysis and reordering,
78 * <li>cursor positioning and movement, including split cursors for
79 * mixed directional text,
80 * <li>highlighting, including both logical and visual highlighting
81 * for mixed directional text,
82 * <li>multiple baselines (roman, hanging, and centered),
83 * <li>hit testing,
84 * <li>justification,
85 * <li>default font substitution,
86 * <li>metric information such as ascent, descent, and advance, and
87 * <li>rendering
88 * </ul>
89 * <p>
90 * A <code>TextLayout</code> object can be rendered using
91 * its <code>draw</code> method.
92 * <p>
93 * <code>TextLayout</code> can be constructed either directly or through
94 * the use of a {@link LineBreakMeasurer}. When constructed directly, the
95 * source text represents a single paragraph. <code>LineBreakMeasurer</code>
96 * allows styled text to be broken into lines that fit within a particular
97 * width. See the <code>LineBreakMeasurer</code> documentation for more
98 * information.
99 * <p>
100 * <code>TextLayout</code> construction logically proceeds as follows:
101 * <ul>
102 * <li>paragraph attributes are extracted and examined,
103 * <li>text is analyzed for bidirectional reordering, and reordering
104 * information is computed if needed,
105 * <li>text is segmented into style runs
106 * <li>fonts are chosen for style runs, first by using a font if the
107 * attribute {@link TextAttribute#FONT} is present, otherwise by computing
108 * a default font using the attributes that have been defined
109 * <li>if text is on multiple baselines, the runs or subruns are further
110 * broken into subruns sharing a common baseline,
111 * <li>glyphvectors are generated for each run using the chosen font,
112 * <li>final bidirectional reordering is performed on the glyphvectors
113 * </ul>
114 * <p>
115 * All graphical information returned from a <code>TextLayout</code>
116 * object's methods is relative to the origin of the
117 * <code>TextLayout</code>, which is the intersection of the
118 * <code>TextLayout</code> object's baseline with its left edge. Also,
119 * coordinates passed into a <code>TextLayout</code> object's methods
120 * are assumed to be relative to the <code>TextLayout</code> object's
121 * origin. Clients usually need to translate between a
122 * <code>TextLayout</code> object's coordinate system and the coordinate
123 * system in another object (such as a
124 * {@link java.awt.Graphics Graphics} object).
125 * <p>
126 * <code>TextLayout</code> objects are constructed from styled text,
127 * but they do not retain a reference to their source text. Thus,
128 * changes in the text previously used to generate a <code>TextLayout</code>
129 * do not affect the <code>TextLayout</code>.
130 * <p>
131 * Three methods on a <code>TextLayout</code> object
132 * (<code>getNextRightHit</code>, <code>getNextLeftHit</code>, and
133 * <code>hitTestChar</code>) return instances of {@link TextHitInfo}.
134 * The offsets contained in these <code>TextHitInfo</code> objects
135 * are relative to the start of the <code>TextLayout</code>, <b>not</b>
136 * to the text used to create the <code>TextLayout</code>. Similarly,
137 * <code>TextLayout</code> methods that accept <code>TextHitInfo</code>
138 * instances as parameters expect the <code>TextHitInfo</code> object's
139 * offsets to be relative to the <code>TextLayout</code>, not to any
140 * underlying text storage model.
141 * <p>
142 * <strong>Examples</strong>:<p>
143 * Constructing and drawing a <code>TextLayout</code> and its bounding
144 * rectangle:
145 * <blockquote><pre>
146 * Graphics2D g = ...;
147 * Point2D loc = ...;
148 * Font font = Font.getFont("Helvetica-bold-italic");
149 * FontRenderContext frc = g.getFontRenderContext();
150 * TextLayout layout = new TextLayout("This is a string", font, frc);
151 * layout.draw(g, (float)loc.getX(), (float)loc.getY());
152 *
153 * Rectangle2D bounds = layout.getBounds();
154 * bounds.setRect(bounds.getX()+loc.getX(),
155 * bounds.getY()+loc.getY(),
156 * bounds.getWidth(),
157 * bounds.getHeight());
158 * g.draw(bounds);
159 * </pre>
160 * </blockquote>
161 * <p>
162 * Hit-testing a <code>TextLayout</code> (determining which character is at
163 * a particular graphical location):
164 * <blockquote><pre>
165 * Point2D click = ...;
166 * TextHitInfo hit = layout.hitTestChar(
167 * (float) (click.getX() - loc.getX()),
168 * (float) (click.getY() - loc.getY()));
169 * </pre>
170 * </blockquote>
171 * <p>
172 * Responding to a right-arrow key press:
173 * <blockquote><pre>
174 * int insertionIndex = ...;
175 * TextHitInfo next = layout.getNextRightHit(insertionIndex);
176 * if (next != null) {
177 * // translate graphics to origin of layout on screen
178 * g.translate(loc.getX(), loc.getY());
179 * Shape[] carets = layout.getCaretShapes(next.getInsertionIndex());
180 * g.draw(carets[0]);
181 * if (carets[1] != null) {
182 * g.draw(carets[1]);
183 * }
184 * }
185 * </pre></blockquote>
186 * <p>
187 * Drawing a selection range corresponding to a substring in the source text.
188 * The selected area may not be visually contiguous:
189 * <blockquote><pre>
190 * // selStart, selLimit should be relative to the layout,
191 * // not to the source text
192 *
193 * int selStart = ..., selLimit = ...;
194 * Color selectionColor = ...;
195 * Shape selection = layout.getLogicalHighlightShape(selStart, selLimit);
196 * // selection may consist of disjoint areas
197 * // graphics is assumed to be tranlated to origin of layout
198 * g.setColor(selectionColor);
199 * g.fill(selection);
200 * </pre></blockquote>
201 * <p>
202 * Drawing a visually contiguous selection range. The selection range may
203 * correspond to more than one substring in the source text. The ranges of
204 * the corresponding source text substrings can be obtained with
205 * <code>getLogicalRangesForVisualSelection()</code>:
206 * <blockquote><pre>
207 * TextHitInfo selStart = ..., selLimit = ...;
208 * Shape selection = layout.getVisualHighlightShape(selStart, selLimit);
209 * g.setColor(selectionColor);
210 * g.fill(selection);
211 * int[] ranges = getLogicalRangesForVisualSelection(selStart, selLimit);
212 * // ranges[0], ranges[1] is the first selection range,
213 * // ranges[2], ranges[3] is the second selection range, etc.
214 * </pre></blockquote>
215 * <p>
216 * Note: Font rotations can cause text baselines to be rotated, and
217 * multiple runs with different rotations can cause the baseline to
218 * bend or zig-zag. In order to account for this (rare) possibility,
219 * some APIs are specified to return metrics and take parameters 'in
220 * baseline-relative coordinates' (e.g. ascent, advance), and others
221 * are in 'in standard coordinates' (e.g. getBounds). Values in
222 * baseline-relative coordinates map the 'x' coordinate to the
223 * distance along the baseline, (positive x is forward along the
224 * baseline), and the 'y' coordinate to a distance along the
225 * perpendicular to the baseline at 'x' (postitive y is 90 degrees
226 * clockwise from the baseline vector). Values in standard
227 * coordinates are measured along the x and y axes, with 0,0 at the
228 * origin of the TextLayout. Documentation for each relevant API
229 * indicates what values are in what coordinate system. In general,
230 * measurement-related APIs are in baseline-relative coordinates,
231 * while display-related APIs are in standard coordinates.
232 *
233 * @see LineBreakMeasurer
234 * @see TextAttribute
235 * @see TextHitInfo
236 * @see LayoutPath
237 */
238public final class TextLayout implements Cloneable {
239
240 private int characterCount;
241 private boolean isVerticalLine = false;
242 private byte baseline;
243 private float[] baselineOffsets; // why have these ?
244 private TextLine textLine;
245
246 // cached values computed from GlyphSets and set info:
247 // all are recomputed from scratch in buildCache()
248 private TextLine.TextLineMetrics lineMetrics = null;
249 private float visibleAdvance;
250 private int hashCodeCache;
251
252 /*
253 * TextLayouts are supposedly immutable. If you mutate a TextLayout under
254 * the covers (like the justification code does) you'll need to set this
255 * back to false. Could be replaced with textLine != null <--> cacheIsValid.
256 */
257 private boolean cacheIsValid = false;
258
259
260 // This value is obtained from an attribute, and constrained to the
261 // interval [0,1]. If 0, the layout cannot be justified.
262 private float justifyRatio;
263
264 // If a layout is produced by justification, then that layout
265 // cannot be justified. To enforce this constraint the
266 // justifyRatio of the justified layout is set to this value.
267 private static final float ALREADY_JUSTIFIED = -53.9f;
268
269 // dx and dy specify the distance between the TextLayout's origin
270 // and the origin of the leftmost GlyphSet (TextLayoutComponent,
271 // actually). They were used for hanging punctuation support,
272 // which is no longer implemented. Currently they are both always 0,
273 // and TextLayout is not guaranteed to work with non-zero dx, dy
274 // values right now. They were left in as an aide and reminder to
275 // anyone who implements hanging punctuation or other similar stuff.
276 // They are static now so they don't take up space in TextLayout
277 // instances.
278 private static float dx;
279 private static float dy;
280
281 /*
282 * Natural bounds is used internally. It is built on demand in
283 * getNaturalBounds.
284 */
285 private Rectangle2D naturalBounds = null;
286
287 /*
288 * boundsRect encloses all of the bits this TextLayout can draw. It
289 * is build on demand in getBounds.
290 */
291 private Rectangle2D boundsRect = null;
292
293 /*
294 * flag to supress/allow carets inside of ligatures when hit testing or
295 * arrow-keying
296 */
297 private boolean caretsInLigaturesAreAllowed = false;
298
299 /**
300 * Defines a policy for determining the strong caret location.
301 * This class contains one method, <code>getStrongCaret</code>, which
302 * is used to specify the policy that determines the strong caret in
303 * dual-caret text. The strong caret is used to move the caret to the
304 * left or right. Instances of this class can be passed to
305 * <code>getCaretShapes</code>, <code>getNextLeftHit</code> and
306 * <code>getNextRightHit</code> to customize strong caret
307 * selection.
308 * <p>
309 * To specify alternate caret policies, subclass <code>CaretPolicy</code>
310 * and override <code>getStrongCaret</code>. <code>getStrongCaret</code>
311 * should inspect the two <code>TextHitInfo</code> arguments and choose
312 * one of them as the strong caret.
313 * <p>
314 * Most clients do not need to use this class.
315 */
316 public static class CaretPolicy {
317
318 /**
319 * Constructs a <code>CaretPolicy</code>.
320 */
321 public CaretPolicy() {
322 }
323
324 /**
325 * Chooses one of the specified <code>TextHitInfo</code> instances as
326 * a strong caret in the specified <code>TextLayout</code>.
327 * @param hit1 a valid hit in <code>layout</code>
328 * @param hit2 a valid hit in <code>layout</code>
329 * @param layout the <code>TextLayout</code> in which
330 * <code>hit1</code> and <code>hit2</code> are used
331 * @return <code>hit1</code> or <code>hit2</code>
332 * (or an equivalent <code>TextHitInfo</code>), indicating the
333 * strong caret.
334 */
335 public TextHitInfo getStrongCaret(TextHitInfo hit1,
336 TextHitInfo hit2,
337 TextLayout layout) {
338
339 // default implmentation just calls private method on layout
340 return layout.getStrongHit(hit1, hit2);
341 }
342 }
343
344 /**
345 * This <code>CaretPolicy</code> is used when a policy is not specified
346 * by the client. With this policy, a hit on a character whose direction
347 * is the same as the line direction is stronger than a hit on a
348 * counterdirectional character. If the characters' directions are
349 * the same, a hit on the leading edge of a character is stronger
350 * than a hit on the trailing edge of a character.
351 */
352 public static final CaretPolicy DEFAULT_CARET_POLICY = new CaretPolicy();
353
354 /**
355 * Constructs a <code>TextLayout</code> from a <code>String</code>
356 * and a {@link Font}. All the text is styled using the specified
357 * <code>Font</code>.
358 * <p>
359 * The <code>String</code> must specify a single paragraph of text,
360 * because an entire paragraph is required for the bidirectional
361 * algorithm.
362 * @param string the text to display
363 * @param font a <code>Font</code> used to style the text
364 * @param frc contains information about a graphics device which is needed
365 * to measure the text correctly.
366 * Text measurements can vary slightly depending on the
367 * device resolution, and attributes such as antialiasing. This
368 * parameter does not specify a translation between the
369 * <code>TextLayout</code> and user space.
370 */
371 public TextLayout(String string, Font font, FontRenderContext frc) {
372
373 if (font == null) {
374 throw new IllegalArgumentException("Null font passed to TextLayout constructor.");
375 }
376
377 if (string == null) {
378 throw new IllegalArgumentException("Null string passed to TextLayout constructor.");
379 }
380
381 if (string.length() == 0) {
382 throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");
383 }
384
385 Map attributes = null;
386 if (font.hasLayoutAttributes()) {
387 attributes = font.getAttributes();
388 }
389
390 char[] text = string.toCharArray();
391 if (sameBaselineUpTo(font, text, 0, text.length) == text.length) {
392 fastInit(text, font, attributes, frc);
393 } else {
394 AttributedString as = attributes == null
395 ? new AttributedString(string)
396 : new AttributedString(string, attributes);
397 as.addAttribute(TextAttribute.FONT, font);
398 standardInit(as.getIterator(), text, frc);
399 }
400 }
401
402 /**
403 * Constructs a <code>TextLayout</code> from a <code>String</code>
404 * and an attribute set.
405 * <p>
406 * All the text is styled using the provided attributes.
407 * <p>
408 * <code>string</code> must specify a single paragraph of text because an
409 * entire paragraph is required for the bidirectional algorithm.
410 * @param string the text to display
411 * @param attributes the attributes used to style the text
412 * @param frc contains information about a graphics device which is needed
413 * to measure the text correctly.
414 * Text measurements can vary slightly depending on the
415 * device resolution, and attributes such as antialiasing. This
416 * parameter does not specify a translation between the
417 * <code>TextLayout</code> and user space.
418 */
419 public TextLayout(String string, Map<? extends Attribute,?> attributes,
420 FontRenderContext frc)
421 {
422 if (string == null) {
423 throw new IllegalArgumentException("Null string passed to TextLayout constructor.");
424 }
425
426 if (attributes == null) {
427 throw new IllegalArgumentException("Null map passed to TextLayout constructor.");
428 }
429
430 if (string.length() == 0) {
431 throw new IllegalArgumentException("Zero length string passed to TextLayout constructor.");
432 }
433
434 char[] text = string.toCharArray();
435 Font font = singleFont(text, 0, text.length, attributes);
436 if (font != null) {
437 fastInit(text, font, attributes, frc);
438 } else {
439 AttributedString as = new AttributedString(string, attributes);
440 standardInit(as.getIterator(), text, frc);
441 }
442 }
443
444 /*
445 * Determines a font for the attributes, and if a single font can render
446 * all the text on one baseline, return it, otherwise null. If the
447 * attributes specify a font, assume it can display all the text without
448 * checking.
449 * If the AttributeSet contains an embedded graphic, return null.
450 */
451 private static Font singleFont(char[] text,
452 int start,
453 int limit,
454 Map attributes) {
455
456 if (attributes.get(TextAttribute.CHAR_REPLACEMENT) != null) {
457 return null;
458 }
459
460 Font font = null;
461 try {
462 font = (Font)attributes.get(TextAttribute.FONT);
463 }
464 catch (ClassCastException e) {
465 }
466 if (font == null) {
467 if (attributes.get(TextAttribute.FAMILY) != null) {
468 font = Font.getFont(attributes);
469 if (font.canDisplayUpTo(text, start, limit) != -1) {
470 return null;
471 }
472 } else {
473 FontResolver resolver = FontResolver.getInstance();
474 CodePointIterator iter = CodePointIterator.create(text, start, limit);
475 int fontIndex = resolver.nextFontRunIndex(iter);
476 if (iter.charIndex() == limit) {
477 font = resolver.getFont(fontIndex, attributes);
478 }
479 }
480 }
481
482 if (sameBaselineUpTo(font, text, start, limit) != limit) {
483 return null;
484 }
485
486 return font;
487 }
488
489 /**
490 * Constructs a <code>TextLayout</code> from an iterator over styled text.
491 * <p>
492 * The iterator must specify a single paragraph of text because an
493 * entire paragraph is required for the bidirectional
494 * algorithm.
495 * @param text the styled text to display
496 * @param frc contains information about a graphics device which is needed
497 * to measure the text correctly.
498 * Text measurements can vary slightly depending on the
499 * device resolution, and attributes such as antialiasing. This
500 * parameter does not specify a translation between the
501 * <code>TextLayout</code> and user space.
502 */
503 public TextLayout(AttributedCharacterIterator text, FontRenderContext frc) {
504
505 if (text == null) {
506 throw new IllegalArgumentException("Null iterator passed to TextLayout constructor.");
507 }
508
509 int start = text.getBeginIndex();
510 int limit = text.getEndIndex();
511 if (start == limit) {
512 throw new IllegalArgumentException("Zero length iterator passed to TextLayout constructor.");
513 }
514
515 int len = limit - start;
516 text.first();
517 char[] chars = new char[len];
518 int n = 0;
519 for (char c = text.first(); c != text.DONE; c = text.next()) {
520 chars[n++] = c;
521 }
522
523 text.first();
524 if (text.getRunLimit() == limit) {
525
526 Map attributes = text.getAttributes();
527 Font font = singleFont(chars, 0, len, attributes);
528 if (font != null) {
529 fastInit(chars, font, attributes, frc);
530 return;
531 }
532 }
533
534 standardInit(text, chars, frc);
535 }
536
537 /**
538 * Creates a <code>TextLayout</code> from a {@link TextLine} and
539 * some paragraph data. This method is used by {@link TextMeasurer}.
540 * @param textLine the line measurement attributes to apply to the
541 * the resulting <code>TextLayout</code>
542 * @param baseline the baseline of the text
543 * @param baselineOffsets the baseline offsets for this
544 * <code>TextLayout</code>. This should already be normalized to
545 * <code>baseline</code>
546 * @param justifyRatio <code>0</code> if the <code>TextLayout</code>
547 * cannot be justified; <code>1</code> otherwise.
548 */
549 TextLayout(TextLine textLine,
550 byte baseline,
551 float[] baselineOffsets,
552 float justifyRatio) {
553
554 this.characterCount = textLine.characterCount();
555 this.baseline = baseline;
556 this.baselineOffsets = baselineOffsets;
557 this.textLine = textLine;
558 this.justifyRatio = justifyRatio;
559 }
560
561 /**
562 * Initialize the paragraph-specific data.
563 */
564 private void paragraphInit(byte aBaseline, CoreMetrics lm, Map paragraphAttrs, char[] text) {
565
566 baseline = aBaseline;
567
568 // normalize to current baseline
569 baselineOffsets = TextLine.getNormalizedOffsets(lm.baselineOffsets, baseline);
570
571 justifyRatio = AttributeValues.getJustification(paragraphAttrs);
572 NumericShaper shaper = AttributeValues.getNumericShaping(paragraphAttrs);
573 if (shaper != null) {
574 shaper.shape(text, 0, text.length);
575 }
576 }
577
578 /*
579 * the fast init generates a single glyph set. This requires:
580 * all one style
581 * all renderable by one font (ie no embedded graphics)
582 * all on one baseline
583 */
584 private void fastInit(char[] chars, Font font, Map attrs, FontRenderContext frc) {
585 // Object vf = attrs.get(TextAttribute.ORIENTATION);
586 // isVerticalLine = TextAttribute.ORIENTATION_VERTICAL.equals(vf);
587 isVerticalLine = false;
588
589 LineMetrics lm = font.getLineMetrics(chars, 0, chars.length, frc);
590 CoreMetrics cm = CoreMetrics.get(lm);
591 byte glyphBaseline = (byte) cm.baselineIndex;
592
593 if (attrs == null) {
594 baseline = glyphBaseline;
595 baselineOffsets = cm.baselineOffsets;
596 justifyRatio = 1.0f;
597 } else {
598 paragraphInit(glyphBaseline, cm, attrs, chars);
599 }
600
601 characterCount = chars.length;
602
603 textLine = TextLine.fastCreateTextLine(frc, chars, font, cm, attrs);
604 }
605
606 /*
607 * the standard init generates multiple glyph sets based on style,
608 * renderable, and baseline runs.
609 * @param chars the text in the iterator, extracted into a char array
610 */
611 private void standardInit(AttributedCharacterIterator text, char[] chars, FontRenderContext frc) {
612
613 characterCount = chars.length;
614
615 // set paragraph attributes
616 {
617 // If there's an embedded graphic at the start of the
618 // paragraph, look for the first non-graphic character
619 // and use it and its font to initialize the paragraph.
620 // If not, use the first graphic to initialize.
621
622 Map paragraphAttrs = text.getAttributes();
623
624 boolean haveFont = TextLine.advanceToFirstFont(text);
625
626 if (haveFont) {
627 Font defaultFont = TextLine.getFontAtCurrentPos(text);
628 int charsStart = text.getIndex() - text.getBeginIndex();
629 LineMetrics lm = defaultFont.getLineMetrics(chars, charsStart, charsStart+1, frc);
630 CoreMetrics cm = CoreMetrics.get(lm);
631 paragraphInit((byte)cm.baselineIndex, cm, paragraphAttrs, chars);
632 }
633 else {
634 // hmmm what to do here? Just try to supply reasonable
635 // values I guess.
636
637 GraphicAttribute graphic = (GraphicAttribute)
638 paragraphAttrs.get(TextAttribute.CHAR_REPLACEMENT);
639 byte defaultBaseline = getBaselineFromGraphic(graphic);
640 CoreMetrics cm = GraphicComponent.createCoreMetrics(graphic);
641 paragraphInit(defaultBaseline, cm, paragraphAttrs, chars);
642 }
643 }
644
645 textLine = TextLine.standardCreateTextLine(frc, text, chars, baselineOffsets);
646 }
647
648 /*
649 * A utility to rebuild the ascent/descent/leading/advance cache.
650 * You'll need to call this if you clone and mutate (like justification,
651 * editing methods do)
652 */
653 private void ensureCache() {
654 if (!cacheIsValid) {
655 buildCache();
656 }
657 }
658
659 private void buildCache() {
660 lineMetrics = textLine.getMetrics();
661
662 // compute visibleAdvance
663 if (textLine.isDirectionLTR()) {
664
665 int lastNonSpace = characterCount-1;
666 while (lastNonSpace != -1) {
667 int logIndex = textLine.visualToLogical(lastNonSpace);
668 if (!textLine.isCharSpace(logIndex)) {
669 break;
670 }
671 else {
672 --lastNonSpace;
673 }
674 }
675 if (lastNonSpace == characterCount-1) {
676 visibleAdvance = lineMetrics.advance;
677 }
678 else if (lastNonSpace == -1) {
679 visibleAdvance = 0;
680 }
681 else {
682 int logIndex = textLine.visualToLogical(lastNonSpace);
683 visibleAdvance = textLine.getCharLinePosition(logIndex)
684 + textLine.getCharAdvance(logIndex);
685 }
686 }
687 else {
688
689 int leftmostNonSpace = 0;
690 while (leftmostNonSpace != characterCount) {
691 int logIndex = textLine.visualToLogical(leftmostNonSpace);
692 if (!textLine.isCharSpace(logIndex)) {
693 break;
694 }
695 else {
696 ++leftmostNonSpace;
697 }
698 }
699 if (leftmostNonSpace == characterCount) {
700 visibleAdvance = 0;
701 }
702 else if (leftmostNonSpace == 0) {
703 visibleAdvance = lineMetrics.advance;
704 }
705 else {
706 int logIndex = textLine.visualToLogical(leftmostNonSpace);
707 float pos = textLine.getCharLinePosition(logIndex);
708 visibleAdvance = lineMetrics.advance - pos;
709 }
710 }
711
712 // naturalBounds, boundsRect will be generated on demand
713 naturalBounds = null;
714 boundsRect = null;
715
716 // hashCode will be regenerated on demand
717 hashCodeCache = 0;
718
719 cacheIsValid = true;
720 }
721
722 /**
723 * The 'natural bounds' encloses all the carets the layout can draw.
724 *
725 */
726 private Rectangle2D getNaturalBounds() {
727 ensureCache();
728
729 if (naturalBounds == null) {
730 naturalBounds = textLine.getItalicBounds();
731 }
732
733 return naturalBounds;
734 }
735
736 /**
737 * Creates a copy of this <code>TextLayout</code>.
738 */
739 protected Object clone() {
740 /*
741 * !!! I think this is safe. Once created, nothing mutates the
742 * glyphvectors or arrays. But we need to make sure.
743 * {jbr} actually, that's not quite true. The justification code
744 * mutates after cloning. It doesn't actually change the glyphvectors
745 * (that's impossible) but it replaces them with justified sets. This
746 * is a problem for GlyphIterator creation, since new GlyphIterators
747 * are created by cloning a prototype. If the prototype has outdated
748 * glyphvectors, so will the new ones. A partial solution is to set the
749 * prototypical GlyphIterator to null when the glyphvectors change. If
750 * you forget this one time, you're hosed.
751 */
752 try {
753 return super.clone();
754 }
755 catch (CloneNotSupportedException e) {
756 throw new InternalError();
757 }
758 }
759
760 /*
761 * Utility to throw an expection if an invalid TextHitInfo is passed
762 * as a parameter. Avoids code duplication.
763 */
764 private void checkTextHit(TextHitInfo hit) {
765 if (hit == null) {
766 throw new IllegalArgumentException("TextHitInfo is null.");
767 }
768
769 if (hit.getInsertionIndex() < 0 ||
770 hit.getInsertionIndex() > characterCount) {
771 throw new IllegalArgumentException("TextHitInfo is out of range");
772 }
773 }
774
775 /**
776 * Creates a copy of this <code>TextLayout</code> justified to the
777 * specified width.
778 * <p>
779 * If this <code>TextLayout</code> has already been justified, an
780 * exception is thrown. If this <code>TextLayout</code> object's
781 * justification ratio is zero, a <code>TextLayout</code> identical
782 * to this <code>TextLayout</code> is returned.
783 * @param justificationWidth the width to use when justifying the line.
784 * For best results, it should not be too different from the current
785 * advance of the line.
786 * @return a <code>TextLayout</code> justified to the specified width.
787 * @exception Error if this layout has already been justified, an Error is
788 * thrown.
789 */
790 public TextLayout getJustifiedLayout(float justificationWidth) {
791
792 if (justificationWidth <= 0) {
793 throw new IllegalArgumentException("justificationWidth <= 0 passed to TextLayout.getJustifiedLayout()");
794 }
795
796 if (justifyRatio == ALREADY_JUSTIFIED) {
797 throw new Error("Can't justify again.");
798 }
799
800 ensureCache(); // make sure textLine is not null
801
802 // default justification range to exclude trailing logical whitespace
803 int limit = characterCount;
804 while (limit > 0 && textLine.isCharWhitespace(limit-1)) {
805 --limit;
806 }
807
808 TextLine newLine = textLine.getJustifiedLine(justificationWidth, justifyRatio, 0, limit);
809 if (newLine != null) {
810 return new TextLayout(newLine, baseline, baselineOffsets, ALREADY_JUSTIFIED);
811 }
812
813 return this;
814 }
815
816 /**
817 * Justify this layout. Overridden by subclassers to control justification
818 * (if there were subclassers, that is...)
819 *
820 * The layout will only justify if the paragraph attributes (from the
821 * source text, possibly defaulted by the layout attributes) indicate a
822 * non-zero justification ratio. The text will be justified to the
823 * indicated width. The current implementation also adjusts hanging
824 * punctuation and trailing whitespace to overhang the justification width.
825 * Once justified, the layout may not be rejustified.
826 * <p>
827 * Some code may rely on immutablity of layouts. Subclassers should not
828 * call this directly, but instead should call getJustifiedLayout, which
829 * will call this method on a clone of this layout, preserving
830 * the original.
831 *
832 * @param justificationWidth the width to use when justifying the line.
833 * For best results, it should not be too different from the current
834 * advance of the line.
835 * @see #getJustifiedLayout(float)
836 */
837 protected void handleJustify(float justificationWidth) {
838 // never called
839 }
840
841
842 /**
843 * Returns the baseline for this <code>TextLayout</code>.
844 * The baseline is one of the values defined in <code>Font</code>,
845 * which are roman, centered and hanging. Ascent and descent are
846 * relative to this baseline. The <code>baselineOffsets</code>
847 * are also relative to this baseline.
848 * @return the baseline of this <code>TextLayout</code>.
849 * @see #getBaselineOffsets()
850 * @see Font
851 */
852 public byte getBaseline() {
853 return baseline;
854 }
855
856 /**
857 * Returns the offsets array for the baselines used for this
858 * <code>TextLayout</code>.
859 * <p>
860 * The array is indexed by one of the values defined in
861 * <code>Font</code>, which are roman, centered and hanging. The
862 * values are relative to this <code>TextLayout</code> object's
863 * baseline, so that <code>getBaselineOffsets[getBaseline()] == 0</code>.
864 * Offsets are added to the position of the <code>TextLayout</code>
865 * object's baseline to get the position for the new baseline.
866 * @return the offsets array containing the baselines used for this
867 * <code>TextLayout</code>.
868 * @see #getBaseline()
869 * @see Font
870 */
871 public float[] getBaselineOffsets() {
872 float[] offsets = new float[baselineOffsets.length];
873 System.arraycopy(baselineOffsets, 0, offsets, 0, offsets.length);
874 return offsets;
875 }
876
877 /**
878 * Returns the advance of this <code>TextLayout</code>.
879 * The advance is the distance from the origin to the advance of the
880 * rightmost (bottommost) character. This is in baseline-relative
881 * coordinates.
882 * @return the advance of this <code>TextLayout</code>.
883 */
884 public float getAdvance() {
885 ensureCache();
886 return lineMetrics.advance;
887 }
888
889 /**
890 * Returns the advance of this <code>TextLayout</code>, minus trailing
891 * whitespace. This is in baseline-relative coordinates.
892 * @return the advance of this <code>TextLayout</code> without the
893 * trailing whitespace.
894 * @see #getAdvance()
895 */
896 public float getVisibleAdvance() {
897 ensureCache();
898 return visibleAdvance;
899 }
900
901 /**
902 * Returns the ascent of this <code>TextLayout</code>.
903 * The ascent is the distance from the top (right) of the
904 * <code>TextLayout</code> to the baseline. It is always either
905 * positive or zero. The ascent is sufficient to
906 * accomodate superscripted text and is the maximum of the sum of the
907 * ascent, offset, and baseline of each glyph. The ascent is
908 * the maximum ascent from the baseline of all the text in the
909 * TextLayout. It is in baseline-relative coordinates.
910 * @return the ascent of this <code>TextLayout</code>.
911 */
912 public float getAscent() {
913 ensureCache();
914 return lineMetrics.ascent;
915 }
916
917 /**
918 * Returns the descent of this <code>TextLayout</code>.
919 * The descent is the distance from the baseline to the bottom (left) of
920 * the <code>TextLayout</code>. It is always either positive or zero.
921 * The descent is sufficient to accomodate subscripted text and is the
922 * maximum of the sum of the descent, offset, and baseline of each glyph.
923 * This is the maximum descent from the baseline of all the text in
924 * the TextLayout. It is in baseline-relative coordinates.
925 * @return the descent of this <code>TextLayout</code>.
926 */
927 public float getDescent() {
928 ensureCache();
929 return lineMetrics.descent;
930 }
931
932 /**
933 * Returns the leading of the <code>TextLayout</code>.
934 * The leading is the suggested interline spacing for this
935 * <code>TextLayout</code>. This is in baseline-relative
936 * coordinates.
937 * <p>
938 * The leading is computed from the leading, descent, and baseline
939 * of all glyphvectors in the <code>TextLayout</code>. The algorithm
940 * is roughly as follows:
941 * <blockquote><pre>
942 * maxD = 0;
943 * maxDL = 0;
944 * for (GlyphVector g in all glyphvectors) {
945 * maxD = max(maxD, g.getDescent() + offsets[g.getBaseline()]);
946 * maxDL = max(maxDL, g.getDescent() + g.getLeading() +
947 * offsets[g.getBaseline()]);
948 * }
949 * return maxDL - maxD;
950 * </pre></blockquote>
951 * @return the leading of this <code>TextLayout</code>.
952 */
953 public float getLeading() {
954 ensureCache();
955 return lineMetrics.leading;
956 }
957
958 /**
959 * Returns the bounds of this <code>TextLayout</code>.
960 * The bounds are in standard coordinates.
961 * <p>Due to rasterization effects, this bounds might not enclose all of the
962 * pixels rendered by the TextLayout.</p>
963 * It might not coincide exactly with the ascent, descent,
964 * origin or advance of the <code>TextLayout</code>.
965 * @return a {@link Rectangle2D} that is the bounds of this
966 * <code>TextLayout</code>.
967 */
968 public Rectangle2D getBounds() {
969 ensureCache();
970
971 if (boundsRect == null) {
972 Rectangle2D vb = textLine.getVisualBounds();
973 if (dx != 0 || dy != 0) {
974 vb.setRect(vb.getX() - dx,
975 vb.getY() - dy,
976 vb.getWidth(),
977 vb.getHeight());
978 }
979 boundsRect = vb;
980 }
981
982 Rectangle2D bounds = new Rectangle2D.Float();
983 bounds.setRect(boundsRect);
984
985 return bounds;
986 }
987
988 /**
989 * Returns the pixel bounds of this <code>TextLayout</code> when
990 * rendered in a graphics with the given
991 * <code>FontRenderContext</code> at the given location. The
992 * graphics render context need not be the same as the
993 * <code>FontRenderContext</code> used to create this
994 * <code>TextLayout</code>, and can be null. If it is null, the
995 * <code>FontRenderContext</code> of this <code>TextLayout</code>
996 * is used.
997 * @param frc the <code>FontRenderContext</code> of the <code>Graphics</code>.
998 * @param x the x-coordinate at which to render this <code>TextLayout</code>.
999 * @param y the y-coordinate at which to render this <code>TextLayout</code>.
1000 * @return a <code>Rectangle</code> bounding the pixels that would be affected.
1001 * @see GlyphVector#getPixelBounds
1002 * @since 1.6
1003 */
1004 public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
1005 return textLine.getPixelBounds(frc, x, y);
1006 }
1007
1008 /**
1009 * Returns <code>true</code> if this <code>TextLayout</code> has
1010 * a left-to-right base direction or <code>false</code> if it has
1011 * a right-to-left base direction. The <code>TextLayout</code>
1012 * has a base direction of either left-to-right (LTR) or
1013 * right-to-left (RTL). The base direction is independent of the
1014 * actual direction of text on the line, which may be either LTR,
1015 * RTL, or mixed. Left-to-right layouts by default should position
1016 * flush left. If the layout is on a tabbed line, the
1017 * tabs run left to right, so that logically successive layouts position
1018 * left to right. The opposite is true for RTL layouts. By default they
1019 * should position flush left, and tabs run right-to-left.
1020 * @return <code>true</code> if the base direction of this
1021 * <code>TextLayout</code> is left-to-right; <code>false</code>
1022 * otherwise.
1023 */
1024 public boolean isLeftToRight() {
1025 return textLine.isDirectionLTR();
1026 }
1027
1028 /**
1029 * Returns <code>true</code> if this <code>TextLayout</code> is vertical.
1030 * @return <code>true</code> if this <code>TextLayout</code> is vertical;
1031 * <code>false</code> otherwise.
1032 */
1033 public boolean isVertical() {
1034 return isVerticalLine;
1035 }
1036
1037 /**
1038 * Returns the number of characters represented by this
1039 * <code>TextLayout</code>.
1040 * @return the number of characters in this <code>TextLayout</code>.
1041 */
1042 public int getCharacterCount() {
1043 return characterCount;
1044 }
1045
1046 /*
1047 * carets and hit testing
1048 *
1049 * Positions on a text line are represented by instances of TextHitInfo.
1050 * Any TextHitInfo with characterOffset between 0 and characterCount-1,
1051 * inclusive, represents a valid position on the line. Additionally,
1052 * [-1, trailing] and [characterCount, leading] are valid positions, and
1053 * represent positions at the logical start and end of the line,
1054 * respectively.
1055 *
1056 * The characterOffsets in TextHitInfo's used and returned by TextLayout
1057 * are relative to the beginning of the text layout, not necessarily to
1058 * the beginning of the text storage the client is using.
1059 *
1060 *
1061 * Every valid TextHitInfo has either one or two carets associated with it.
1062 * A caret is a visual location in the TextLayout indicating where text at
1063 * the TextHitInfo will be displayed on screen. If a TextHitInfo
1064 * represents a location on a directional boundary, then there are two
1065 * possible visible positions for newly inserted text. Consider the
1066 * following example, in which capital letters indicate right-to-left text,
1067 * and the overall line direction is left-to-right:
1068 *
1069 * Text Storage: [ a, b, C, D, E, f ]
1070 * Display: a b E D C f
1071 *
1072 * The text hit info (1, t) represents the trailing side of 'b'. If 'q',
1073 * a left-to-right character is inserted into the text storage at this
1074 * location, it will be displayed between the 'b' and the 'E':
1075 *
1076 * Text Storage: [ a, b, q, C, D, E, f ]
1077 * Display: a b q E D C f
1078 *
1079 * However, if a 'W', which is right-to-left, is inserted into the storage
1080 * after 'b', the storage and display will be:
1081 *
1082 * Text Storage: [ a, b, W, C, D, E, f ]
1083 * Display: a b E D C W f
1084 *
1085 * So, for the original text storage, two carets should be displayed for
1086 * location (1, t): one visually between 'b' and 'E' and one visually
1087 * between 'C' and 'f'.
1088 *
1089 *
1090 * When two carets are displayed for a TextHitInfo, one caret is the
1091 * 'strong' caret and the other is the 'weak' caret. The strong caret
1092 * indicates where an inserted character will be displayed when that
1093 * character's direction is the same as the direction of the TextLayout.
1094 * The weak caret shows where an character inserted character will be
1095 * displayed when the character's direction is opposite that of the
1096 * TextLayout.
1097 *
1098 *
1099 * Clients should not be overly concerned with the details of correct
1100 * caret display. TextLayout.getCaretShapes(TextHitInfo) will return an
1101 * array of two paths representing where carets should be displayed.
1102 * The first path in the array is the strong caret; the second element,
1103 * if non-null, is the weak caret. If the second element is null,
1104 * then there is no weak caret for the given TextHitInfo.
1105 *
1106 *
1107 * Since text can be visually reordered, logically consecutive
1108 * TextHitInfo's may not be visually consecutive. One implication of this
1109 * is that a client cannot tell from inspecting a TextHitInfo whether the
1110 * hit represents the first (or last) caret in the layout. Clients
1111 * can call getVisualOtherHit(); if the visual companion is
1112 * (-1, TRAILING) or (characterCount, LEADING), then the hit is at the
1113 * first (last) caret position in the layout.
1114 */
1115
1116 private float[] getCaretInfo(int caret,
1117 Rectangle2D bounds,
1118 float[] info) {
1119
1120 float top1X, top2X;
1121 float bottom1X, bottom2X;
1122
1123 if (caret == 0 || caret == characterCount) {
1124
1125 float pos;
1126 int logIndex;
1127 if (caret == characterCount) {
1128 logIndex = textLine.visualToLogical(characterCount-1);
1129 pos = textLine.getCharLinePosition(logIndex)
1130 + textLine.getCharAdvance(logIndex);
1131 }
1132 else {
1133 logIndex = textLine.visualToLogical(caret);
1134 pos = textLine.getCharLinePosition(logIndex);
1135 }
1136 float angle = textLine.getCharAngle(logIndex);
1137 float shift = textLine.getCharShift(logIndex);
1138 pos += angle * shift;
1139 top1X = top2X = pos + angle*textLine.getCharAscent(logIndex);
1140 bottom1X = bottom2X = pos - angle*textLine.getCharDescent(logIndex);
1141 }
1142 else {
1143
1144 {
1145 int logIndex = textLine.visualToLogical(caret-1);
1146 float angle1 = textLine.getCharAngle(logIndex);
1147 float pos1 = textLine.getCharLinePosition(logIndex)
1148 + textLine.getCharAdvance(logIndex);
1149 if (angle1 != 0) {
1150 pos1 += angle1 * textLine.getCharShift(logIndex);
1151 top1X = pos1 + angle1*textLine.getCharAscent(logIndex);
1152 bottom1X = pos1 - angle1*textLine.getCharDescent(logIndex);
1153 }
1154 else {
1155 top1X = bottom1X = pos1;
1156 }
1157 }
1158 {
1159 int logIndex = textLine.visualToLogical(caret);
1160 float angle2 = textLine.getCharAngle(logIndex);
1161 float pos2 = textLine.getCharLinePosition(logIndex);
1162 if (angle2 != 0) {
1163 pos2 += angle2*textLine.getCharShift(logIndex);
1164 top2X = pos2 + angle2*textLine.getCharAscent(logIndex);
1165 bottom2X = pos2 - angle2*textLine.getCharDescent(logIndex);
1166 }
1167 else {
1168 top2X = bottom2X = pos2;
1169 }
1170 }
1171 }
1172
1173 float topX = (top1X + top2X) / 2;
1174 float bottomX = (bottom1X + bottom2X) / 2;
1175
1176 if (info == null) {
1177 info = new float[2];
1178 }
1179
1180 if (isVerticalLine) {
1181 info[1] = (float) ((topX - bottomX) / bounds.getWidth());
1182 info[0] = (float) (topX + (info[1]*bounds.getX()));
1183 }
1184 else {
1185 info[1] = (float) ((topX - bottomX) / bounds.getHeight());
1186 info[0] = (float) (bottomX + (info[1]*bounds.getMaxY()));
1187 }
1188
1189 return info;
1190 }
1191
1192 /**
1193 * Returns information about the caret corresponding to <code>hit</code>.
1194 * The first element of the array is the intersection of the caret with
1195 * the baseline, as a distance along the baseline. The second element
1196 * of the array is the inverse slope (run/rise) of the caret, measured
1197 * with respect to the baseline at that point.
1198 * <p>
1199 * This method is meant for informational use. To display carets, it
1200 * is better to use <code>getCaretShapes</code>.
1201 * @param hit a hit on a character in this <code>TextLayout</code>
1202 * @param bounds the bounds to which the caret info is constructed.
1203 * The bounds is in baseline-relative coordinates.
1204 * @return a two-element array containing the position and slope of
1205 * the caret. The returned caret info is in baseline-relative coordinates.
1206 * @see #getCaretShapes(int, Rectangle2D, TextLayout.CaretPolicy)
1207 * @see Font#getItalicAngle
1208 */
1209 public float[] getCaretInfo(TextHitInfo hit, Rectangle2D bounds) {
1210 ensureCache();
1211 checkTextHit(hit);
1212
1213 return getCaretInfoTestInternal(hit, bounds);
1214 }
1215
1216 // this version provides extra info in the float array
1217 // the first two values are as above
1218 // the next four values are the endpoints of the caret, as computed
1219 // using the hit character's offset (baseline + ssoffset) and
1220 // natural ascent and descent.
1221 // these values are trimmed to the bounds where required to fit,
1222 // but otherwise independent of it.
1223 private float[] getCaretInfoTestInternal(TextHitInfo hit, Rectangle2D bounds) {
1224 ensureCache();
1225 checkTextHit(hit);
1226
1227 float[] info = new float[6];
1228
1229 // get old data first
1230 getCaretInfo(hitToCaret(hit), bounds, info);
1231
1232 // then add our new data
1233 double iangle, ixbase, p1x, p1y, p2x, p2y;
1234
1235 int charix = hit.getCharIndex();
1236 boolean lead = hit.isLeadingEdge();
1237 boolean ltr = textLine.isDirectionLTR();
1238 boolean horiz = !isVertical();
1239
1240 if (charix == -1 || charix == characterCount) {
1241 // !!! note: want non-shifted, baseline ascent and descent here!
1242 // TextLine should return appropriate line metrics object for these values
1243 TextLineMetrics m = textLine.getMetrics();
1244 boolean low = ltr == (charix == -1);
1245 iangle = 0;
1246 if (horiz) {
1247 p1x = p2x = low ? 0 : m.advance;
1248 p1y = -m.ascent;
1249 p2y = m.descent;
1250 } else {
1251 p1y = p2y = low ? 0 : m.advance;
1252 p1x = m.descent;
1253 p2x = m.ascent;
1254 }
1255 } else {
1256 CoreMetrics thiscm = textLine.getCoreMetricsAt(charix);
1257 iangle = thiscm.italicAngle;
1258 ixbase = textLine.getCharLinePosition(charix, lead);
1259 if (thiscm.baselineIndex < 0) {
1260 // this is a graphic, no italics, use entire line height for caret
1261 TextLineMetrics m = textLine.getMetrics();
1262 if (horiz) {
1263 p1x = p2x = ixbase;
1264 if (thiscm.baselineIndex == GraphicAttribute.TOP_ALIGNMENT) {
1265 p1y = -m.ascent;
1266 p2y = p1y + thiscm.height;
1267 } else {
1268 p2y = m.descent;
1269 p1y = p2y - thiscm.height;
1270 }
1271 } else {
1272 p1y = p2y = ixbase;
1273 p1x = m.descent;
1274 p2x = m.ascent;
1275 // !!! top/bottom adjustment not implemented for vertical
1276 }
1277 } else {
1278 float bo = baselineOffsets[thiscm.baselineIndex];
1279 if (horiz) {
1280 ixbase += iangle * thiscm.ssOffset;
1281 p1x = ixbase + iangle * thiscm.ascent;
1282 p2x = ixbase - iangle * thiscm.descent;
1283 p1y = bo - thiscm.ascent;
1284 p2y = bo + thiscm.descent;
1285 } else {
1286 ixbase -= iangle * thiscm.ssOffset;
1287 p1y = ixbase + iangle * thiscm.ascent;
1288 p2y = ixbase - iangle * thiscm.descent;
1289 p1x = bo + thiscm.ascent;
1290 p2x = bo + thiscm.descent;
1291 }
1292 }
1293 }
1294
1295 info[2] = (float)p1x;
1296 info[3] = (float)p1y;
1297 info[4] = (float)p2x;
1298 info[5] = (float)p2y;
1299
1300 return info;
1301 }
1302
1303 /**
1304 * Returns information about the caret corresponding to <code>hit</code>.
1305 * This method is a convenience overload of <code>getCaretInfo</code> and
1306 * uses the natural bounds of this <code>TextLayout</code>.
1307 * @param hit a hit on a character in this <code>TextLayout</code>
1308 * @return the information about a caret corresponding to a hit. The
1309 * returned caret info is in baseline-relative coordinates.
1310 */
1311 public float[] getCaretInfo(TextHitInfo hit) {
1312
1313 return getCaretInfo(hit, getNaturalBounds());
1314 }
1315
1316 /**
1317 * Returns a caret index corresponding to <code>hit</code>.
1318 * Carets are numbered from left to right (top to bottom) starting from
1319 * zero. This always places carets next to the character hit, on the
1320 * indicated side of the character.
1321 * @param hit a hit on a character in this <code>TextLayout</code>
1322 * @return a caret index corresponding to the specified hit.
1323 */
1324 private int hitToCaret(TextHitInfo hit) {
1325
1326 int hitIndex = hit.getCharIndex();
1327
1328 if (hitIndex < 0) {
1329 return textLine.isDirectionLTR() ? 0 : characterCount;
1330 } else if (hitIndex >= characterCount) {
1331 return textLine.isDirectionLTR() ? characterCount : 0;
1332 }
1333
1334 int visIndex = textLine.logicalToVisual(hitIndex);
1335
1336 if (hit.isLeadingEdge() != textLine.isCharLTR(hitIndex)) {
1337 ++visIndex;
1338 }
1339
1340 return visIndex;
1341 }
1342
1343 /**
1344 * Given a caret index, return a hit whose caret is at the index.
1345 * The hit is NOT guaranteed to be strong!!!
1346 *
1347 * @param caret a caret index.
1348 * @return a hit on this layout whose strong caret is at the requested
1349 * index.
1350 */
1351 private TextHitInfo caretToHit(int caret) {
1352
1353 if (caret == 0 || caret == characterCount) {
1354
1355 if ((caret == characterCount) == textLine.isDirectionLTR()) {
1356 return TextHitInfo.leading(characterCount);
1357 }
1358 else {
1359 return TextHitInfo.trailing(-1);
1360 }
1361 }
1362 else {
1363
1364 int charIndex = textLine.visualToLogical(caret);
1365 boolean leading = textLine.isCharLTR(charIndex);
1366
1367 return leading? TextHitInfo.leading(charIndex)
1368 : TextHitInfo.trailing(charIndex);
1369 }
1370 }
1371
1372 private boolean caretIsValid(int caret) {
1373
1374 if (caret == characterCount || caret == 0) {
1375 return true;
1376 }
1377
1378 int offset = textLine.visualToLogical(caret);
1379
1380 if (!textLine.isCharLTR(offset)) {
1381 offset = textLine.visualToLogical(caret-1);
1382 if (textLine.isCharLTR(offset)) {
1383 return true;
1384 }
1385 }
1386
1387 // At this point, the leading edge of the character
1388 // at offset is at the given caret.
1389
1390 return textLine.caretAtOffsetIsValid(offset);
1391 }
1392
1393 /**
1394 * Returns the hit for the next caret to the right (bottom); if there
1395 * is no such hit, returns <code>null</code>.
1396 * If the hit character index is out of bounds, an
1397 * {@link IllegalArgumentException} is thrown.
1398 * @param hit a hit on a character in this layout
1399 * @return a hit whose caret appears at the next position to the
1400 * right (bottom) of the caret of the provided hit or <code>null</code>.
1401 */
1402 public TextHitInfo getNextRightHit(TextHitInfo hit) {
1403 ensureCache();
1404 checkTextHit(hit);
1405
1406 int caret = hitToCaret(hit);
1407
1408 if (caret == characterCount) {
1409 return null;
1410 }
1411
1412 do {
1413 ++caret;
1414 } while (!caretIsValid(caret));
1415
1416 return caretToHit(caret);
1417 }
1418
1419 /**
1420 * Returns the hit for the next caret to the right (bottom); if no
1421 * such hit, returns <code>null</code>. The hit is to the right of
1422 * the strong caret at the specified offset, as determined by the
1423 * specified policy.
1424 * The returned hit is the stronger of the two possible
1425 * hits, as determined by the specified policy.
1426 * @param offset an insertion offset in this <code>TextLayout</code>.
1427 * Cannot be less than 0 or greater than this <code>TextLayout</code>
1428 * object's character count.
1429 * @param policy the policy used to select the strong caret
1430 * @return a hit whose caret appears at the next position to the
1431 * right (bottom) of the caret of the provided hit, or <code>null</code>.
1432 */
1433 public TextHitInfo getNextRightHit(int offset, CaretPolicy policy) {
1434
1435 if (offset < 0 || offset > characterCount) {
1436 throw new IllegalArgumentException("Offset out of bounds in TextLayout.getNextRightHit()");
1437 }
1438
1439 if (policy == null) {
1440 throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getNextRightHit()");
1441 }
1442
1443 TextHitInfo hit1 = TextHitInfo.afterOffset(offset);
1444 TextHitInfo hit2 = hit1.getOtherHit();
1445
1446 TextHitInfo nextHit = getNextRightHit(policy.getStrongCaret(hit1, hit2, this));
1447
1448 if (nextHit != null) {
1449 TextHitInfo otherHit = getVisualOtherHit(nextHit);
1450 return policy.getStrongCaret(otherHit, nextHit, this);
1451 }
1452 else {
1453 return null;
1454 }
1455 }
1456
1457 /**
1458 * Returns the hit for the next caret to the right (bottom); if no
1459 * such hit, returns <code>null</code>. The hit is to the right of
1460 * the strong caret at the specified offset, as determined by the
1461 * default policy.
1462 * The returned hit is the stronger of the two possible
1463 * hits, as determined by the default policy.
1464 * @param offset an insertion offset in this <code>TextLayout</code>.
1465 * Cannot be less than 0 or greater than the <code>TextLayout</code>
1466 * object's character count.
1467 * @return a hit whose caret appears at the next position to the
1468 * right (bottom) of the caret of the provided hit, or <code>null</code>.
1469 */
1470 public TextHitInfo getNextRightHit(int offset) {
1471
1472 return getNextRightHit(offset, DEFAULT_CARET_POLICY);
1473 }
1474
1475 /**
1476 * Returns the hit for the next caret to the left (top); if no such
1477 * hit, returns <code>null</code>.
1478 * If the hit character index is out of bounds, an
1479 * <code>IllegalArgumentException</code> is thrown.
1480 * @param hit a hit on a character in this <code>TextLayout</code>.
1481 * @return a hit whose caret appears at the next position to the
1482 * left (top) of the caret of the provided hit, or <code>null</code>.
1483 */
1484 public TextHitInfo getNextLeftHit(TextHitInfo hit) {
1485 ensureCache();
1486 checkTextHit(hit);
1487
1488 int caret = hitToCaret(hit);
1489
1490 if (caret == 0) {
1491 return null;
1492 }
1493
1494 do {
1495 --caret;
1496 } while(!caretIsValid(caret));
1497
1498 return caretToHit(caret);
1499 }
1500
1501 /**
1502 * Returns the hit for the next caret to the left (top); if no
1503 * such hit, returns <code>null</code>. The hit is to the left of
1504 * the strong caret at the specified offset, as determined by the
1505 * specified policy.
1506 * The returned hit is the stronger of the two possible
1507 * hits, as determined by the specified policy.
1508 * @param offset an insertion offset in this <code>TextLayout</code>.
1509 * Cannot be less than 0 or greater than this <code>TextLayout</code>
1510 * object's character count.
1511 * @param policy the policy used to select the strong caret
1512 * @return a hit whose caret appears at the next position to the
1513 * left (top) of the caret of the provided hit, or <code>null</code>.
1514 */
1515 public TextHitInfo getNextLeftHit(int offset, CaretPolicy policy) {
1516
1517 if (policy == null) {
1518 throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getNextLeftHit()");
1519 }
1520
1521 if (offset < 0 || offset > characterCount) {
1522 throw new IllegalArgumentException("Offset out of bounds in TextLayout.getNextLeftHit()");
1523 }
1524
1525 TextHitInfo hit1 = TextHitInfo.afterOffset(offset);
1526 TextHitInfo hit2 = hit1.getOtherHit();
1527
1528 TextHitInfo nextHit = getNextLeftHit(policy.getStrongCaret(hit1, hit2, this));
1529
1530 if (nextHit != null) {
1531 TextHitInfo otherHit = getVisualOtherHit(nextHit);
1532 return policy.getStrongCaret(otherHit, nextHit, this);
1533 }
1534 else {
1535 return null;
1536 }
1537 }
1538
1539 /**
1540 * Returns the hit for the next caret to the left (top); if no
1541 * such hit, returns <code>null</code>. The hit is to the left of
1542 * the strong caret at the specified offset, as determined by the
1543 * default policy.
1544 * The returned hit is the stronger of the two possible
1545 * hits, as determined by the default policy.
1546 * @param offset an insertion offset in this <code>TextLayout</code>.
1547 * Cannot be less than 0 or greater than this <code>TextLayout</code>
1548 * object's character count.
1549 * @return a hit whose caret appears at the next position to the
1550 * left (top) of the caret of the provided hit, or <code>null</code>.
1551 */
1552 public TextHitInfo getNextLeftHit(int offset) {
1553
1554 return getNextLeftHit(offset, DEFAULT_CARET_POLICY);
1555 }
1556
1557 /**
1558 * Returns the hit on the opposite side of the specified hit's caret.
1559 * @param hit the specified hit
1560 * @return a hit that is on the opposite side of the specified hit's
1561 * caret.
1562 */
1563 public TextHitInfo getVisualOtherHit(TextHitInfo hit) {
1564
1565 ensureCache();
1566 checkTextHit(hit);
1567
1568 int hitCharIndex = hit.getCharIndex();
1569
1570 int charIndex;
1571 boolean leading;
1572
1573 if (hitCharIndex == -1 || hitCharIndex == characterCount) {
1574
1575 int visIndex;
1576 if (textLine.isDirectionLTR() == (hitCharIndex == -1)) {
1577 visIndex = 0;
1578 }
1579 else {
1580 visIndex = characterCount-1;
1581 }
1582
1583 charIndex = textLine.visualToLogical(visIndex);
1584
1585 if (textLine.isDirectionLTR() == (hitCharIndex == -1)) {
1586 // at left end
1587 leading = textLine.isCharLTR(charIndex);
1588 }
1589 else {
1590 // at right end
1591 leading = !textLine.isCharLTR(charIndex);
1592 }
1593 }
1594 else {
1595
1596 int visIndex = textLine.logicalToVisual(hitCharIndex);
1597
1598 boolean movedToRight;
1599 if (textLine.isCharLTR(hitCharIndex) == hit.isLeadingEdge()) {
1600 --visIndex;
1601 movedToRight = false;
1602 }
1603 else {
1604 ++visIndex;
1605 movedToRight = true;
1606 }
1607
1608 if (visIndex > -1 && visIndex < characterCount) {
1609 charIndex = textLine.visualToLogical(visIndex);
1610 leading = movedToRight == textLine.isCharLTR(charIndex);
1611 }
1612 else {
1613 charIndex =
1614 (movedToRight == textLine.isDirectionLTR())? characterCount : -1;
1615 leading = charIndex == characterCount;
1616 }
1617 }
1618
1619 return leading? TextHitInfo.leading(charIndex) :
1620 TextHitInfo.trailing(charIndex);
1621 }
1622
1623 private double[] getCaretPath(TextHitInfo hit, Rectangle2D bounds) {
1624 float[] info = getCaretInfo(hit, bounds);
1625 return new double[] { info[2], info[3], info[4], info[5] };
1626 }
1627
1628 /**
1629 * Return an array of four floats corresponding the endpoints of the caret
1630 * x0, y0, x1, y1.
1631 *
1632 * This creates a line along the slope of the caret intersecting the
1633 * baseline at the caret
1634 * position, and extending from ascent above the baseline to descent below
1635 * it.
1636 */
1637 private double[] getCaretPath(int caret, Rectangle2D bounds,
1638 boolean clipToBounds) {
1639
1640 float[] info = getCaretInfo(caret, bounds, null);
1641
1642 double pos = info[0];
1643 double slope = info[1];
1644
1645 double x0, y0, x1, y1;
1646 double x2 = -3141.59, y2 = -2.7; // values are there to make compiler happy
1647
1648 double left = bounds.getX();
1649 double right = left + bounds.getWidth();
1650 double top = bounds.getY();
1651 double bottom = top + bounds.getHeight();
1652
1653 boolean threePoints = false;
1654
1655 if (isVerticalLine) {
1656
1657 if (slope >= 0) {
1658 x0 = left;
1659 x1 = right;
1660 }
1661 else {
1662 x1 = left;
1663 x0 = right;
1664 }
1665
1666 y0 = pos + x0 * slope;
1667 y1 = pos + x1 * slope;
1668
1669 // y0 <= y1, always
1670
1671 if (clipToBounds) {
1672 if (y0 < top) {
1673 if (slope <= 0 || y1 <= top) {
1674 y0 = y1 = top;
1675 }
1676 else {
1677 threePoints = true;
1678 y0 = top;
1679 y2 = top;
1680 x2 = x1 + (top-y1)/slope;
1681 if (y1 > bottom) {
1682 y1 = bottom;
1683 }
1684 }
1685 }
1686 else if (y1 > bottom) {
1687 if (slope >= 0 || y0 >= bottom) {
1688 y0 = y1 = bottom;
1689 }
1690 else {
1691 threePoints = true;
1692 y1 = bottom;
1693 y2 = bottom;
1694 x2 = x0 + (bottom-x1)/slope;
1695 }
1696 }
1697 }
1698
1699 }
1700 else {
1701
1702 if (slope >= 0) {
1703 y0 = bottom;
1704 y1 = top;
1705 }
1706 else {
1707 y1 = bottom;
1708 y0 = top;
1709 }
1710
1711 x0 = pos - y0 * slope;
1712 x1 = pos - y1 * slope;
1713
1714 // x0 <= x1, always
1715
1716 if (clipToBounds) {
1717 if (x0 < left) {
1718 if (slope <= 0 || x1 <= left) {
1719 x0 = x1 = left;
1720 }
1721 else {
1722 threePoints = true;
1723 x0 = left;
1724 x2 = left;
1725 y2 = y1 - (left-x1)/slope;
1726 if (x1 > right) {
1727 x1 = right;
1728 }
1729 }
1730 }
1731 else if (x1 > right) {
1732 if (slope >= 0 || x0 >= right) {
1733 x0 = x1 = right;
1734 }
1735 else {
1736 threePoints = true;
1737 x1 = right;
1738 x2 = right;
1739 y2 = y0 - (right-x0)/slope;
1740 }
1741 }
1742 }
1743 }
1744
1745 return threePoints?
1746 new double[] { x0, y0, x2, y2, x1, y1 } :
1747 new double[] { x0, y0, x1, y1 };
1748 }
1749
1750
1751 private static GeneralPath pathToShape(double[] path, boolean close, LayoutPathImpl lp) {
1752 GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD, path.length);
1753 result.moveTo((float)path[0], (float)path[1]);
1754 for (int i = 2; i < path.length; i += 2) {
1755 result.lineTo((float)path[i], (float)path[i+1]);
1756 }
1757 if (close) {
1758 result.closePath();
1759 }
1760
1761 if (lp != null) {
1762 result = (GeneralPath)lp.mapShape(result);
1763 }
1764 return result;
1765 }
1766
1767 /**
1768 * Returns a {@link Shape} representing the caret at the specified
1769 * hit inside the specified bounds.
1770 * @param hit the hit at which to generate the caret
1771 * @param bounds the bounds of the <code>TextLayout</code> to use
1772 * in generating the caret. The bounds is in baseline-relative
1773 * coordinates.
1774 * @return a <code>Shape</code> representing the caret. The returned
1775 * shape is in standard coordinates.
1776 */
1777 public Shape getCaretShape(TextHitInfo hit, Rectangle2D bounds) {
1778 ensureCache();
1779 checkTextHit(hit);
1780
1781 if (bounds == null) {
1782 throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaret()");
1783 }
1784
1785 return pathToShape(getCaretPath(hit, bounds), false, textLine.getLayoutPath());
1786 }
1787
1788 /**
1789 * Returns a <code>Shape</code> representing the caret at the specified
1790 * hit inside the natural bounds of this <code>TextLayout</code>.
1791 * @param hit the hit at which to generate the caret
1792 * @return a <code>Shape</code> representing the caret. The returned
1793 * shape is in standard coordinates.
1794 */
1795 public Shape getCaretShape(TextHitInfo hit) {
1796
1797 return getCaretShape(hit, getNaturalBounds());
1798 }
1799
1800 /**
1801 * Return the "stronger" of the TextHitInfos. The TextHitInfos
1802 * should be logical or visual counterparts. They are not
1803 * checked for validity.
1804 */
1805 private final TextHitInfo getStrongHit(TextHitInfo hit1, TextHitInfo hit2) {
1806
1807 // right now we're using the following rule for strong hits:
1808 // A hit on a character with a lower level
1809 // is stronger than one on a character with a higher level.
1810 // If this rule ties, the hit on the leading edge of a character wins.
1811 // If THIS rule ties, hit1 wins. Both rules shouldn't tie, unless the
1812 // infos aren't counterparts of some sort.
1813
1814 byte hit1Level = getCharacterLevel(hit1.getCharIndex());
1815 byte hit2Level = getCharacterLevel(hit2.getCharIndex());
1816
1817 if (hit1Level == hit2Level) {
1818 if (hit2.isLeadingEdge() && !hit1.isLeadingEdge()) {
1819 return hit2;
1820 }
1821 else {
1822 return hit1;
1823 }
1824 }
1825 else {
1826 return (hit1Level < hit2Level)? hit1 : hit2;
1827 }
1828 }
1829
1830 /**
1831 * Returns the level of the character at <code>index</code>.
1832 * Indices -1 and <code>characterCount</code> are assigned the base
1833 * level of this <code>TextLayout</code>.
1834 * @param index the index of the character from which to get the level
1835 * @return the level of the character at the specified index.
1836 */
1837 public byte getCharacterLevel(int index) {
1838
1839 // hmm, allow indices at endpoints? For now, yes.
1840 if (index < -1 || index > characterCount) {
1841 throw new IllegalArgumentException("Index is out of range in getCharacterLevel.");
1842 }
1843
1844 ensureCache();
1845 if (index == -1 || index == characterCount) {
1846 return (byte) (textLine.isDirectionLTR()? 0 : 1);
1847 }
1848
1849 return textLine.getCharLevel(index);
1850 }
1851
1852 /**
1853 * Returns two paths corresponding to the strong and weak caret.
1854 * @param offset an offset in this <code>TextLayout</code>
1855 * @param bounds the bounds to which to extend the carets. The
1856 * bounds is in baseline-relative coordinates.
1857 * @param policy the specified <code>CaretPolicy</code>
1858 * @return an array of two paths. Element zero is the strong
1859 * caret. If there are two carets, element one is the weak caret,
1860 * otherwise it is <code>null</code>. The returned shapes
1861 * are in standard coordinates.
1862 */
1863 public Shape[] getCaretShapes(int offset, Rectangle2D bounds, CaretPolicy policy) {
1864
1865 ensureCache();
1866
1867 if (offset < 0 || offset > characterCount) {
1868 throw new IllegalArgumentException("Offset out of bounds in TextLayout.getCaretShapes()");
1869 }
1870
1871 if (bounds == null) {
1872 throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getCaretShapes()");
1873 }
1874
1875 if (policy == null) {
1876 throw new IllegalArgumentException("Null CaretPolicy passed to TextLayout.getCaretShapes()");
1877 }
1878
1879 Shape[] result = new Shape[2];
1880
1881 TextHitInfo hit = TextHitInfo.afterOffset(offset);
1882
1883 int hitCaret = hitToCaret(hit);
1884
1885 LayoutPathImpl lp = textLine.getLayoutPath();
1886 Shape hitShape = pathToShape(getCaretPath(hit, bounds), false, lp);
1887 TextHitInfo otherHit = hit.getOtherHit();
1888 int otherCaret = hitToCaret(otherHit);
1889
1890 if (hitCaret == otherCaret) {
1891 result[0] = hitShape;
1892 }
1893 else { // more than one caret
1894 Shape otherShape = pathToShape(getCaretPath(otherHit, bounds), false, lp);
1895
1896 TextHitInfo strongHit = policy.getStrongCaret(hit, otherHit, this);
1897 boolean hitIsStrong = strongHit.equals(hit);
1898
1899 if (hitIsStrong) {// then other is weak
1900 result[0] = hitShape;
1901 result[1] = otherShape;
1902 }
1903 else {
1904 result[0] = otherShape;
1905 result[1] = hitShape;
1906 }
1907 }
1908
1909 return result;
1910 }
1911
1912 /**
1913 * Returns two paths corresponding to the strong and weak caret.
1914 * This method is a convenience overload of <code>getCaretShapes</code>
1915 * that uses the default caret policy.
1916 * @param offset an offset in this <code>TextLayout</code>
1917 * @param bounds the bounds to which to extend the carets. This is
1918 * in baseline-relative coordinates.
1919 * @return two paths corresponding to the strong and weak caret as
1920 * defined by the <code>DEFAULT_CARET_POLICY</code>. These are
1921 * in standard coordinates.
1922 */
1923 public Shape[] getCaretShapes(int offset, Rectangle2D bounds) {
1924 // {sfb} parameter checking is done in overloaded version
1925 return getCaretShapes(offset, bounds, DEFAULT_CARET_POLICY);
1926 }
1927
1928 /**
1929 * Returns two paths corresponding to the strong and weak caret.
1930 * This method is a convenience overload of <code>getCaretShapes</code>
1931 * that uses the default caret policy and this <code>TextLayout</code>
1932 * object's natural bounds.
1933 * @param offset an offset in this <code>TextLayout</code>
1934 * @return two paths corresponding to the strong and weak caret as
1935 * defined by the <code>DEFAULT_CARET_POLICY</code>. These are
1936 * in standard coordinates.
1937 */
1938 public Shape[] getCaretShapes(int offset) {
1939 // {sfb} parameter checking is done in overloaded version
1940 return getCaretShapes(offset, getNaturalBounds(), DEFAULT_CARET_POLICY);
1941 }
1942
1943 // A utility to return a path enclosing the given path
1944 // Path0 must be left or top of path1
1945 // {jbr} no assumptions about size of path0, path1 anymore.
1946 private GeneralPath boundingShape(double[] path0, double[] path1) {
1947
1948 // Really, we want the path to be a convex hull around all of the
1949 // points in path0 and path1. But we can get by with less than
1950 // that. We do need to prevent the two segments which
1951 // join path0 to path1 from crossing each other. So, if we
1952 // traverse path0 from top to bottom, we'll traverse path1 from
1953 // bottom to top (and vice versa).
1954
1955 GeneralPath result = pathToShape(path0, false, null);
1956
1957 boolean sameDirection;
1958
1959 if (isVerticalLine) {
1960 sameDirection = (path0[1] > path0[path0.length-1]) ==
1961 (path1[1] > path1[path1.length-1]);
1962 }
1963 else {
1964 sameDirection = (path0[0] > path0[path0.length-2]) ==
1965 (path1[0] > path1[path1.length-2]);
1966 }
1967
1968 int start;
1969 int limit;
1970 int increment;
1971
1972 if (sameDirection) {
1973 start = path1.length-2;
1974 limit = -2;
1975 increment = -2;
1976 }
1977 else {
1978 start = 0;
1979 limit = path1.length;
1980 increment = 2;
1981 }
1982
1983 for (int i = start; i != limit; i += increment) {
1984 result.lineTo((float)path1[i], (float)path1[i+1]);
1985 }
1986
1987 result.closePath();
1988
1989 return result;
1990 }
1991
1992 // A utility to convert a pair of carets into a bounding path
1993 // {jbr} Shape is never outside of bounds.
1994 private GeneralPath caretBoundingShape(int caret0,
1995 int caret1,
1996 Rectangle2D bounds) {
1997
1998 if (caret0 > caret1) {
1999 int temp = caret0;
2000 caret0 = caret1;
2001 caret1 = temp;
2002 }
2003
2004 return boundingShape(getCaretPath(caret0, bounds, true),
2005 getCaretPath(caret1, bounds, true));
2006 }
2007
2008 /*
2009 * A utility to return the path bounding the area to the left (top) of the
2010 * layout.
2011 * Shape is never outside of bounds.
2012 */
2013 private GeneralPath leftShape(Rectangle2D bounds) {
2014
2015 double[] path0;
2016 if (isVerticalLine) {
2017 path0 = new double[] { bounds.getX(), bounds.getY(),
2018 bounds.getX() + bounds.getWidth(),
2019 bounds.getY() };
2020 } else {
2021 path0 = new double[] { bounds.getX(),
2022 bounds.getY() + bounds.getHeight(),
2023 bounds.getX(), bounds.getY() };
2024 }
2025
2026 double[] path1 = getCaretPath(0, bounds, true);
2027
2028 return boundingShape(path0, path1);
2029 }
2030
2031 /*
2032 * A utility to return the path bounding the area to the right (bottom) of
2033 * the layout.
2034 */
2035 private GeneralPath rightShape(Rectangle2D bounds) {
2036 double[] path1;
2037 if (isVerticalLine) {
2038 path1 = new double[] {
2039 bounds.getX(),
2040 bounds.getY() + bounds.getHeight(),
2041 bounds.getX() + bounds.getWidth(),
2042 bounds.getY() + bounds.getHeight()
2043 };
2044 } else {
2045 path1 = new double[] {
2046 bounds.getX() + bounds.getWidth(),
2047 bounds.getY() + bounds.getHeight(),
2048 bounds.getX() + bounds.getWidth(),
2049 bounds.getY()
2050 };
2051 }
2052
2053 double[] path0 = getCaretPath(characterCount, bounds, true);
2054
2055 return boundingShape(path0, path1);
2056 }
2057
2058 /**
2059 * Returns the logical ranges of text corresponding to a visual selection.
2060 * @param firstEndpoint an endpoint of the visual range
2061 * @param secondEndpoint the other endpoint of the visual range.
2062 * This endpoint can be less than <code>firstEndpoint</code>.
2063 * @return an array of integers representing start/limit pairs for the
2064 * selected ranges.
2065 * @see #getVisualHighlightShape(TextHitInfo, TextHitInfo, Rectangle2D)
2066 */
2067 public int[] getLogicalRangesForVisualSelection(TextHitInfo firstEndpoint,
2068 TextHitInfo secondEndpoint) {
2069 ensureCache();
2070
2071 checkTextHit(firstEndpoint);
2072 checkTextHit(secondEndpoint);
2073
2074 // !!! probably want to optimize for all LTR text
2075
2076 boolean[] included = new boolean[characterCount];
2077
2078 int startIndex = hitToCaret(firstEndpoint);
2079 int limitIndex = hitToCaret(secondEndpoint);
2080
2081 if (startIndex > limitIndex) {
2082 int t = startIndex;
2083 startIndex = limitIndex;
2084 limitIndex = t;
2085 }
2086
2087 /*
2088 * now we have the visual indexes of the glyphs at the start and limit
2089 * of the selection range walk through runs marking characters that
2090 * were included in the visual range there is probably a more efficient
2091 * way to do this, but this ought to work, so hey
2092 */
2093
2094 if (startIndex < limitIndex) {
2095 int visIndex = startIndex;
2096 while (visIndex < limitIndex) {
2097 included[textLine.visualToLogical(visIndex)] = true;
2098 ++visIndex;
2099 }
2100 }
2101
2102 /*
2103 * count how many runs we have, ought to be one or two, but perhaps
2104 * things are especially weird
2105 */
2106 int count = 0;
2107 boolean inrun = false;
2108 for (int i = 0; i < characterCount; i++) {
2109 if (included[i] != inrun) {
2110 inrun = !inrun;
2111 if (inrun) {
2112 count++;
2113 }
2114 }
2115 }
2116
2117 int[] ranges = new int[count * 2];
2118 count = 0;
2119 inrun = false;
2120 for (int i = 0; i < characterCount; i++) {
2121 if (included[i] != inrun) {
2122 ranges[count++] = i;
2123 inrun = !inrun;
2124 }
2125 }
2126 if (inrun) {
2127 ranges[count++] = characterCount;
2128 }
2129
2130 return ranges;
2131 }
2132
2133 /**
2134 * Returns a path enclosing the visual selection in the specified range,
2135 * extended to <code>bounds</code>.
2136 * <p>
2137 * If the selection includes the leftmost (topmost) position, the selection
2138 * is extended to the left (top) of <code>bounds</code>. If the
2139 * selection includes the rightmost (bottommost) position, the selection
2140 * is extended to the right (bottom) of the bounds. The height
2141 * (width on vertical lines) of the selection is always extended to
2142 * <code>bounds</code>.
2143 * <p>
2144 * Although the selection is always contiguous, the logically selected
2145 * text can be discontiguous on lines with mixed-direction text. The
2146 * logical ranges of text selected can be retrieved using
2147 * <code>getLogicalRangesForVisualSelection</code>. For example,
2148 * consider the text 'ABCdef' where capital letters indicate
2149 * right-to-left text, rendered on a right-to-left line, with a visual
2150 * selection from 0L (the leading edge of 'A') to 3T (the trailing edge
2151 * of 'd'). The text appears as follows, with bold underlined areas
2152 * representing the selection:
2153 * <br><pre>
2154 * d<u><b>efCBA </b></u>
2155 * </pre>
2156 * The logical selection ranges are 0-3, 4-6 (ABC, ef) because the
2157 * visually contiguous text is logically discontiguous. Also note that
2158 * since the rightmost position on the layout (to the right of 'A') is
2159 * selected, the selection is extended to the right of the bounds.
2160 * @param firstEndpoint one end of the visual selection
2161 * @param secondEndpoint the other end of the visual selection
2162 * @param bounds the bounding rectangle to which to extend the selection.
2163 * This is in baseline-relative coordinates.
2164 * @return a <code>Shape</code> enclosing the selection. This is in
2165 * standard coordinates.
2166 * @see #getLogicalRangesForVisualSelection(TextHitInfo, TextHitInfo)
2167 * @see #getLogicalHighlightShape(int, int, Rectangle2D)
2168 */
2169 public Shape getVisualHighlightShape(TextHitInfo firstEndpoint,
2170 TextHitInfo secondEndpoint,
2171 Rectangle2D bounds)
2172 {
2173 ensureCache();
2174
2175 checkTextHit(firstEndpoint);
2176 checkTextHit(secondEndpoint);
2177
2178 if(bounds == null) {
2179 throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getVisualHighlightShape()");
2180 }
2181
2182 GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
2183
2184 int firstCaret = hitToCaret(firstEndpoint);
2185 int secondCaret = hitToCaret(secondEndpoint);
2186
2187 result.append(caretBoundingShape(firstCaret, secondCaret, bounds),
2188 false);
2189
2190 if (firstCaret == 0 || secondCaret == 0) {
2191 GeneralPath ls = leftShape(bounds);
2192 if (!ls.getBounds().isEmpty())
2193 result.append(ls, false);
2194 }
2195
2196 if (firstCaret == characterCount || secondCaret == characterCount) {
2197 GeneralPath rs = rightShape(bounds);
2198 if (!rs.getBounds().isEmpty()) {
2199 result.append(rs, false);
2200 }
2201 }
2202
2203 LayoutPathImpl lp = textLine.getLayoutPath();
2204 if (lp != null) {
2205 result = (GeneralPath)lp.mapShape(result); // dlf cast safe?
2206 }
2207
2208 return result;
2209 }
2210
2211 /**
2212 * Returns a <code>Shape</code> enclosing the visual selection in the
2213 * specified range, extended to the bounds. This method is a
2214 * convenience overload of <code>getVisualHighlightShape</code> that
2215 * uses the natural bounds of this <code>TextLayout</code>.
2216 * @param firstEndpoint one end of the visual selection
2217 * @param secondEndpoint the other end of the visual selection
2218 * @return a <code>Shape</code> enclosing the selection. This is
2219 * in standard coordinates.
2220 */
2221 public Shape getVisualHighlightShape(TextHitInfo firstEndpoint,
2222 TextHitInfo secondEndpoint) {
2223 return getVisualHighlightShape(firstEndpoint, secondEndpoint, getNaturalBounds());
2224 }
2225
2226 /**
2227 * Returns a <code>Shape</code> enclosing the logical selection in the
2228 * specified range, extended to the specified <code>bounds</code>.
2229 * <p>
2230 * If the selection range includes the first logical character, the
2231 * selection is extended to the portion of <code>bounds</code> before
2232 * the start of this <code>TextLayout</code>. If the range includes
2233 * the last logical character, the selection is extended to the portion
2234 * of <code>bounds</code> after the end of this <code>TextLayout</code>.
2235 * The height (width on vertical lines) of the selection is always
2236 * extended to <code>bounds</code>.
2237 * <p>
2238 * The selection can be discontiguous on lines with mixed-direction text.
2239 * Only those characters in the logical range between start and limit
2240 * appear selected. For example, consider the text 'ABCdef' where capital
2241 * letters indicate right-to-left text, rendered on a right-to-left line,
2242 * with a logical selection from 0 to 4 ('ABCd'). The text appears as
2243 * follows, with bold standing in for the selection, and underlining for
2244 * the extension:
2245 * <br><pre>
2246 * <u><b>d</b></u>ef<u><b>CBA </b></u>
2247 * </pre>
2248 * The selection is discontiguous because the selected characters are
2249 * visually discontiguous. Also note that since the range includes the
2250 * first logical character (A), the selection is extended to the portion
2251 * of the <code>bounds</code> before the start of the layout, which in
2252 * this case (a right-to-left line) is the right portion of the
2253 * <code>bounds</code>.
2254 * @param firstEndpoint an endpoint in the range of characters to select
2255 * @param secondEndpoint the other endpoint of the range of characters
2256 * to select. Can be less than <code>firstEndpoint</code>. The range
2257 * includes the character at min(firstEndpoint, secondEndpoint), but
2258 * excludes max(firstEndpoint, secondEndpoint).
2259 * @param bounds the bounding rectangle to which to extend the selection.
2260 * This is in baseline-relative coordinates.
2261 * @return an area enclosing the selection. This is in standard
2262 * coordinates.
2263 * @see #getVisualHighlightShape(TextHitInfo, TextHitInfo, Rectangle2D)
2264 */
2265 public Shape getLogicalHighlightShape(int firstEndpoint,
2266 int secondEndpoint,
2267 Rectangle2D bounds) {
2268 if (bounds == null) {
2269 throw new IllegalArgumentException("Null Rectangle2D passed to TextLayout.getLogicalHighlightShape()");
2270 }
2271
2272 ensureCache();
2273
2274 if (firstEndpoint > secondEndpoint) {
2275 int t = firstEndpoint;
2276 firstEndpoint = secondEndpoint;
2277 secondEndpoint = t;
2278 }
2279
2280 if(firstEndpoint < 0 || secondEndpoint > characterCount) {
2281 throw new IllegalArgumentException("Range is invalid in TextLayout.getLogicalHighlightShape()");
2282 }
2283
2284 GeneralPath result = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
2285
2286 int[] carets = new int[10]; // would this ever not handle all cases?
2287 int count = 0;
2288
2289 if (firstEndpoint < secondEndpoint) {
2290 int logIndex = firstEndpoint;
2291 do {
2292 carets[count++] = hitToCaret(TextHitInfo.leading(logIndex));
2293 boolean ltr = textLine.isCharLTR(logIndex);
2294
2295 do {
2296 logIndex++;
2297 } while (logIndex < secondEndpoint && textLine.isCharLTR(logIndex) == ltr);
2298
2299 int hitCh = logIndex;
2300 carets[count++] = hitToCaret(TextHitInfo.trailing(hitCh - 1));
2301
2302 if (count == carets.length) {
2303 int[] temp = new int[carets.length + 10];
2304 System.arraycopy(carets, 0, temp, 0, count);
2305 carets = temp;
2306 }
2307 } while (logIndex < secondEndpoint);
2308 }
2309 else {
2310 count = 2;
2311 carets[0] = carets[1] = hitToCaret(TextHitInfo.leading(firstEndpoint));
2312 }
2313
2314 // now create paths for pairs of carets
2315
2316 for (int i = 0; i < count; i += 2) {
2317 result.append(caretBoundingShape(carets[i], carets[i+1], bounds),
2318 false);
2319 }
2320
2321 if (firstEndpoint != secondEndpoint) {
2322 if ((textLine.isDirectionLTR() && firstEndpoint == 0) || (!textLine.isDirectionLTR() &&
2323 secondEndpoint == characterCount)) {
2324 GeneralPath ls = leftShape(bounds);
2325 if (!ls.getBounds().isEmpty()) {
2326 result.append(ls, false);
2327 }
2328 }
2329
2330 if ((textLine.isDirectionLTR() && secondEndpoint == characterCount) ||
2331 (!textLine.isDirectionLTR() && firstEndpoint == 0)) {
2332
2333 GeneralPath rs = rightShape(bounds);
2334 if (!rs.getBounds().isEmpty()) {
2335 result.append(rs, false);
2336 }
2337 }
2338 }
2339
2340 LayoutPathImpl lp = textLine.getLayoutPath();
2341 if (lp != null) {
2342 result = (GeneralPath)lp.mapShape(result); // dlf cast safe?
2343 }
2344 return result;
2345 }
2346
2347 /**
2348 * Returns a <code>Shape</code> enclosing the logical selection in the
2349 * specified range, extended to the natural bounds of this
2350 * <code>TextLayout</code>. This method is a convenience overload of
2351 * <code>getLogicalHighlightShape</code> that uses the natural bounds of
2352 * this <code>TextLayout</code>.
2353 * @param firstEndpoint an endpoint in the range of characters to select
2354 * @param secondEndpoint the other endpoint of the range of characters
2355 * to select. Can be less than <code>firstEndpoint</code>. The range
2356 * includes the character at min(firstEndpoint, secondEndpoint), but
2357 * excludes max(firstEndpoint, secondEndpoint).
2358 * @return a <code>Shape</code> enclosing the selection. This is in
2359 * standard coordinates.
2360 */
2361 public Shape getLogicalHighlightShape(int firstEndpoint, int secondEndpoint) {
2362
2363 return getLogicalHighlightShape(firstEndpoint, secondEndpoint, getNaturalBounds());
2364 }
2365
2366 /**
2367 * Returns the black box bounds of the characters in the specified range.
2368 * The black box bounds is an area consisting of the union of the bounding
2369 * boxes of all the glyphs corresponding to the characters between start
2370 * and limit. This area can be disjoint.
2371 * @param firstEndpoint one end of the character range
2372 * @param secondEndpoint the other end of the character range. Can be
2373 * less than <code>firstEndpoint</code>.
2374 * @return a <code>Shape</code> enclosing the black box bounds. This is
2375 * in standard coordinates.
2376 */
2377 public Shape getBlackBoxBounds(int firstEndpoint, int secondEndpoint) {
2378 ensureCache();
2379
2380 if (firstEndpoint > secondEndpoint) {
2381 int t = firstEndpoint;
2382 firstEndpoint = secondEndpoint;
2383 secondEndpoint = t;
2384 }
2385
2386 if (firstEndpoint < 0 || secondEndpoint > characterCount) {
2387 throw new IllegalArgumentException("Invalid range passed to TextLayout.getBlackBoxBounds()");
2388 }
2389
2390 /*
2391 * return an area that consists of the bounding boxes of all the
2392 * characters from firstEndpoint to limit
2393 */
2394
2395 GeneralPath result = new GeneralPath(GeneralPath.WIND_NON_ZERO);
2396
2397 if (firstEndpoint < characterCount) {
2398 for (int logIndex = firstEndpoint;
2399 logIndex < secondEndpoint;
2400 logIndex++) {
2401
2402 Rectangle2D r = textLine.getCharBounds(logIndex);
2403 if (!r.isEmpty()) {
2404 result.append(r, false);
2405 }
2406 }
2407 }
2408
2409 if (dx != 0 || dy != 0) {
2410 AffineTransform tx = AffineTransform.getTranslateInstance(dx, dy);
2411 result = (GeneralPath)tx.createTransformedShape(result);
2412 }
2413 LayoutPathImpl lp = textLine.getLayoutPath();
2414 if (lp != null) {
2415 result = (GeneralPath)lp.mapShape(result);
2416 }
2417
2418 //return new Highlight(result, false);
2419 return result;
2420 }
2421
2422 /**
2423 * Returns the distance from the point (x,&nbsp;y) to the caret along
2424 * the line direction defined in <code>caretInfo</code>. Distance is
2425 * negative if the point is to the left of the caret on a horizontal
2426 * line, or above the caret on a vertical line.
2427 * Utility for use by hitTestChar.
2428 */
2429 private float caretToPointDistance(float[] caretInfo, float x, float y) {
2430 // distanceOffBaseline is negative if you're 'above' baseline
2431
2432 float lineDistance = isVerticalLine? y : x;
2433 float distanceOffBaseline = isVerticalLine? -x : y;
2434
2435 return lineDistance - caretInfo[0] +
2436 (distanceOffBaseline*caretInfo[1]);
2437 }
2438
2439 /**
2440 * Returns a <code>TextHitInfo</code> corresponding to the
2441 * specified point.
2442 * Coordinates outside the bounds of the <code>TextLayout</code>
2443 * map to hits on the leading edge of the first logical character,
2444 * or the trailing edge of the last logical character, as appropriate,
2445 * regardless of the position of that character in the line. Only the
2446 * direction along the baseline is used to make this evaluation.
2447 * @param x the x offset from the origin of this
2448 * <code>TextLayout</code>. This is in standard coordinates.
2449 * @param y the y offset from the origin of this
2450 * <code>TextLayout</code>. This is in standard coordinates.
2451 * @param bounds the bounds of the <code>TextLayout</code>. This
2452 * is in baseline-relative coordinates.
2453 * @return a hit describing the character and edge (leading or trailing)
2454 * under the specified point.
2455 */
2456 public TextHitInfo hitTestChar(float x, float y, Rectangle2D bounds) {
2457 // check boundary conditions
2458
2459 LayoutPathImpl lp = textLine.getLayoutPath();
2460 boolean prev = false;
2461 if (lp != null) {
2462 Point2D.Float pt = new Point2D.Float(x, y);
2463 prev = lp.pointToPath(pt, pt);
2464 x = pt.x;
2465 y = pt.y;
2466 }
2467
2468 if (isVertical()) {
2469 if (y < bounds.getMinY()) {
2470 return TextHitInfo.leading(0);
2471 } else if (y >= bounds.getMaxY()) {
2472 return TextHitInfo.trailing(characterCount-1);
2473 }
2474 } else {
2475 if (x < bounds.getMinX()) {
2476 return isLeftToRight() ? TextHitInfo.leading(0) : TextHitInfo.trailing(characterCount-1);
2477 } else if (x >= bounds.getMaxX()) {
2478 return isLeftToRight() ? TextHitInfo.trailing(characterCount-1) : TextHitInfo.leading(0);
2479 }
2480 }
2481
2482 // revised hit test
2483 // the original seems too complex and fails miserably with italic offsets
2484 // the natural tendency is to move towards the character you want to hit
2485 // so we'll just measure distance to the center of each character's visual
2486 // bounds, pick the closest one, then see which side of the character's
2487 // center line (italic) the point is on.
2488 // this tends to make it easier to hit narrow characters, which can be a
2489 // bit odd if you're visually over an adjacent wide character. this makes
2490 // a difference with bidi, so perhaps i need to revisit this yet again.
2491
2492 double distance = Double.MAX_VALUE;
2493 int index = 0;
2494 int trail = -1;
2495 CoreMetrics lcm = null;
2496 float icx = 0, icy = 0, ia = 0, cy = 0, dya = 0, ydsq = 0;
2497
2498 for (int i = 0; i < characterCount; ++i) {
2499 if (!textLine.caretAtOffsetIsValid(i)) {
2500 continue;
2501 }
2502 if (trail == -1) {
2503 trail = i;
2504 }
2505 CoreMetrics cm = textLine.getCoreMetricsAt(i);
2506 if (cm != lcm) {
2507 lcm = cm;
2508 // just work around baseline mess for now
2509 if (cm.baselineIndex == GraphicAttribute.TOP_ALIGNMENT) {
2510 cy = -(textLine.getMetrics().ascent - cm.ascent) + cm.ssOffset;
2511 } else if (cm.baselineIndex == GraphicAttribute.BOTTOM_ALIGNMENT) {
2512 cy = textLine.getMetrics().descent - cm.descent + cm.ssOffset;
2513 } else {
2514 cy = cm.effectiveBaselineOffset(baselineOffsets) + cm.ssOffset;
2515 }
2516 float dy = (cm.descent - cm.ascent) / 2 - cy;
2517 dya = dy * cm.italicAngle;
2518 cy += dy;
2519 ydsq = (cy - y)*(cy - y);
2520 }
2521 float cx = textLine.getCharXPosition(i);
2522 float ca = textLine.getCharAdvance(i);
2523 float dx = ca / 2;
2524 cx += dx - dya;
2525
2526 // proximity in x (along baseline) is two times as important as proximity in y
2527 double nd = Math.sqrt(4*(cx - x)*(cx - x) + ydsq);
2528 if (nd < distance) {
2529 distance = nd;
2530 index = i;
2531 trail = -1;
2532 icx = cx; icy = cy; ia = cm.italicAngle;
2533 }
2534 }
2535 boolean left = x < icx - (y - icy) * ia;
2536 boolean leading = textLine.isCharLTR(index) == left;
2537 if (trail == -1) {
2538 trail = characterCount;
2539 }
2540 TextHitInfo result = leading ? TextHitInfo.leading(index) :
2541 TextHitInfo.trailing(trail-1);
2542 return result;
2543 }
2544
2545 /**
2546 * Returns a <code>TextHitInfo</code> corresponding to the
2547 * specified point. This method is a convenience overload of
2548 * <code>hitTestChar</code> that uses the natural bounds of this
2549 * <code>TextLayout</code>.
2550 * @param x the x offset from the origin of this
2551 * <code>TextLayout</code>. This is in standard coordinates.
2552 * @param y the y offset from the origin of this
2553 * <code>TextLayout</code>. This is in standard coordinates.
2554 * @return a hit describing the character and edge (leading or trailing)
2555 * under the specified point.
2556 */
2557 public TextHitInfo hitTestChar(float x, float y) {
2558
2559 return hitTestChar(x, y, getNaturalBounds());
2560 }
2561
2562 /**
2563 * Returns the hash code of this <code>TextLayout</code>.
2564 * @return the hash code of this <code>TextLayout</code>.
2565 */
2566 public int hashCode() {
2567 if (hashCodeCache == 0) {
2568 ensureCache();
2569 hashCodeCache = textLine.hashCode();
2570 }
2571 return hashCodeCache;
2572 }
2573
2574 /**
2575 * Returns <code>true</code> if the specified <code>Object</code> is a
2576 * <code>TextLayout</code> object and if the specified <code>Object</code>
2577 * equals this <code>TextLayout</code>.
2578 * @param obj an <code>Object</code> to test for equality
2579 * @return <code>true</code> if the specified <code>Object</code>
2580 * equals this <code>TextLayout</code>; <code>false</code>
2581 * otherwise.
2582 */
2583 public boolean equals(Object obj) {
2584 return (obj instanceof TextLayout) && equals((TextLayout)obj);
2585 }
2586
2587 /**
2588 * Returns <code>true</code> if the two layouts are equal.
2589 * Two layouts are equal if they contain equal glyphvectors in the same order.
2590 * @param rhs the <code>TextLayout</code> to compare to this
2591 * <code>TextLayout</code>
2592 * @return <code>true</code> if the specified <code>TextLayout</code>
2593 * equals this <code>TextLayout</code>.
2594 *
2595 */
2596 public boolean equals(TextLayout rhs) {
2597
2598 if (rhs == null) {
2599 return false;
2600 }
2601 if (rhs == this) {
2602 return true;
2603 }
2604
2605 ensureCache();
2606 return textLine.equals(rhs.textLine);
2607 }
2608
2609 /**
2610 * Returns debugging information for this <code>TextLayout</code>.
2611 * @return the <code>textLine</code> of this <code>TextLayout</code>
2612 * as a <code>String</code>.
2613 */
2614 public String toString() {
2615 ensureCache();
2616 return textLine.toString();
2617 }
2618
2619 /**
2620 * Renders this <code>TextLayout</code> at the specified location in
2621 * the specified {@link java.awt.Graphics2D Graphics2D} context.
2622 * The origin of the layout is placed at x,&nbsp;y. Rendering may touch
2623 * any point within <code>getBounds()</code> of this position. This
2624 * leaves the <code>g2</code> unchanged. Text is rendered along the
2625 * baseline path.
2626 * @param g2 the <code>Graphics2D</code> context into which to render
2627 * the layout
2628 * @param x the X coordinate of the origin of this <code>TextLayout</code>
2629 * @param y the Y coordinate of the origin of this <code>TextLayout</code>
2630 * @see #getBounds()
2631 */
2632 public void draw(Graphics2D g2, float x, float y) {
2633
2634 if (g2 == null) {
2635 throw new IllegalArgumentException("Null Graphics2D passed to TextLayout.draw()");
2636 }
2637
2638 textLine.draw(g2, x - dx, y - dy);
2639 }
2640
2641 /**
2642 * Package-only method for testing ONLY. Please don't abuse.
2643 */
2644 TextLine getTextLineForTesting() {
2645
2646 return textLine;
2647 }
2648
2649 /**
2650 *
2651 * Return the index of the first character with a different baseline from the
2652 * character at start, or limit if all characters between start and limit have
2653 * the same baseline.
2654 */
2655 private static int sameBaselineUpTo(Font font, char[] text,
2656 int start, int limit) {
2657 // current implementation doesn't support multiple baselines
2658 return limit;
2659 /*
2660 byte bl = font.getBaselineFor(text[start++]);
2661 while (start < limit && font.getBaselineFor(text[start]) == bl) {
2662 ++start;
2663 }
2664 return start;
2665 */
2666 }
2667
2668 static byte getBaselineFromGraphic(GraphicAttribute graphic) {
2669
2670 byte alignment = (byte) graphic.getAlignment();
2671
2672 if (alignment == GraphicAttribute.BOTTOM_ALIGNMENT ||
2673 alignment == GraphicAttribute.TOP_ALIGNMENT) {
2674
2675 return (byte)GraphicAttribute.ROMAN_BASELINE;
2676 }
2677 else {
2678 return alignment;
2679 }
2680 }
2681
2682 /**
2683 * Returns a <code>Shape</code> representing the outline of this
2684 * <code>TextLayout</code>.
2685 * @param tx an optional {@link AffineTransform} to apply to the
2686 * outline of this <code>TextLayout</code>.
2687 * @return a <code>Shape</code> that is the outline of this
2688 * <code>TextLayout</code>. This is in standard coordinates.
2689 */
2690 public Shape getOutline(AffineTransform tx) {
2691 ensureCache();
2692 Shape result = textLine.getOutline(tx);
2693 LayoutPathImpl lp = textLine.getLayoutPath();
2694 if (lp != null) {
2695 result = lp.mapShape(result);
2696 }
2697 return result;
2698 }
2699
2700 /**
2701 * Return the LayoutPath, or null if the layout path is the
2702 * default path (x maps to advance, y maps to offset).
2703 * @return the layout path
2704 * @since 1.6
2705 */
2706 public LayoutPath getLayoutPath() {
2707 return textLine.getLayoutPath();
2708 }
2709
2710 /**
2711 * Convert a hit to a point in standard coordinates. The point is
2712 * on the baseline of the character at the leading or trailing
2713 * edge of the character, as appropriate. If the path is
2714 * broken at the side of the character represented by the hit, the
2715 * point will be adjacent to the character.
2716 * @param hit the hit to check. This must be a valid hit on
2717 * the TextLayout.
2718 * @param point the returned point. The point is in standard
2719 * coordinates.
2720 * @throws IllegalArgumentException if the hit is not valid for the
2721 * TextLayout.
2722 * @throws NullPointerException if hit or point is null.
2723 * @since 1.6
2724 */
2725 public void hitToPoint(TextHitInfo hit, Point2D point) {
2726 if (hit == null || point == null) {
2727 throw new NullPointerException((hit == null ? "hit" : "point") +
2728 " can't be null");
2729 }
2730 ensureCache();
2731 checkTextHit(hit);
2732
2733 float adv = 0;
2734 float off = 0;
2735
2736 int ix = hit.getCharIndex();
2737 boolean leading = hit.isLeadingEdge();
2738 boolean ltr;
2739 if (ix == -1 || ix == textLine.characterCount()) {
2740 ltr = textLine.isDirectionLTR();
2741 adv = (ltr == (ix == -1)) ? 0 : lineMetrics.advance;
2742 } else {
2743 ltr = textLine.isCharLTR(ix);
2744 adv = textLine.getCharLinePosition(ix, leading);
2745 off = textLine.getCharYPosition(ix);
2746 }
2747 point.setLocation(adv, off);
2748 LayoutPath lp = textLine.getLayoutPath();
2749 if (lp != null) {
2750 lp.pathToPoint(point, ltr != leading, point);
2751 }
2752 }
2753}
java/pkg/java/awt/font/TextLine$1.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLine$2.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLine$3.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLine$4.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLine$Function.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLine$TextLineMetrics.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLine.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextLine.java+1437−0
@@ -0,0 +1,1437 @@
1/*
2 * Copyright (c) 1998, 2006, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright IBM Corp. 1998-2003, All Rights Reserved
28 *
29 */
30
31package java.awt.font;
32
33import java.awt.Color;
34import java.awt.Font;
35import java.awt.Graphics2D;
36import java.awt.Rectangle;
37import java.awt.Shape;
38import java.awt.geom.AffineTransform;
39import java.awt.geom.GeneralPath;
40import java.awt.geom.Point2D;
41import java.awt.geom.Rectangle2D;
42import java.awt.im.InputMethodHighlight;
43import java.awt.image.BufferedImage;
44import java.text.Annotation;
45import java.text.AttributedCharacterIterator;
46import java.text.Bidi;
47import java.text.CharacterIterator;
48import java.util.Hashtable;
49import java.util.Map;
50import sun.font.AttributeValues;
51import sun.font.BidiUtils;
52import sun.font.CoreMetrics;
53import sun.font.Decoration;
54import sun.font.FontLineMetrics;
55import sun.font.FontResolver;
56import sun.font.GraphicComponent;
57import sun.font.LayoutPathImpl;
58import sun.font.LayoutPathImpl.EmptyPath;
59import sun.font.LayoutPathImpl.SegmentPathBuilder;
60import sun.font.TextLabelFactory;
61import sun.font.TextLineComponent;
62import sun.text.CodePointIterator;
63
64import java.awt.geom.Line2D;
65
66final class TextLine {
67
68 static final class TextLineMetrics {
69 public final float ascent;
70 public final float descent;
71 public final float leading;
72 public final float advance;
73
74 public TextLineMetrics(float ascent,
75 float descent,
76 float leading,
77 float advance) {
78 this.ascent = ascent;
79 this.descent = descent;
80 this.leading = leading;
81 this.advance = advance;
82 }
83 }
84
85 private TextLineComponent[] fComponents;
86 private float[] fBaselineOffsets;
87 private int[] fComponentVisualOrder; // if null, ltr
88 private float[] locs; // x,y pairs for components in visual order
89 private char[] fChars;
90 private int fCharsStart;
91 private int fCharsLimit;
92 private int[] fCharVisualOrder; // if null, ltr
93 private int[] fCharLogicalOrder; // if null, ltr
94 private byte[] fCharLevels; // if null, 0
95 private boolean fIsDirectionLTR;
96 private LayoutPathImpl lp;
97 private boolean isSimple;
98 private Rectangle pixelBounds;
99 private FontRenderContext frc;
100
101 private TextLineMetrics fMetrics = null; // built on demand in getMetrics
102
103 public TextLine(FontRenderContext frc,
104 TextLineComponent[] components,
105 float[] baselineOffsets,
106 char[] chars,
107 int charsStart,
108 int charsLimit,
109 int[] charLogicalOrder,
110 byte[] charLevels,
111 boolean isDirectionLTR) {
112
113 int[] componentVisualOrder = computeComponentOrder(components,
114 charLogicalOrder);
115
116 this.frc = frc;
117 fComponents = components;
118 fBaselineOffsets = baselineOffsets;
119 fComponentVisualOrder = componentVisualOrder;
120 fChars = chars;
121 fCharsStart = charsStart;
122 fCharsLimit = charsLimit;
123 fCharLogicalOrder = charLogicalOrder;
124 fCharLevels = charLevels;
125 fIsDirectionLTR = isDirectionLTR;
126 checkCtorArgs();
127
128 init();
129 }
130
131 private void checkCtorArgs() {
132
133 int checkCharCount = 0;
134 for (int i=0; i < fComponents.length; i++) {
135 checkCharCount += fComponents[i].getNumCharacters();
136 }
137
138 if (checkCharCount != this.characterCount()) {
139 throw new IllegalArgumentException("Invalid TextLine! " +
140 "char count is different from " +
141 "sum of char counts of components.");
142 }
143 }
144
145 private void init() {
146
147 // first, we need to check for graphic components on the TOP or BOTTOM baselines. So
148 // we perform the work that used to be in getMetrics here.
149
150 float ascent = 0;
151 float descent = 0;
152 float leading = 0;
153 float advance = 0;
154
155 // ascent + descent must not be less than this value
156 float maxGraphicHeight = 0;
157 float maxGraphicHeightWithLeading = 0;
158
159 // walk through EGA's
160 TextLineComponent tlc;
161 boolean fitTopAndBottomGraphics = false;
162
163 isSimple = true;
164
165 for (int i = 0; i < fComponents.length; i++) {
166 tlc = fComponents[i];
167
168 isSimple &= tlc.isSimple();
169
170 CoreMetrics cm = tlc.getCoreMetrics();
171
172 byte baseline = (byte)cm.baselineIndex;
173
174 if (baseline >= 0) {
175 float baselineOffset = fBaselineOffsets[baseline];
176
177 ascent = Math.max(ascent, -baselineOffset + cm.ascent);
178
179 float gd = baselineOffset + cm.descent;
180 descent = Math.max(descent, gd);
181
182 leading = Math.max(leading, gd + cm.leading);
183 }
184 else {
185 fitTopAndBottomGraphics = true;
186 float graphicHeight = cm.ascent + cm.descent;
187 float graphicHeightWithLeading = graphicHeight + cm.leading;
188 maxGraphicHeight = Math.max(maxGraphicHeight, graphicHeight);
189 maxGraphicHeightWithLeading = Math.max(maxGraphicHeightWithLeading,
190 graphicHeightWithLeading);
191 }
192 }
193
194 if (fitTopAndBottomGraphics) {
195 if (maxGraphicHeight > ascent + descent) {
196 descent = maxGraphicHeight - ascent;
197 }
198 if (maxGraphicHeightWithLeading > ascent + leading) {
199 leading = maxGraphicHeightWithLeading - ascent;
200 }
201 }
202
203 leading -= descent;
204
205 // we now know enough to compute the locs, but we need the final loc
206 // for the advance before we can create the metrics object
207
208 if (fitTopAndBottomGraphics) {
209 // we have top or bottom baselines, so expand the baselines array
210 // full offsets are needed by CoreMetrics.effectiveBaselineOffset
211 fBaselineOffsets = new float[] {
212 fBaselineOffsets[0],
213 fBaselineOffsets[1],
214 fBaselineOffsets[2],
215 descent,
216 -ascent
217 };
218 }
219
220 float x = 0;
221 float y = 0;
222 CoreMetrics pcm = null;
223
224 boolean needPath = false;
225 locs = new float[fComponents.length * 2 + 2];
226
227 for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) {
228 tlc = fComponents[getComponentLogicalIndex(i)];
229 CoreMetrics cm = tlc.getCoreMetrics();
230
231 if ((pcm != null) &&
232 (pcm.italicAngle != 0 || cm.italicAngle != 0) && // adjust because of italics
233 (pcm.italicAngle != cm.italicAngle ||
234 pcm.baselineIndex != cm.baselineIndex ||
235 pcm.ssOffset != cm.ssOffset)) {
236
237 // 1) compute the area of overlap - min effective ascent and min effective descent
238 // 2) compute the x positions along italic angle of ascent and descent for left and right
239 // 3) compute maximum left - right, adjust right position by this value
240 // this is a crude form of kerning between textcomponents
241
242 // note glyphvectors preposition glyphs based on offset,
243 // so tl doesn't need to adjust glyphvector position
244 // 1)
245 float pb = pcm.effectiveBaselineOffset(fBaselineOffsets);
246 float pa = pb - pcm.ascent;
247 float pd = pb + pcm.descent;
248 // pb += pcm.ssOffset;
249
250 float cb = cm.effectiveBaselineOffset(fBaselineOffsets);
251 float ca = cb - cm.ascent;
252 float cd = cb + cm.descent;
253 // cb += cm.ssOffset;
254
255 float a = Math.max(pa, ca);
256 float d = Math.min(pd, cd);
257
258 // 2)
259 float pax = pcm.italicAngle * (pb - a);
260 float pdx = pcm.italicAngle * (pb - d);
261
262 float cax = cm.italicAngle * (cb - a);
263 float cdx = cm.italicAngle * (cb - d);
264
265 // 3)
266 float dax = pax - cax;
267 float ddx = pdx - cdx;
268 float dx = Math.max(dax, ddx);
269
270 x += dx;
271 y = cb;
272 } else {
273 // no italic adjustment for x, but still need to compute y
274 y = cm.effectiveBaselineOffset(fBaselineOffsets); // + cm.ssOffset;
275 }
276
277 locs[n] = x;
278 locs[n+1] = y;
279
280 x += tlc.getAdvance();
281 pcm = cm;
282
283 needPath |= tlc.getBaselineTransform() != null;
284 }
285
286 // do we want italic padding at the right of the line?
287 if (pcm.italicAngle != 0) {
288 float pb = pcm.effectiveBaselineOffset(fBaselineOffsets);
289 float pa = pb - pcm.ascent;
290 float pd = pb + pcm.descent;
291 pb += pcm.ssOffset;
292
293 float d;
294 if (pcm.italicAngle > 0) {
295 d = pb + pcm.ascent;
296 } else {
297 d = pb - pcm.descent;
298 }
299 d *= pcm.italicAngle;
300
301 x += d;
302 }
303 locs[locs.length - 2] = x;
304 // locs[locs.length - 1] = 0; // final offset is always back on baseline
305
306 // ok, build fMetrics since we have the final advance
307 advance = x;
308 fMetrics = new TextLineMetrics(ascent, descent, leading, advance);
309
310 // build path if we need it
311 if (needPath) {
312 isSimple = false;
313
314 Point2D.Double pt = new Point2D.Double();
315 double tx = 0, ty = 0;
316 SegmentPathBuilder builder = new SegmentPathBuilder();
317 builder.moveTo(locs[0], 0);
318 for (int i = 0, n = 0; i < fComponents.length; ++i, n += 2) {
319 tlc = fComponents[getComponentLogicalIndex(i)];
320 AffineTransform at = tlc.getBaselineTransform();
321 if (at != null && ((at.getType() & at.TYPE_TRANSLATION) != 0)) {
322 double dx = at.getTranslateX();
323 double dy = at.getTranslateY();
324 builder.moveTo(tx += dx, ty += dy);
325 }
326 pt.x = locs[n+2] - locs[n];
327 pt.y = 0;
328 if (at != null) {
329 at.deltaTransform(pt, pt);
330 }
331 builder.lineTo(tx += pt.x, ty += pt.y);
332 }
333 lp = builder.complete();
334
335 if (lp == null) { // empty path
336 tlc = fComponents[getComponentLogicalIndex(0)];
337 AffineTransform at = tlc.getBaselineTransform();
338 if (at != null) {
339 lp = new EmptyPath(at);
340 }
341 }
342 }
343 }
344
345 public Rectangle getPixelBounds(FontRenderContext frc, float x, float y) {
346 Rectangle result = null;
347
348 // if we have a matching frc, set it to null so we don't have to test it
349 // for each component
350 if (frc != null && frc.equals(this.frc)) {
351 frc = null;
352 }
353
354 // only cache integral locations with the default frc, this is a bit strict
355 int ix = (int)Math.floor(x);
356 int iy = (int)Math.floor(y);
357 float rx = x - ix;
358 float ry = y - iy;
359 boolean canCache = frc == null && rx == 0 && ry == 0;
360
361 if (canCache && pixelBounds != null) {
362 result = new Rectangle(pixelBounds);
363 result.x += ix;
364 result.y += iy;
365 return result;
366 }
367
368 // couldn't use cache, or didn't have it, so compute
369
370 if (isSimple) { // all glyphvectors with no decorations, no layout path
371 for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
372 TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
373 Rectangle pb = tlc.getPixelBounds(frc, locs[n] + rx, locs[n+1] + ry);
374 if (!pb.isEmpty()) {
375 if (result == null) {
376 result = pb;
377 } else {
378 result.add(pb);
379 }
380 }
381 }
382 if (result == null) {
383 result = new Rectangle(0, 0, 0, 0);
384 }
385 } else { // draw and test
386 final int MARGIN = 3;
387 Rectangle2D r2d = getVisualBounds();
388 if (lp != null) {
389 r2d = lp.mapShape(r2d).getBounds();
390 }
391 Rectangle bounds = r2d.getBounds();
392 BufferedImage im = new BufferedImage(bounds.width + MARGIN * 2,
393 bounds.height + MARGIN * 2,
394 BufferedImage.TYPE_INT_ARGB);
395
396 Graphics2D g2d = im.createGraphics();
397 g2d.setColor(Color.WHITE);
398 g2d.fillRect(0, 0, im.getWidth(), im.getHeight());
399
400 g2d.setColor(Color.BLACK);
401 draw(g2d, rx + MARGIN - bounds.x, ry + MARGIN - bounds.y);
402
403 result = computePixelBounds(im);
404 result.x -= MARGIN - bounds.x;
405 result.y -= MARGIN - bounds.y;
406 }
407
408 if (canCache) {
409 pixelBounds = new Rectangle(result);
410 }
411
412 result.x += ix;
413 result.y += iy;
414 return result;
415 }
416
417 static Rectangle computePixelBounds(BufferedImage im) {
418 int w = im.getWidth();
419 int h = im.getHeight();
420
421 int l = -1, t = -1, r = w, b = h;
422
423 {
424 // get top
425 int[] buf = new int[w];
426 loop: while (++t < h) {
427 im.getRGB(0, t, buf.length, 1, buf, 0, w); // w ignored
428 for (int i = 0; i < buf.length; i++) {
429 if (buf[i] != -1) {
430 break loop;
431 }
432 }
433 }
434 }
435
436 // get bottom
437 {
438 int[] buf = new int[w];
439 loop: while (--b > t) {
440 im.getRGB(0, b, buf.length, 1, buf, 0, w); // w ignored
441 for (int i = 0; i < buf.length; ++i) {
442 if (buf[i] != -1) {
443 break loop;
444 }
445 }
446 }
447 ++b;
448 }
449
450 // get left
451 {
452 loop: while (++l < r) {
453 for (int i = t; i < b; ++i) {
454 int v = im.getRGB(l, i);
455 if (v != -1) {
456 break loop;
457 }
458 }
459 }
460 }
461
462 // get right
463 {
464 loop: while (--r > l) {
465 for (int i = t; i < b; ++i) {
466 int v = im.getRGB(r, i);
467 if (v != -1) {
468 break loop;
469 }
470 }
471 }
472 ++r;
473 }
474
475 return new Rectangle(l, t, r-l, b-t);
476 }
477
478 private abstract static class Function {
479
480 abstract float computeFunction(TextLine line,
481 int componentIndex,
482 int indexInArray);
483 }
484
485 private static Function fgPosAdvF = new Function() {
486 float computeFunction(TextLine line,
487 int componentIndex,
488 int indexInArray) {
489
490 TextLineComponent tlc = line.fComponents[componentIndex];
491 int vi = line.getComponentVisualIndex(componentIndex);
492 return line.locs[vi * 2] + tlc.getCharX(indexInArray) + tlc.getCharAdvance(indexInArray);
493 }
494 };
495
496 private static Function fgAdvanceF = new Function() {
497
498 float computeFunction(TextLine line,
499 int componentIndex,
500 int indexInArray) {
501
502 TextLineComponent tlc = line.fComponents[componentIndex];
503 return tlc.getCharAdvance(indexInArray);
504 }
505 };
506
507 private static Function fgXPositionF = new Function() {
508
509 float computeFunction(TextLine line,
510 int componentIndex,
511 int indexInArray) {
512
513 int vi = line.getComponentVisualIndex(componentIndex);
514 TextLineComponent tlc = line.fComponents[componentIndex];
515 return line.locs[vi * 2] + tlc.getCharX(indexInArray);
516 }
517 };
518
519 private static Function fgYPositionF = new Function() {
520
521 float computeFunction(TextLine line,
522 int componentIndex,
523 int indexInArray) {
524
525 TextLineComponent tlc = line.fComponents[componentIndex];
526 float charPos = tlc.getCharY(indexInArray);
527
528 // charPos is relative to the component - adjust for
529 // baseline
530
531 return charPos + line.getComponentShift(componentIndex);
532 }
533 };
534
535 public int characterCount() {
536
537 return fCharsLimit - fCharsStart;
538 }
539
540 public boolean isDirectionLTR() {
541
542 return fIsDirectionLTR;
543 }
544
545 public TextLineMetrics getMetrics() {
546 return fMetrics;
547 }
548
549 public int visualToLogical(int visualIndex) {
550
551 if (fCharLogicalOrder == null) {
552 return visualIndex;
553 }
554
555 if (fCharVisualOrder == null) {
556 fCharVisualOrder = BidiUtils.createInverseMap(fCharLogicalOrder);
557 }
558
559 return fCharVisualOrder[visualIndex];
560 }
561
562 public int logicalToVisual(int logicalIndex) {
563
564 return (fCharLogicalOrder == null)?
565 logicalIndex : fCharLogicalOrder[logicalIndex];
566 }
567
568 public byte getCharLevel(int logicalIndex) {
569
570 return fCharLevels==null? 0 : fCharLevels[logicalIndex];
571 }
572
573 public boolean isCharLTR(int logicalIndex) {
574
575 return (getCharLevel(logicalIndex) & 0x1) == 0;
576 }
577
578 public int getCharType(int logicalIndex) {
579
580 return Character.getType(fChars[logicalIndex + fCharsStart]);
581 }
582
583 public boolean isCharSpace(int logicalIndex) {
584
585 return Character.isSpaceChar(fChars[logicalIndex + fCharsStart]);
586 }
587
588 public boolean isCharWhitespace(int logicalIndex) {
589
590 return Character.isWhitespace(fChars[logicalIndex + fCharsStart]);
591 }
592
593 public float getCharAngle(int logicalIndex) {
594
595 return getCoreMetricsAt(logicalIndex).italicAngle;
596 }
597
598 public CoreMetrics getCoreMetricsAt(int logicalIndex) {
599
600 if (logicalIndex < 0) {
601 throw new IllegalArgumentException("Negative logicalIndex.");
602 }
603
604 if (logicalIndex > fCharsLimit - fCharsStart) {
605 throw new IllegalArgumentException("logicalIndex too large.");
606 }
607
608 int currentTlc = 0;
609 int tlcStart = 0;
610 int tlcLimit = 0;
611
612 do {
613 tlcLimit += fComponents[currentTlc].getNumCharacters();
614 if (tlcLimit > logicalIndex) {
615 break;
616 }
617 ++currentTlc;
618 tlcStart = tlcLimit;
619 } while(currentTlc < fComponents.length);
620
621 return fComponents[currentTlc].getCoreMetrics();
622 }
623
624 public float getCharAscent(int logicalIndex) {
625
626 return getCoreMetricsAt(logicalIndex).ascent;
627 }
628
629 public float getCharDescent(int logicalIndex) {
630
631 return getCoreMetricsAt(logicalIndex).descent;
632 }
633
634 public float getCharShift(int logicalIndex) {
635
636 return getCoreMetricsAt(logicalIndex).ssOffset;
637 }
638
639 private float applyFunctionAtIndex(int logicalIndex, Function f) {
640
641 if (logicalIndex < 0) {
642 throw new IllegalArgumentException("Negative logicalIndex.");
643 }
644
645 int tlcStart = 0;
646
647 for(int i=0; i < fComponents.length; i++) {
648
649 int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
650 if (tlcLimit > logicalIndex) {
651 return f.computeFunction(this, i, logicalIndex - tlcStart);
652 }
653 else {
654 tlcStart = tlcLimit;
655 }
656 }
657
658 throw new IllegalArgumentException("logicalIndex too large.");
659 }
660
661 public float getCharAdvance(int logicalIndex) {
662
663 return applyFunctionAtIndex(logicalIndex, fgAdvanceF);
664 }
665
666 public float getCharXPosition(int logicalIndex) {
667
668 return applyFunctionAtIndex(logicalIndex, fgXPositionF);
669 }
670
671 public float getCharYPosition(int logicalIndex) {
672
673 return applyFunctionAtIndex(logicalIndex, fgYPositionF);
674 }
675
676 public float getCharLinePosition(int logicalIndex) {
677
678 return getCharXPosition(logicalIndex);
679 }
680
681 public float getCharLinePosition(int logicalIndex, boolean leading) {
682 Function f = isCharLTR(logicalIndex) == leading ? fgXPositionF : fgPosAdvF;
683 return applyFunctionAtIndex(logicalIndex, f);
684 }
685
686 public boolean caretAtOffsetIsValid(int offset) {
687
688 if (offset < 0) {
689 throw new IllegalArgumentException("Negative offset.");
690 }
691
692 int tlcStart = 0;
693
694 for(int i=0; i < fComponents.length; i++) {
695
696 int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
697 if (tlcLimit > offset) {
698 return fComponents[i].caretAtOffsetIsValid(offset-tlcStart);
699 }
700 else {
701 tlcStart = tlcLimit;
702 }
703 }
704
705 throw new IllegalArgumentException("logicalIndex too large.");
706 }
707
708 /**
709 * map a component visual index to the logical index.
710 */
711 private int getComponentLogicalIndex(int vi) {
712 if (fComponentVisualOrder == null) {
713 return vi;
714 }
715 return fComponentVisualOrder[vi];
716 }
717
718 /**
719 * map a component logical index to the visual index.
720 */
721 private int getComponentVisualIndex(int li) {
722 if (fComponentVisualOrder == null) {
723 return li;
724 }
725 for (int i = 0; i < fComponentVisualOrder.length; ++i) {
726 if (fComponentVisualOrder[i] == li) {
727 return i;
728 }
729 }
730 throw new IndexOutOfBoundsException("bad component index: " + li);
731 }
732
733 public Rectangle2D getCharBounds(int logicalIndex) {
734
735 if (logicalIndex < 0) {
736 throw new IllegalArgumentException("Negative logicalIndex.");
737 }
738
739 int tlcStart = 0;
740
741 for (int i=0; i < fComponents.length; i++) {
742
743 int tlcLimit = tlcStart + fComponents[i].getNumCharacters();
744 if (tlcLimit > logicalIndex) {
745
746 TextLineComponent tlc = fComponents[i];
747 int indexInTlc = logicalIndex - tlcStart;
748 Rectangle2D chBounds = tlc.getCharVisualBounds(indexInTlc);
749
750 int vi = getComponentVisualIndex(i);
751 chBounds.setRect(chBounds.getX() + locs[vi * 2],
752 chBounds.getY() + locs[vi * 2 + 1],
753 chBounds.getWidth(),
754 chBounds.getHeight());
755 return chBounds;
756 }
757 else {
758 tlcStart = tlcLimit;
759 }
760 }
761
762 throw new IllegalArgumentException("logicalIndex too large.");
763 }
764
765 private float getComponentShift(int index) {
766 CoreMetrics cm = fComponents[index].getCoreMetrics();
767 return cm.effectiveBaselineOffset(fBaselineOffsets);
768 }
769
770 public void draw(Graphics2D g2, float x, float y) {
771 if (lp == null) {
772 for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
773 TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
774 tlc.draw(g2, locs[n] + x, locs[n+1] + y);
775 }
776 } else {
777 AffineTransform oldTx = g2.getTransform();
778 Point2D.Float pt = new Point2D.Float();
779 for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
780 TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
781 lp.pathToPoint(locs[n], locs[n+1], false, pt);
782 pt.x += x;
783 pt.y += y;
784 AffineTransform at = tlc.getBaselineTransform();
785
786 if (at != null) {
787 g2.translate(pt.x - at.getTranslateX(), pt.y - at.getTranslateY());
788 g2.transform(at);
789 tlc.draw(g2, 0, 0);
790 g2.setTransform(oldTx);
791 } else {
792 tlc.draw(g2, pt.x, pt.y);
793 }
794 }
795 }
796 }
797
798 /**
799 * Return the union of the visual bounds of all the components.
800 * This incorporates the path. It does not include logical
801 * bounds (used by carets).
802 */
803 public Rectangle2D getVisualBounds() {
804 Rectangle2D result = null;
805
806 for (int i = 0, n = 0; i < fComponents.length; i++, n += 2) {
807 TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
808 Rectangle2D r = tlc.getVisualBounds();
809
810 Point2D.Float pt = new Point2D.Float(locs[n], locs[n+1]);
811 if (lp == null) {
812 r.setRect(r.getMinX() + pt.x, r.getMinY() + pt.y,
813 r.getWidth(), r.getHeight());
814 } else {
815 lp.pathToPoint(pt, false, pt);
816
817 AffineTransform at = tlc.getBaselineTransform();
818 if (at != null) {
819 AffineTransform tx = AffineTransform.getTranslateInstance
820 (pt.x - at.getTranslateX(), pt.y - at.getTranslateY());
821 tx.concatenate(at);
822 r = tx.createTransformedShape(r).getBounds2D();
823 } else {
824 r.setRect(r.getMinX() + pt.x, r.getMinY() + pt.y,
825 r.getWidth(), r.getHeight());
826 }
827 }
828
829 if (result == null) {
830 result = r;
831 } else {
832 result.add(r);
833 }
834 }
835
836 if (result == null) {
837 result = new Rectangle2D.Float(Float.MAX_VALUE, Float.MAX_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
838 }
839
840 return result;
841 }
842
843 public Rectangle2D getItalicBounds() {
844
845 float left = Float.MAX_VALUE, right = -Float.MAX_VALUE;
846 float top = Float.MAX_VALUE, bottom = -Float.MAX_VALUE;
847
848 for (int i=0, n = 0; i < fComponents.length; i++, n += 2) {
849 TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
850
851 Rectangle2D tlcBounds = tlc.getItalicBounds();
852 float x = locs[n];
853 float y = locs[n+1];
854
855 left = Math.min(left, x + (float)tlcBounds.getX());
856 right = Math.max(right, x + (float)tlcBounds.getMaxX());
857
858 top = Math.min(top, y + (float)tlcBounds.getY());
859 bottom = Math.max(bottom, y + (float)tlcBounds.getMaxY());
860 }
861
862 return new Rectangle2D.Float(left, top, right-left, bottom-top);
863 }
864
865 public Shape getOutline(AffineTransform tx) {
866
867 GeneralPath dstShape = new GeneralPath(GeneralPath.WIND_NON_ZERO);
868
869 for (int i=0, n = 0; i < fComponents.length; i++, n += 2) {
870 TextLineComponent tlc = fComponents[getComponentLogicalIndex(i)];
871
872 dstShape.append(tlc.getOutline(locs[n], locs[n+1]), false);
873 }
874
875 if (tx != null) {
876 dstShape.transform(tx);
877 }
878 return dstShape;
879 }
880
881 public int hashCode() {
882 return (fComponents.length << 16) ^
883 (fComponents[0].hashCode() << 3) ^ (fCharsLimit-fCharsStart);
884 }
885
886 public String toString() {
887 StringBuilder buf = new StringBuilder();
888
889 for (int i = 0; i < fComponents.length; i++) {
890 buf.append(fComponents[i]);
891 }
892
893 return buf.toString();
894 }
895
896 /**
897 * Create a TextLine from the text. The Font must be able to
898 * display all of the text.
899 * attributes==null is equivalent to using an empty Map for
900 * attributes
901 */
902 public static TextLine fastCreateTextLine(FontRenderContext frc,
903 char[] chars,
904 Font font,
905 CoreMetrics lm,
906 Map attributes) {
907
908 boolean isDirectionLTR = true;
909 byte[] levels = null;
910 int[] charsLtoV = null;
911 Bidi bidi = null;
912 int characterCount = chars.length;
913
914 boolean requiresBidi = false;
915 byte[] embs = null;
916
917 AttributeValues values = null;
918 if (attributes != null) {
919 values = AttributeValues.fromMap(attributes);
920 if (values.getRunDirection() >= 0) {
921 isDirectionLTR = values.getRunDirection() == 0;
922 requiresBidi = !isDirectionLTR;
923 }
924 if (values.getBidiEmbedding() != 0) {
925 requiresBidi = true;
926 byte level = (byte)values.getBidiEmbedding();
927 embs = new byte[characterCount];
928 for (int i = 0; i < embs.length; ++i) {
929 embs[i] = level;
930 }
931 }
932 }
933
934 // dlf: get baseRot from font for now???
935
936 if (!requiresBidi) {
937 requiresBidi = Bidi.requiresBidi(chars, 0, chars.length);
938 }
939
940 if (requiresBidi) {
941 int bidiflags = values == null
942 ? Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT
943 : values.getRunDirection();
944
945 bidi = new Bidi(chars, 0, embs, 0, chars.length, bidiflags);
946 if (!bidi.isLeftToRight()) {
947 levels = BidiUtils.getLevels(bidi);
948 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels);
949 charsLtoV = BidiUtils.createInverseMap(charsVtoL);
950 isDirectionLTR = bidi.baseIsLeftToRight();
951 }
952 }
953
954 Decoration decorator = Decoration.getDecoration(values);
955
956 int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
957 TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags);
958
959 TextLineComponent[] components = new TextLineComponent[1];
960
961 components = createComponentsOnRun(0, chars.length,
962 chars,
963 charsLtoV, levels,
964 factory, font, lm,
965 frc,
966 decorator,
967 components,
968 0);
969
970 int numComponents = components.length;
971 while (components[numComponents-1] == null) {
972 numComponents -= 1;
973 }
974
975 if (numComponents != components.length) {
976 TextLineComponent[] temp = new TextLineComponent[numComponents];
977 System.arraycopy(components, 0, temp, 0, numComponents);
978 components = temp;
979 }
980
981 return new TextLine(frc, components, lm.baselineOffsets,
982 chars, 0, chars.length, charsLtoV, levels, isDirectionLTR);
983 }
984
985 private static TextLineComponent[] expandArray(TextLineComponent[] orig) {
986
987 TextLineComponent[] newComponents = new TextLineComponent[orig.length + 8];
988 System.arraycopy(orig, 0, newComponents, 0, orig.length);
989
990 return newComponents;
991 }
992
993 /**
994 * Returns an array in logical order of the TextLineComponents on
995 * the text in the given range, with the given attributes.
996 */
997 public static TextLineComponent[] createComponentsOnRun(int runStart,
998 int runLimit,
999 char[] chars,
1000 int[] charsLtoV,
1001 byte[] levels,
1002 TextLabelFactory factory,
1003 Font font,
1004 CoreMetrics cm,
1005 FontRenderContext frc,
1006 Decoration decorator,
1007 TextLineComponent[] components,
1008 int numComponents) {
1009
1010 int pos = runStart;
1011 do {
1012 int chunkLimit = firstVisualChunk(charsLtoV, levels, pos, runLimit); // <= displayLimit
1013
1014 do {
1015 int startPos = pos;
1016 int lmCount;
1017
1018 if (cm == null) {
1019 LineMetrics lineMetrics = font.getLineMetrics(chars, startPos, chunkLimit, frc);
1020 cm = CoreMetrics.get(lineMetrics);
1021 lmCount = lineMetrics.getNumChars();
1022 }
1023 else {
1024 lmCount = (chunkLimit-startPos);
1025 }
1026
1027 TextLineComponent nextComponent =
1028 factory.createExtended(font, cm, decorator, startPos, startPos + lmCount);
1029
1030 ++numComponents;
1031 if (numComponents >= components.length) {
1032 components = expandArray(components);
1033 }
1034
1035 components[numComponents-1] = nextComponent;
1036
1037 pos += lmCount;
1038 } while (pos < chunkLimit);
1039
1040 } while (pos < runLimit);
1041
1042 return components;
1043 }
1044
1045 /**
1046 * Returns an array (in logical order) of the TextLineComponents representing
1047 * the text. The components are both logically and visually contiguous.
1048 */
1049 public static TextLineComponent[] getComponents(StyledParagraph styledParagraph,
1050 char[] chars,
1051 int textStart,
1052 int textLimit,
1053 int[] charsLtoV,
1054 byte[] levels,
1055 TextLabelFactory factory) {
1056
1057 FontRenderContext frc = factory.getFontRenderContext();
1058
1059 int numComponents = 0;
1060 TextLineComponent[] tempComponents = new TextLineComponent[1];
1061
1062 int pos = textStart;
1063 do {
1064 int runLimit = Math.min(styledParagraph.getRunLimit(pos), textLimit);
1065
1066 Decoration decorator = styledParagraph.getDecorationAt(pos);
1067
1068 Object graphicOrFont = styledParagraph.getFontOrGraphicAt(pos);
1069
1070 if (graphicOrFont instanceof GraphicAttribute) {
1071 // AffineTransform baseRot = styledParagraph.getBaselineRotationAt(pos);
1072 // !!! For now, let's assign runs of text with both fonts and graphic attributes
1073 // a null rotation (e.g. the baseline rotation goes away when a graphic
1074 // is applied.
1075 AffineTransform baseRot = null;
1076 GraphicAttribute graphicAttribute = (GraphicAttribute) graphicOrFont;
1077 do {
1078 int chunkLimit = firstVisualChunk(charsLtoV, levels,
1079 pos, runLimit);
1080
1081 GraphicComponent nextGraphic =
1082 new GraphicComponent(graphicAttribute, decorator, charsLtoV, levels, pos, chunkLimit, baseRot);
1083 pos = chunkLimit;
1084
1085 ++numComponents;
1086 if (numComponents >= tempComponents.length) {
1087 tempComponents = expandArray(tempComponents);
1088 }
1089
1090 tempComponents[numComponents-1] = nextGraphic;
1091
1092 } while(pos < runLimit);
1093 }
1094 else {
1095 Font font = (Font) graphicOrFont;
1096
1097 tempComponents = createComponentsOnRun(pos, runLimit,
1098 chars,
1099 charsLtoV, levels,
1100 factory, font, null,
1101 frc,
1102 decorator,
1103 tempComponents,
1104 numComponents);
1105 pos = runLimit;
1106 numComponents = tempComponents.length;
1107 while (tempComponents[numComponents-1] == null) {
1108 numComponents -= 1;
1109 }
1110 }
1111
1112 } while (pos < textLimit);
1113
1114 TextLineComponent[] components;
1115 if (tempComponents.length == numComponents) {
1116 components = tempComponents;
1117 }
1118 else {
1119 components = new TextLineComponent[numComponents];
1120 System.arraycopy(tempComponents, 0, components, 0, numComponents);
1121 }
1122
1123 return components;
1124 }
1125
1126 /**
1127 * Create a TextLine from the Font and character data over the
1128 * range. The range is relative to both the StyledParagraph and the
1129 * character array.
1130 */
1131 public static TextLine createLineFromText(char[] chars,
1132 StyledParagraph styledParagraph,
1133 TextLabelFactory factory,
1134 boolean isDirectionLTR,
1135 float[] baselineOffsets) {
1136
1137 factory.setLineContext(0, chars.length);
1138
1139 Bidi lineBidi = factory.getLineBidi();
1140 int[] charsLtoV = null;
1141 byte[] levels = null;
1142
1143 if (lineBidi != null) {
1144 levels = BidiUtils.getLevels(lineBidi);
1145 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels);
1146 charsLtoV = BidiUtils.createInverseMap(charsVtoL);
1147 }
1148
1149 TextLineComponent[] components =
1150 getComponents(styledParagraph, chars, 0, chars.length, charsLtoV, levels, factory);
1151
1152 return new TextLine(factory.getFontRenderContext(), components, baselineOffsets,
1153 chars, 0, chars.length, charsLtoV, levels, isDirectionLTR);
1154 }
1155
1156 /**
1157 * Compute the components order from the given components array and
1158 * logical-to-visual character mapping. May return null if canonical.
1159 */
1160 private static int[] computeComponentOrder(TextLineComponent[] components,
1161 int[] charsLtoV) {
1162
1163 /*
1164 * Create a visual ordering for the glyph sets. The important thing
1165 * here is that the values have the proper rank with respect to
1166 * each other, not the exact values. For example, the first glyph
1167 * set that appears visually should have the lowest value. The last
1168 * should have the highest value. The values are then normalized
1169 * to map 1-1 with positions in glyphs.
1170 *
1171 */
1172 int[] componentOrder = null;
1173 if (charsLtoV != null && components.length > 1) {
1174 componentOrder = new int[components.length];
1175 int gStart = 0;
1176 for (int i = 0; i < components.length; i++) {
1177 componentOrder[i] = charsLtoV[gStart];
1178 gStart += components[i].getNumCharacters();
1179 }
1180
1181 componentOrder = BidiUtils.createContiguousOrder(componentOrder);
1182 componentOrder = BidiUtils.createInverseMap(componentOrder);
1183 }
1184 return componentOrder;
1185 }
1186
1187
1188 /**
1189 * Create a TextLine from the text. chars is just the text in the iterator.
1190 */
1191 public static TextLine standardCreateTextLine(FontRenderContext frc,
1192 AttributedCharacterIterator text,
1193 char[] chars,
1194 float[] baselineOffsets) {
1195
1196 StyledParagraph styledParagraph = new StyledParagraph(text, chars);
1197 Bidi bidi = new Bidi(text);
1198 if (bidi.isLeftToRight()) {
1199 bidi = null;
1200 }
1201 int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
1202 TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags);
1203
1204 boolean isDirectionLTR = true;
1205 if (bidi != null) {
1206 isDirectionLTR = bidi.baseIsLeftToRight();
1207 }
1208 return createLineFromText(chars, styledParagraph, factory, isDirectionLTR, baselineOffsets);
1209 }
1210
1211
1212
1213 /*
1214 * A utility to get a range of text that is both logically and visually
1215 * contiguous.
1216 * If the entire range is ok, return limit, otherwise return the first
1217 * directional change after start. We could do better than this, but
1218 * it doesn't seem worth it at the moment.
1219 private static int firstVisualChunk(int order[], byte direction[],
1220 int start, int limit)
1221 {
1222 if (order != null) {
1223 int min = order[start];
1224 int max = order[start];
1225 int count = limit - start;
1226 for (int i = start + 1; i < limit; i++) {
1227 min = Math.min(min, order[i]);
1228 max = Math.max(max, order[i]);
1229 if (max - min >= count) {
1230 if (direction != null) {
1231 byte baseLevel = direction[start];
1232 for (int j = start + 1; j < i; j++) {
1233 if (direction[j] != baseLevel) {
1234 return j;
1235 }
1236 }
1237 }
1238 return i;
1239 }
1240 }
1241 }
1242 return limit;
1243 }
1244 */
1245
1246 /**
1247 * When this returns, the ACI's current position will be at the start of the
1248 * first run which does NOT contain a GraphicAttribute. If no such run exists
1249 * the ACI's position will be at the end, and this method will return false.
1250 */
1251 static boolean advanceToFirstFont(AttributedCharacterIterator aci) {
1252
1253 for (char ch = aci.first(); ch != aci.DONE; ch = aci.setIndex(aci.getRunLimit())) {
1254
1255 if (aci.getAttribute(TextAttribute.CHAR_REPLACEMENT) == null) {
1256 return true;
1257 }
1258 }
1259
1260 return false;
1261 }
1262
1263 static float[] getNormalizedOffsets(float[] baselineOffsets, byte baseline) {
1264
1265 if (baselineOffsets[baseline] != 0) {
1266 float base = baselineOffsets[baseline];
1267 float[] temp = new float[baselineOffsets.length];
1268 for (int i = 0; i < temp.length; i++)
1269 temp[i] = baselineOffsets[i] - base;
1270 baselineOffsets = temp;
1271 }
1272 return baselineOffsets;
1273 }
1274
1275 static Font getFontAtCurrentPos(AttributedCharacterIterator aci) {
1276
1277 Object value = aci.getAttribute(TextAttribute.FONT);
1278 if (value != null) {
1279 return (Font) value;
1280 }
1281 if (aci.getAttribute(TextAttribute.FAMILY) != null) {
1282 return Font.getFont(aci.getAttributes());
1283 }
1284
1285 int ch = CodePointIterator.create(aci).next();
1286 if (ch != CodePointIterator.DONE) {
1287 FontResolver resolver = FontResolver.getInstance();
1288 return resolver.getFont(resolver.getFontIndex(ch), aci.getAttributes());
1289 }
1290 return null;
1291 }
1292
1293 /*
1294 * The new version requires that chunks be at the same level.
1295 */
1296 private static int firstVisualChunk(int order[], byte direction[],
1297 int start, int limit)
1298 {
1299 if (order != null && direction != null) {
1300 byte dir = direction[start];
1301 while (++start < limit && direction[start] == dir) {}
1302 return start;
1303 }
1304 return limit;
1305 }
1306
1307 /*
1308 * create a new line with characters between charStart and charLimit
1309 * justified using the provided width and ratio.
1310 */
1311 public TextLine getJustifiedLine(float justificationWidth, float justifyRatio, int justStart, int justLimit) {
1312
1313 TextLineComponent[] newComponents = new TextLineComponent[fComponents.length];
1314 System.arraycopy(fComponents, 0, newComponents, 0, fComponents.length);
1315
1316 float leftHang = 0;
1317 float adv = 0;
1318 float justifyDelta = 0;
1319 boolean rejustify = false;
1320 do {
1321 adv = getAdvanceBetween(newComponents, 0, characterCount());
1322
1323 // all characters outside the justification range must be in the base direction
1324 // of the layout, otherwise justification makes no sense.
1325
1326 float justifyAdvance = getAdvanceBetween(newComponents, justStart, justLimit);
1327
1328 // get the actual justification delta
1329 justifyDelta = (justificationWidth - justifyAdvance) * justifyRatio;
1330
1331 // generate an array of GlyphJustificationInfo records to pass to
1332 // the justifier. Array is visually ordered.
1333
1334 // get positions that each component will be using
1335 int[] infoPositions = new int[newComponents.length];
1336 int infoCount = 0;
1337 for (int visIndex = 0; visIndex < newComponents.length; visIndex++) {
1338 int logIndex = getComponentLogicalIndex(visIndex);
1339 infoPositions[logIndex] = infoCount;
1340 infoCount += newComponents[logIndex].getNumJustificationInfos();
1341 }
1342 GlyphJustificationInfo[] infos = new GlyphJustificationInfo[infoCount];
1343
1344 // get justification infos
1345 int compStart = 0;
1346 for (int i = 0; i < newComponents.length; i++) {
1347 TextLineComponent comp = newComponents[i];
1348 int compLength = comp.getNumCharacters();
1349 int compLimit = compStart + compLength;
1350 if (compLimit > justStart) {
1351 int rangeMin = Math.max(0, justStart - compStart);
1352 int rangeMax = Math.min(compLength, justLimit - compStart);
1353 comp.getJustificationInfos(infos, infoPositions[i], rangeMin, rangeMax);
1354
1355 if (compLimit >= justLimit) {
1356 break;
1357 }
1358 }
1359 }
1360
1361 // records are visually ordered, and contiguous, so start and end are
1362 // simply the places where we didn't fetch records
1363 int infoStart = 0;
1364 int infoLimit = infoCount;
1365 while (infoStart < infoLimit && infos[infoStart] == null) {
1366 ++infoStart;
1367 }
1368
1369 while (infoLimit > infoStart && infos[infoLimit - 1] == null) {
1370 --infoLimit;
1371 }
1372
1373 // invoke justifier on the records
1374 TextJustifier justifier = new TextJustifier(infos, infoStart, infoLimit);
1375
1376 float[] deltas = justifier.justify(justifyDelta);
1377
1378 boolean canRejustify = rejustify == false;
1379 boolean wantRejustify = false;
1380 boolean[] flags = new boolean[1];
1381
1382 // apply justification deltas
1383 compStart = 0;
1384 for (int i = 0; i < newComponents.length; i++) {
1385 TextLineComponent comp = newComponents[i];
1386 int compLength = comp.getNumCharacters();
1387 int compLimit = compStart + compLength;
1388 if (compLimit > justStart) {
1389 int rangeMin = Math.max(0, justStart - compStart);
1390 int rangeMax = Math.min(compLength, justLimit - compStart);
1391 newComponents[i] = comp.applyJustificationDeltas(deltas, infoPositions[i] * 2, flags);
1392
1393 wantRejustify |= flags[0];
1394
1395 if (compLimit >= justLimit) {
1396 break;
1397 }
1398 }
1399 }
1400
1401 rejustify = wantRejustify && !rejustify; // only make two passes
1402 } while (rejustify);
1403
1404 return new TextLine(frc, newComponents, fBaselineOffsets, fChars, fCharsStart,
1405 fCharsLimit, fCharLogicalOrder, fCharLevels,
1406 fIsDirectionLTR);
1407 }
1408
1409 // return the sum of the advances of text between the logical start and limit
1410 public static float getAdvanceBetween(TextLineComponent[] components, int start, int limit) {
1411 float advance = 0;
1412
1413 int tlcStart = 0;
1414 for(int i = 0; i < components.length; i++) {
1415 TextLineComponent comp = components[i];
1416
1417 int tlcLength = comp.getNumCharacters();
1418 int tlcLimit = tlcStart + tlcLength;
1419 if (tlcLimit > start) {
1420 int measureStart = Math.max(0, start - tlcStart);
1421 int measureLimit = Math.min(tlcLength, limit - tlcStart);
1422 advance += comp.getAdvanceBetween(measureStart, measureLimit);
1423 if (tlcLimit >= limit) {
1424 break;
1425 }
1426 }
1427
1428 tlcStart = tlcLimit;
1429 }
1430
1431 return advance;
1432 }
1433
1434 LayoutPathImpl getLayoutPath() {
1435 return lp;
1436 }
1437}
java/pkg/java/awt/font/TextMeasurer.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TextMeasurer.java+759−0
@@ -0,0 +1,759 @@
1/*
2 * Copyright (c) 1997, 2005, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.awt.Font;
44
45import java.text.AttributedCharacterIterator;
46import java.text.AttributedString;
47import java.text.Bidi;
48import java.text.BreakIterator;
49import java.text.CharacterIterator;
50
51import java.awt.font.FontRenderContext;
52
53import java.util.Hashtable;
54import java.util.Map;
55
56import sun.font.AttributeValues;
57import sun.font.BidiUtils;
58import sun.font.TextLineComponent;
59import sun.font.TextLabelFactory;
60import sun.font.FontResolver;
61
62/**
63 * The <code>TextMeasurer</code> class provides the primitive operations
64 * needed for line break: measuring up to a given advance, determining the
65 * advance of a range of characters, and generating a
66 * <code>TextLayout</code> for a range of characters. It also provides
67 * methods for incremental editing of paragraphs.
68 * <p>
69 * A <code>TextMeasurer</code> object is constructed with an
70 * {@link java.text.AttributedCharacterIterator AttributedCharacterIterator}
71 * representing a single paragraph of text. The value returned by the
72 * {@link AttributedCharacterIterator#getBeginIndex() getBeginIndex}
73 * method of <code>AttributedCharacterIterator</code>
74 * defines the absolute index of the first character. The value
75 * returned by the
76 * {@link AttributedCharacterIterator#getEndIndex() getEndIndex}
77 * method of <code>AttributedCharacterIterator</code> defines the index
78 * past the last character. These values define the range of indexes to
79 * use in calls to the <code>TextMeasurer</code>. For example, calls to
80 * get the advance of a range of text or the line break of a range of text
81 * must use indexes between the beginning and end index values. Calls to
82 * {@link #insertChar(java.text.AttributedCharacterIterator, int) insertChar}
83 * and
84 * {@link #deleteChar(java.text.AttributedCharacterIterator, int) deleteChar}
85 * reset the <code>TextMeasurer</code> to use the beginning index and end
86 * index of the <code>AttributedCharacterIterator</code> passed in those calls.
87 * <p>
88 * Most clients will use the more convenient <code>LineBreakMeasurer</code>,
89 * which implements the standard line break policy (placing as many words
90 * as will fit on each line).
91 *
92 * @author John Raley
93 * @see LineBreakMeasurer
94 * @since 1.3
95 */
96
97public final class TextMeasurer implements Cloneable {
98
99 // Number of lines to format to.
100 private static float EST_LINES = (float) 2.1;
101
102 /*
103 static {
104 String s = System.getProperty("estLines");
105 if (s != null) {
106 try {
107 Float f = new Float(s);
108 EST_LINES = f.floatValue();
109 }
110 catch(NumberFormatException e) {
111 }
112 }
113 //System.out.println("EST_LINES="+EST_LINES);
114 }
115 */
116
117 private FontRenderContext fFrc;
118
119 private int fStart;
120
121 // characters in source text
122 private char[] fChars;
123
124 // Bidi for this paragraph
125 private Bidi fBidi;
126
127 // Levels array for chars in this paragraph - needed to reorder
128 // trailing counterdirectional whitespace
129 private byte[] fLevels;
130
131 // line components in logical order
132 private TextLineComponent[] fComponents;
133
134 // index where components begin
135 private int fComponentStart;
136
137 // index where components end
138 private int fComponentLimit;
139
140 private boolean haveLayoutWindow;
141
142 // used to find valid starting points for line components
143 private BreakIterator fLineBreak = null;
144 private CharArrayIterator charIter = null;
145 int layoutCount = 0;
146 int layoutCharCount = 0;
147
148 // paragraph, with resolved fonts and styles
149 private StyledParagraph fParagraph;
150
151 // paragraph data - same across all layouts
152 private boolean fIsDirectionLTR;
153 private byte fBaseline;
154 private float[] fBaselineOffsets;
155 private float fJustifyRatio = 1;
156
157 /**
158 * Constructs a <code>TextMeasurer</code> from the source text.
159 * The source text should be a single entire paragraph.
160 * @param text the source paragraph. Cannot be null.
161 * @param frc the information about a graphics device which is needed
162 * to measure the text correctly. Cannot be null.
163 */
164 public TextMeasurer(AttributedCharacterIterator text, FontRenderContext frc) {
165
166 fFrc = frc;
167 initAll(text);
168 }
169
170 protected Object clone() {
171 TextMeasurer other;
172 try {
173 other = (TextMeasurer) super.clone();
174 }
175 catch(CloneNotSupportedException e) {
176 throw new Error();
177 }
178 if (fComponents != null) {
179 other.fComponents = (TextLineComponent[]) fComponents.clone();
180 }
181 return other;
182 }
183
184 private void invalidateComponents() {
185 fComponentStart = fComponentLimit = fChars.length;
186 fComponents = null;
187 haveLayoutWindow = false;
188 }
189
190 /**
191 * Initialize state, including fChars array, direction, and
192 * fBidi.
193 */
194 private void initAll(AttributedCharacterIterator text) {
195
196 fStart = text.getBeginIndex();
197
198 // extract chars
199 fChars = new char[text.getEndIndex() - fStart];
200
201 int n = 0;
202 for (char c = text.first(); c != text.DONE; c = text.next()) {
203 fChars[n++] = c;
204 }
205
206 text.first();
207
208 fBidi = new Bidi(text);
209 if (fBidi.isLeftToRight()) {
210 fBidi = null;
211 }
212
213 text.first();
214 Map paragraphAttrs = text.getAttributes();
215 NumericShaper shaper = AttributeValues.getNumericShaping(paragraphAttrs);
216 if (shaper != null) {
217 shaper.shape(fChars, 0, fChars.length);
218 }
219
220 fParagraph = new StyledParagraph(text, fChars);
221
222 // set paragraph attributes
223 {
224 // If there's an embedded graphic at the start of the
225 // paragraph, look for the first non-graphic character
226 // and use it and its font to initialize the paragraph.
227 // If not, use the first graphic to initialize.
228 fJustifyRatio = AttributeValues.getJustification(paragraphAttrs);
229
230 boolean haveFont = TextLine.advanceToFirstFont(text);
231
232 if (haveFont) {
233 Font defaultFont = TextLine.getFontAtCurrentPos(text);
234 int charsStart = text.getIndex() - text.getBeginIndex();
235 LineMetrics lm = defaultFont.getLineMetrics(fChars, charsStart, charsStart+1, fFrc);
236 fBaseline = (byte) lm.getBaselineIndex();
237 fBaselineOffsets = lm.getBaselineOffsets();
238 }
239 else {
240 // hmmm what to do here? Just try to supply reasonable
241 // values I guess.
242
243 GraphicAttribute graphic = (GraphicAttribute)
244 paragraphAttrs.get(TextAttribute.CHAR_REPLACEMENT);
245 fBaseline = TextLayout.getBaselineFromGraphic(graphic);
246 Font dummyFont = new Font(new Hashtable(5, (float)0.9));
247 LineMetrics lm = dummyFont.getLineMetrics(" ", 0, 1, fFrc);
248 fBaselineOffsets = lm.getBaselineOffsets();
249 }
250 fBaselineOffsets = TextLine.getNormalizedOffsets(fBaselineOffsets, fBaseline);
251 }
252
253 invalidateComponents();
254 }
255
256 /**
257 * Generate components for the paragraph. fChars, fBidi should have been
258 * initialized already.
259 */
260 private void generateComponents(int startingAt, int endingAt) {
261
262 if (collectStats) {
263 formattedChars += (endingAt-startingAt);
264 }
265 int layoutFlags = 0; // no extra info yet, bidi determines run and line direction
266 TextLabelFactory factory = new TextLabelFactory(fFrc, fChars, fBidi, layoutFlags);
267
268 int[] charsLtoV = null;
269
270 if (fBidi != null) {
271 fLevels = BidiUtils.getLevels(fBidi);
272 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(fLevels);
273 charsLtoV = BidiUtils.createInverseMap(charsVtoL);
274 fIsDirectionLTR = fBidi.baseIsLeftToRight();
275 }
276 else {
277 fLevels = null;
278 fIsDirectionLTR = true;
279 }
280
281 try {
282 fComponents = TextLine.getComponents(
283 fParagraph, fChars, startingAt, endingAt, charsLtoV, fLevels, factory);
284 }
285 catch(IllegalArgumentException e) {
286 System.out.println("startingAt="+startingAt+"; endingAt="+endingAt);
287 System.out.println("fComponentLimit="+fComponentLimit);
288 throw e;
289 }
290
291 fComponentStart = startingAt;
292 fComponentLimit = endingAt;
293 //debugFormatCount += (endingAt-startingAt);
294 }
295
296 private int calcLineBreak(final int pos, final float maxAdvance) {
297
298 // either of these statements removes the bug:
299 //generateComponents(0, fChars.length);
300 //generateComponents(pos, fChars.length);
301
302 int startPos = pos;
303 float width = maxAdvance;
304
305 int tlcIndex;
306 int tlcStart = fComponentStart;
307
308 for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
309 int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
310 if (gaLimit > startPos) {
311 break;
312 }
313 else {
314 tlcStart = gaLimit;
315 }
316 }
317
318 // tlcStart is now the start of the tlc at tlcIndex
319
320 for (; tlcIndex < fComponents.length; tlcIndex++) {
321
322 TextLineComponent tlc = fComponents[tlcIndex];
323 int numCharsInGa = tlc.getNumCharacters();
324
325 int lineBreak = tlc.getLineBreakIndex(startPos - tlcStart, width);
326 if (lineBreak == numCharsInGa && tlcIndex < fComponents.length) {
327 width -= tlc.getAdvanceBetween(startPos - tlcStart, lineBreak);
328 tlcStart += numCharsInGa;
329 startPos = tlcStart;
330 }
331 else {
332 return tlcStart + lineBreak;
333 }
334 }
335
336 if (fComponentLimit < fChars.length) {
337 // format more text and try again
338 //if (haveLayoutWindow) {
339 // outOfWindow++;
340 //}
341
342 generateComponents(pos, fChars.length);
343 return calcLineBreak(pos, maxAdvance);
344 }
345
346 return fChars.length;
347 }
348
349 /**
350 * According to the Unicode Bidirectional Behavior specification
351 * (Unicode Standard 2.0, section 3.11), whitespace at the ends
352 * of lines which would naturally flow against the base direction
353 * must be made to flow with the line direction, and moved to the
354 * end of the line. This method returns the start of the sequence
355 * of trailing whitespace characters to move to the end of a
356 * line taken from the given range.
357 */
358 private int trailingCdWhitespaceStart(int startPos, int limitPos) {
359
360 if (fLevels != null) {
361 // Back up over counterdirectional whitespace
362 final byte baseLevel = (byte) (fIsDirectionLTR? 0 : 1);
363 for (int cdWsStart = limitPos; --cdWsStart >= startPos;) {
364 if ((fLevels[cdWsStart] % 2) == baseLevel ||
365 Character.getDirectionality(fChars[cdWsStart]) != Character.DIRECTIONALITY_WHITESPACE) {
366 return ++cdWsStart;
367 }
368 }
369 }
370
371 return startPos;
372 }
373
374 private TextLineComponent[] makeComponentsOnRange(int startPos,
375 int limitPos) {
376
377 // sigh I really hate to do this here since it's part of the
378 // bidi algorithm.
379 // cdWsStart is the start of the trailing counterdirectional
380 // whitespace
381 final int cdWsStart = trailingCdWhitespaceStart(startPos, limitPos);
382
383 int tlcIndex;
384 int tlcStart = fComponentStart;
385
386 for (tlcIndex = 0; tlcIndex < fComponents.length; tlcIndex++) {
387 int gaLimit = tlcStart + fComponents[tlcIndex].getNumCharacters();
388 if (gaLimit > startPos) {
389 break;
390 }
391 else {
392 tlcStart = gaLimit;
393 }
394 }
395
396 // tlcStart is now the start of the tlc at tlcIndex
397
398 int componentCount;
399 {
400 boolean split = false;
401 int compStart = tlcStart;
402 int lim=tlcIndex;
403 for (boolean cont=true; cont; lim++) {
404 int gaLimit = compStart + fComponents[lim].getNumCharacters();
405 if (cdWsStart > Math.max(compStart, startPos)
406 && cdWsStart < Math.min(gaLimit, limitPos)) {
407 split = true;
408 }
409 if (gaLimit >= limitPos) {
410 cont=false;
411 }
412 else {
413 compStart = gaLimit;
414 }
415 }
416 componentCount = lim-tlcIndex;
417 if (split) {
418 componentCount++;
419 }
420 }
421
422 TextLineComponent[] components = new TextLineComponent[componentCount];
423 int newCompIndex = 0;
424 int linePos = startPos;
425
426 int breakPt = cdWsStart;
427
428 int subsetFlag;
429 if (breakPt == startPos) {
430 subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
431 TextLineComponent.RIGHT_TO_LEFT;
432 breakPt = limitPos;
433 }
434 else {
435 subsetFlag = TextLineComponent.UNCHANGED;
436 }
437
438 while (linePos < limitPos) {
439
440 int compLength = fComponents[tlcIndex].getNumCharacters();
441 int tlcLimit = tlcStart + compLength;
442
443 int start = Math.max(linePos, tlcStart);
444 int limit = Math.min(breakPt, tlcLimit);
445
446 components[newCompIndex++] = fComponents[tlcIndex].getSubset(
447 start-tlcStart,
448 limit-tlcStart,
449 subsetFlag);
450 linePos += (limit-start);
451 if (linePos == breakPt) {
452 breakPt = limitPos;
453 subsetFlag = fIsDirectionLTR? TextLineComponent.LEFT_TO_RIGHT :
454 TextLineComponent.RIGHT_TO_LEFT;
455 }
456 if (linePos == tlcLimit) {
457 tlcIndex++;
458 tlcStart = tlcLimit;
459 }
460 }
461
462 return components;
463 }
464
465 private TextLine makeTextLineOnRange(int startPos, int limitPos) {
466
467 int[] charsLtoV = null;
468 byte[] charLevels = null;
469
470 if (fBidi != null) {
471 Bidi lineBidi = fBidi.createLineBidi(startPos, limitPos);
472 charLevels = BidiUtils.getLevels(lineBidi);
473 int[] charsVtoL = BidiUtils.createVisualToLogicalMap(charLevels);
474 charsLtoV = BidiUtils.createInverseMap(charsVtoL);
475 }
476
477 TextLineComponent[] components = makeComponentsOnRange(startPos, limitPos);
478
479 return new TextLine(fFrc,
480 components,
481 fBaselineOffsets,
482 fChars,
483 startPos,
484 limitPos,
485 charsLtoV,
486 charLevels,
487 fIsDirectionLTR);
488
489 }
490
491 private void ensureComponents(int start, int limit) {
492
493 if (start < fComponentStart || limit > fComponentLimit) {
494 generateComponents(start, limit);
495 }
496 }
497
498 private void makeLayoutWindow(int localStart) {
499
500 int compStart = localStart;
501 int compLimit = fChars.length;
502
503 // If we've already gone past the layout window, format to end of paragraph
504 if (layoutCount > 0 && !haveLayoutWindow) {
505 float avgLineLength = Math.max(layoutCharCount / layoutCount, 1);
506 compLimit = Math.min(localStart + (int)(avgLineLength*EST_LINES), fChars.length);
507 }
508
509 if (localStart > 0 || compLimit < fChars.length) {
510 if (charIter == null) {
511 charIter = new CharArrayIterator(fChars);
512 }
513 else {
514 charIter.reset(fChars);
515 }
516 if (fLineBreak == null) {
517 fLineBreak = BreakIterator.getLineInstance();
518 }
519 fLineBreak.setText(charIter);
520 if (localStart > 0) {
521 if (!fLineBreak.isBoundary(localStart)) {
522 compStart = fLineBreak.preceding(localStart);
523 }
524 }
525 if (compLimit < fChars.length) {
526 if (!fLineBreak.isBoundary(compLimit)) {
527 compLimit = fLineBreak.following(compLimit);
528 }
529 }
530 }
531
532 ensureComponents(compStart, compLimit);
533 haveLayoutWindow = true;
534 }
535
536 /**
537 * Returns the index of the first character which will not fit on
538 * on a line beginning at <code>start</code> and possible
539 * measuring up to <code>maxAdvance</code> in graphical width.
540 *
541 * @param start the character index at which to start measuring.
542 * <code>start</code> is an absolute index, not relative to the
543 * start of the paragraph
544 * @param maxAdvance the graphical width in which the line must fit
545 * @return the index after the last character that will fit
546 * on a line beginning at <code>start</code>, which is not longer
547 * than <code>maxAdvance</code> in graphical width
548 * @throws IllegalArgumentException if <code>start</code> is
549 * less than the beginning of the paragraph.
550 */
551 public int getLineBreakIndex(int start, float maxAdvance) {
552
553 int localStart = start - fStart;
554
555 if (!haveLayoutWindow ||
556 localStart < fComponentStart ||
557 localStart >= fComponentLimit) {
558 makeLayoutWindow(localStart);
559 }
560
561 return calcLineBreak(localStart, maxAdvance) + fStart;
562 }
563
564 /**
565 * Returns the graphical width of a line beginning at <code>start</code>
566 * and including characters up to <code>limit</code>.
567 * <code>start</code> and <code>limit</code> are absolute indices,
568 * not relative to the start of the paragraph.
569 *
570 * @param start the character index at which to start measuring
571 * @param limit the character index at which to stop measuring
572 * @return the graphical width of a line beginning at <code>start</code>
573 * and including characters up to <code>limit</code>
574 * @throws IndexOutOfBoundsException if <code>limit</code> is less
575 * than <code>start</code>
576 * @throws IllegalArgumentException if <code>start</code> or
577 * <code>limit</code> is not between the beginning of
578 * the paragraph and the end of the paragraph.
579 */
580 public float getAdvanceBetween(int start, int limit) {
581
582 int localStart = start - fStart;
583 int localLimit = limit - fStart;
584
585 ensureComponents(localStart, localLimit);
586 TextLine line = makeTextLineOnRange(localStart, localLimit);
587 return line.getMetrics().advance;
588 // could cache line in case getLayout is called with same start, limit
589 }
590
591 /**
592 * Returns a <code>TextLayout</code> on the given character range.
593 *
594 * @param start the index of the first character
595 * @param limit the index after the last character. Must be greater
596 * than <code>start</code>
597 * @return a <code>TextLayout</code> for the characters beginning at
598 * <code>start</code> up to (but not including) <code>limit</code>
599 * @throws IndexOutOfBoundsException if <code>limit</code> is less
600 * than <code>start</code>
601 * @throws IllegalArgumentException if <code>start</code> or
602 * <code>limit</code> is not between the beginning of
603 * the paragraph and the end of the paragraph.
604 */
605 public TextLayout getLayout(int start, int limit) {
606
607 int localStart = start - fStart;
608 int localLimit = limit - fStart;
609
610 ensureComponents(localStart, localLimit);
611 TextLine textLine = makeTextLineOnRange(localStart, localLimit);
612
613 if (localLimit < fChars.length) {
614 layoutCharCount += limit-start;
615 layoutCount++;
616 }
617
618 return new TextLayout(textLine,
619 fBaseline,
620 fBaselineOffsets,
621 fJustifyRatio);
622 }
623
624 private int formattedChars = 0;
625 private static boolean wantStats = false;/*"true".equals(System.getProperty("collectStats"));*/
626 private boolean collectStats = false;
627
628 private void printStats() {
629 System.out.println("formattedChars: " + formattedChars);
630 //formattedChars = 0;
631 collectStats = false;
632 }
633
634 /**
635 * Updates the <code>TextMeasurer</code> after a single character has
636 * been inserted
637 * into the paragraph currently represented by this
638 * <code>TextMeasurer</code>. After this call, this
639 * <code>TextMeasurer</code> is equivalent to a new
640 * <code>TextMeasurer</code> created from the text; however, it will
641 * usually be more efficient to update an existing
642 * <code>TextMeasurer</code> than to create a new one from scratch.
643 *
644 * @param newParagraph the text of the paragraph after performing
645 * the insertion. Cannot be null.
646 * @param insertPos the position in the text where the character was
647 * inserted. Must not be less than the start of
648 * <code>newParagraph</code>, and must be less than the end of
649 * <code>newParagraph</code>.
650 * @throws IndexOutOfBoundsException if <code>insertPos</code> is less
651 * than the start of <code>newParagraph</code> or greater than
652 * or equal to the end of <code>newParagraph</code>
653 * @throws NullPointerException if <code>newParagraph</code> is
654 * <code>null</code>
655 */
656 public void insertChar(AttributedCharacterIterator newParagraph, int insertPos) {
657
658 if (collectStats) {
659 printStats();
660 }
661 if (wantStats) {
662 collectStats = true;
663 }
664
665 fStart = newParagraph.getBeginIndex();
666 int end = newParagraph.getEndIndex();
667 if (end - fStart != fChars.length+1) {
668 initAll(newParagraph);
669 }
670
671 char[] newChars = new char[end-fStart];
672 int newCharIndex = insertPos - fStart;
673 System.arraycopy(fChars, 0, newChars, 0, newCharIndex);
674
675 char newChar = newParagraph.setIndex(insertPos);
676 newChars[newCharIndex] = newChar;
677 System.arraycopy(fChars,
678 newCharIndex,
679 newChars,
680 newCharIndex+1,
681 end-insertPos-1);
682 fChars = newChars;
683
684 if (fBidi != null || Bidi.requiresBidi(newChars, newCharIndex, newCharIndex + 1) ||
685 newParagraph.getAttribute(TextAttribute.BIDI_EMBEDDING) != null) {
686
687 fBidi = new Bidi(newParagraph);
688 if (fBidi.isLeftToRight()) {
689 fBidi = null;
690 }
691 }
692
693 fParagraph = StyledParagraph.insertChar(newParagraph,
694 fChars,
695 insertPos,
696 fParagraph);
697 invalidateComponents();
698 }
699
700 /**
701 * Updates the <code>TextMeasurer</code> after a single character has
702 * been deleted
703 * from the paragraph currently represented by this
704 * <code>TextMeasurer</code>. After this call, this
705 * <code>TextMeasurer</code> is equivalent to a new <code>TextMeasurer</code>
706 * created from the text; however, it will usually be more efficient
707 * to update an existing <code>TextMeasurer</code> than to create a new one
708 * from scratch.
709 *
710 * @param newParagraph the text of the paragraph after performing
711 * the deletion. Cannot be null.
712 * @param deletePos the position in the text where the character was removed.
713 * Must not be less than
714 * the start of <code>newParagraph</code>, and must not be greater than the
715 * end of <code>newParagraph</code>.
716 * @throws IndexOutOfBoundsException if <code>deletePos</code> is
717 * less than the start of <code>newParagraph</code> or greater
718 * than the end of <code>newParagraph</code>
719 * @throws NullPointerException if <code>newParagraph</code> is
720 * <code>null</code>
721 */
722 public void deleteChar(AttributedCharacterIterator newParagraph, int deletePos) {
723
724 fStart = newParagraph.getBeginIndex();
725 int end = newParagraph.getEndIndex();
726 if (end - fStart != fChars.length-1) {
727 initAll(newParagraph);
728 }
729
730 char[] newChars = new char[end-fStart];
731 int changedIndex = deletePos-fStart;
732
733 System.arraycopy(fChars, 0, newChars, 0, deletePos-fStart);
734 System.arraycopy(fChars, changedIndex+1, newChars, changedIndex, end-deletePos);
735 fChars = newChars;
736
737 if (fBidi != null) {
738 fBidi = new Bidi(newParagraph);
739 if (fBidi.isLeftToRight()) {
740 fBidi = null;
741 }
742 }
743
744 fParagraph = StyledParagraph.deleteChar(newParagraph,
745 fChars,
746 deletePos,
747 fParagraph);
748 invalidateComponents();
749 }
750
751 /**
752 * NOTE: This method is only for LineBreakMeasurer's use. It is package-
753 * private because it returns internal data.
754 */
755 char[] getChars() {
756
757 return fChars;
758 }
759}
java/pkg/java/awt/font/TransformAttribute.class+0−0

Binary file not shown.

java/pkg/java/awt/font/TransformAttribute.java+155−0
@@ -0,0 +1,155 @@
1/*
2 * Copyright (c) 1998, 2005, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - 1997, All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998, All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by Taligent, Inc., a wholly-owned subsidiary
32 * of IBM. These materials are provided under terms of a License
33 * Agreement between Taligent and Sun. This technology is protected
34 * by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.awt.font;
42
43import java.awt.geom.AffineTransform;
44import java.io.Serializable;
45import java.io.ObjectStreamException;
46
47/**
48 * The <code>TransformAttribute</code> class provides an immutable
49 * wrapper for a transform so that it is safe to use as an attribute.
50 */
51public final class TransformAttribute implements Serializable {
52
53 /**
54 * The <code>AffineTransform</code> for this
55 * <code>TransformAttribute</code>, or <code>null</code>
56 * if <code>AffineTransform</code> is the identity transform.
57 */
58 private AffineTransform transform;
59
60 /**
61 * Wraps the specified transform. The transform is cloned and a
62 * reference to the clone is kept. The original transform is unchanged.
63 * If null is passed as the argument, this constructor behaves as though
64 * it were the identity transform. (Note that it is preferable to use
65 * {@link #IDENTITY} in this case.)
66 * @param transform the specified {@link AffineTransform} to be wrapped,
67 * or null.
68 */
69 public TransformAttribute(AffineTransform transform) {
70 if (transform != null && !transform.isIdentity()) {
71 this.transform = new AffineTransform(transform);
72 }
73 }
74
75 /**
76 * Returns a copy of the wrapped transform.
77 * @return a <code>AffineTransform</code> that is a copy of the wrapped
78 * transform of this <code>TransformAttribute</code>.
79 */
80 public AffineTransform getTransform() {
81 AffineTransform at = transform;
82 return (at == null) ? new AffineTransform() : new AffineTransform(at);
83 }
84
85 /**
86 * Returns <code>true</code> if the wrapped transform is
87 * an identity transform.
88 * @return <code>true</code> if the wrapped transform is
89 * an identity transform; <code>false</code> otherwise.
90 * @since 1.4
91 */
92 public boolean isIdentity() {
93 return transform == null;
94 }
95
96 /**
97 * A <code>TransformAttribute</code> representing the identity transform.
98 * @since 1.6
99 */
100 public static final TransformAttribute IDENTITY = new TransformAttribute(null);
101
102 private void writeObject(java.io.ObjectOutputStream s)
103 throws java.lang.ClassNotFoundException,
104 java.io.IOException
105 {
106 // sigh -- 1.3 expects transform is never null, so we need to always write one out
107 if (this.transform == null) {
108 this.transform = new AffineTransform();
109 }
110 s.defaultWriteObject();
111 }
112
113 /*
114 * @since 1.6
115 */
116 private Object readResolve() throws ObjectStreamException {
117 if (transform == null || transform.isIdentity()) {
118 return IDENTITY;
119 }
120 return this;
121 }
122
123 // Added for serial backwards compatability (4348425)
124 static final long serialVersionUID = 3356247357827709530L;
125
126 /**
127 * @since 1.6
128 */
129 public int hashCode() {
130 return transform == null ? 0 : transform.hashCode();
131 }
132
133 /**
134 * Returns <code>true</code> if rhs is a <code>TransformAttribute</code>
135 * whose transform is equal to this <code>TransformAttribute</code>'s
136 * transform.
137 * @param rhs the object to compare to
138 * @return <code>true</code> if the argument is a <code>TransformAttribute</code>
139 * whose transform is equal to this <code>TransformAttribute</code>'s
140 * transform.
141 * @since 1.6
142 */
143 public boolean equals(Object rhs) {
144 try {
145 TransformAttribute that = (TransformAttribute)rhs;
146 if (transform == null) {
147 return that.transform == null;
148 }
149 return transform.equals(that.transform);
150 }
151 catch (ClassCastException e) {
152 }
153 return false;
154 }
155}
java/pkg/java/awt/font/mkfile+29−0
@@ -0,0 +1,29 @@
1<../../../../../mkconfig
2
3TARG=\
4 CharArrayIterator.dis\
5 FontRenderContext.dis\
6 GlyphJustificationInfo.dis\
7 GlyphMetrics.dis\
8 GlyphVector.dis\
9 GraphicAttribute.dis\
10 ImageGraphicAttribute.dis\
11 LayoutPath.dis\
12 LineBreakMeasurer.dis\
13 LineMetrics.dis\
14 MultipleMaster.dis\
15 NumericShaper.dis\
16 OpenType.dis\
17 ShapeGraphicAttribute.dis\
18 StyledParagraph.dis\
19 TextAttribute.dis\
20 TextHitInfo.dis\
21 TextJustifier.dis\
22 TextLayout.dis\
23 TextLine.dis\
24 TextMeasurer.dis\
25 TransformAttribute.dis\
26
27DISBIN=$ROOT/java/dis/java/java/java/awt/font
28
29<$ROOT/mkfiles/mkjava
java/pkg/java/awt/font/package.html+52−0
@@ -0,0 +1,52 @@
1<!--
2 Copyright (c) 1998, 1999, Oracle and/or its affiliates. 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. Oracle designates this
8 particular file as subject to the "Classpath" exception as provided
9 by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 or visit www.oracle.com if you need additional information or have any
23 questions.
24-->
25
26<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
27<html>
28<body bgcolor="white">
29
30Provides classes and interface relating to fonts. It
31contains support for representing Type 1, Type 1 Multiple Master
32fonts, OpenType fonts, and TrueType fonts.
33
34<!--
35<h2>Package Specification</h2>
36
37##### FILL IN ANY SPECS NEEDED BY JAVA COMPATIBILITY KIT #####
38<ul>
39 <li><a href="">##### REFER TO ANY FRAMEMAKER SPECIFICATION HERE #####</a>
40</ul>
41
42<h2>Related Documentation</h2>
43
44For overviews, tutorials, examples, guides, and tool documentation, please see:
45<ul>
46 <li><a href="">##### REFER TO NON-SPEC DOCUMENTATION HERE #####</a>
47</ul>
48-->
49
50@since 1.2
51</body>
52</html>