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+
26+package java.awt.font;
27+
28+import java.text.CharacterIterator;
29+
30+class 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+
30+package java.awt.font;
31+
32+import java.awt.RenderingHints;
33+import static java.awt.RenderingHints.*;
34+import 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+
67+public 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+
40+package 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+
77+public 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+
41+package java.awt.font;
42+
43+import 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+
109+public 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+
30+package java.awt.font;
31+
32+import java.awt.Graphics2D;
33+import java.awt.Font;
34+import java.awt.Polygon; // remind - need a floating point version
35+import java.awt.Rectangle;
36+import java.awt.geom.Point2D;
37+import java.awt.geom.Rectangle2D;
38+import java.awt.geom.AffineTransform;
39+import java.awt.Shape;
40+import java.awt.font.GlyphMetrics;
41+import 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+
109+public 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+
41+package java.awt.font;
42+
43+import java.awt.Graphics2D;
44+import java.awt.Font;
45+import java.awt.Shape;
46+import java.awt.geom.AffineTransform;
47+import 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+ */
64+public 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+
41+package java.awt.font;
42+
43+import java.awt.Image;
44+import java.awt.Graphics2D;
45+import 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+
54+public 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+ */
28+package java.awt.font;
29+
30+import 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+ */
43+public 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+
41+package java.awt.font;
42+
43+import java.text.BreakIterator;
44+import java.text.CharacterIterator;
45+import java.text.AttributedCharacterIterator;
46+import 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+
248+public 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+
26+package 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+
44+public 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+ */
25+package java.awt.font;
26+
27+import 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+ */
34+public 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+
26+package java.awt.font;
27+
28+import java.io.IOException;
29+import java.io.ObjectOutputStream;
30+import java.util.Arrays;
31+import java.util.Comparator;
32+import java.util.EnumSet;
33+import 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+
136+public 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+
26+package 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+ */
38+public 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+
41+package java.awt.font;
42+
43+import java.awt.Shape;
44+import java.awt.Graphics;
45+import java.awt.Rectangle;
46+import java.awt.Graphics2D;
47+import java.awt.Shape;
48+import java.awt.geom.AffineTransform;
49+import 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+ */
56+public 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+ */
30+package java.awt.font;
31+
32+import java.awt.Font;
33+import java.awt.Toolkit;
34+import java.awt.im.InputMethodHighlight;
35+import java.text.Annotation;
36+import java.text.AttributedCharacterIterator;
37+import java.util.Vector;
38+import java.util.HashMap;
39+import java.util.Map;
40+import sun.font.Decoration;
41+import sun.font.FontResolver;
42+import 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+ */
51+final 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+
41+package java.awt.font;
42+
43+import java.io.InvalidObjectException;
44+import java.text.AttributedCharacterIterator.Attribute;
45+import java.util.Map;
46+import 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+ */
257+public 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+
41+package java.awt.font;
42+import 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+
88+public 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+
41+package 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+
50+class 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+
41+package java.awt.font;
42+
43+import java.awt.Color;
44+import java.awt.Font;
45+import java.awt.Graphics2D;
46+import java.awt.Rectangle;
47+import java.awt.Shape;
48+import java.awt.font.NumericShaper;
49+import java.awt.font.TextLine.TextLineMetrics;
50+import java.awt.geom.AffineTransform;
51+import java.awt.geom.GeneralPath;
52+import java.awt.geom.NoninvertibleTransformException;
53+import java.awt.geom.Point2D;
54+import java.awt.geom.Rectangle2D;
55+import java.text.AttributedString;
56+import java.text.AttributedCharacterIterator;
57+import java.text.AttributedCharacterIterator.Attribute;
58+import java.util.Map;
59+import java.util.HashMap;
60+import java.util.Hashtable;
61+import sun.font.AttributeValues;
62+import sun.font.CoreMetrics;
63+import sun.font.Decoration;
64+import sun.font.FontLineMetrics;
65+import sun.font.FontResolver;
66+import sun.font.GraphicComponent;
67+import sun.font.LayoutPathImpl;
68+import 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+ */
238+public 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+
31+package java.awt.font;
32+
33+import java.awt.Color;
34+import java.awt.Font;
35+import java.awt.Graphics2D;
36+import java.awt.Rectangle;
37+import java.awt.Shape;
38+import java.awt.geom.AffineTransform;
39+import java.awt.geom.GeneralPath;
40+import java.awt.geom.Point2D;
41+import java.awt.geom.Rectangle2D;
42+import java.awt.im.InputMethodHighlight;
43+import java.awt.image.BufferedImage;
44+import java.text.Annotation;
45+import java.text.AttributedCharacterIterator;
46+import java.text.Bidi;
47+import java.text.CharacterIterator;
48+import java.util.Hashtable;
49+import java.util.Map;
50+import sun.font.AttributeValues;
51+import sun.font.BidiUtils;
52+import sun.font.CoreMetrics;
53+import sun.font.Decoration;
54+import sun.font.FontLineMetrics;
55+import sun.font.FontResolver;
56+import sun.font.GraphicComponent;
57+import sun.font.LayoutPathImpl;
58+import sun.font.LayoutPathImpl.EmptyPath;
59+import sun.font.LayoutPathImpl.SegmentPathBuilder;
60+import sun.font.TextLabelFactory;
61+import sun.font.TextLineComponent;
62+import sun.text.CodePointIterator;
63+
64+import java.awt.geom.Line2D;
65+
66+final 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+
41+package java.awt.font;
42+
43+import java.awt.Font;
44+
45+import java.text.AttributedCharacterIterator;
46+import java.text.AttributedString;
47+import java.text.Bidi;
48+import java.text.BreakIterator;
49+import java.text.CharacterIterator;
50+
51+import java.awt.font.FontRenderContext;
52+
53+import java.util.Hashtable;
54+import java.util.Map;
55+
56+import sun.font.AttributeValues;
57+import sun.font.BidiUtils;
58+import sun.font.TextLineComponent;
59+import sun.font.TextLabelFactory;
60+import 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+
97+public 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+
41+package java.awt.font;
42+
43+import java.awt.geom.AffineTransform;
44+import java.io.Serializable;
45+import 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+ */
51+public 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+
3+TARG=\
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+
27+DISBIN=$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+
30+Provides classes and interface relating to fonts. It
31+contains support for representing Type 1, Type 1 Multiple Master
32+fonts, 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+
44+For 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>