blob: bc1b74e951abe510cd854f1888113980b402d0fa [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Portions Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26/*
27 *
28 * (C) Copyright IBM Corp. 2005 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is
31 * copyrighted and owned by IBM. These materials are provided
32 * under terms of a License Agreement between IBM and Sun.
33 * This technology is protected by multiple US and International
34 * patents. This notice and attribution to IBM may not be removed.
35 */
36
37package sun.font;
38
39import static sun.font.EAttribute.*;
40import static java.lang.Math.*;
41
42import java.awt.Font;
43import java.awt.Paint;
44import java.awt.Toolkit;
45import java.awt.font.GraphicAttribute;
46import java.awt.font.NumericShaper;
47import java.awt.font.TextAttribute;
48import java.awt.font.TransformAttribute;
49import java.awt.geom.AffineTransform;
50import java.awt.geom.NoninvertibleTransformException;
51import java.awt.geom.Point2D;
52import java.awt.im.InputMethodHighlight;
53import java.io.Serializable;
54import java.text.Annotation;
55import java.text.AttributedCharacterIterator.Attribute;
56import java.util.Map;
57import java.util.HashMap;
58import java.util.Hashtable;
59
60public final class AttributeValues implements Cloneable {
61 private int defined;
62 private int nondefault;
63
64 private String family = "Default";
65 private float weight = 1f;
66 private float width = 1f;
67 private float posture; // 0f
68 private float size = 12f;
69 private float tracking; // 0f
70 private NumericShaper numericShaping; // null
71 private AffineTransform transform; // null == identity
72 private GraphicAttribute charReplacement; // null
73 private Paint foreground; // null
74 private Paint background; // null
75 private float justification = 1f;
76 private Object imHighlight; // null
77 // (can be either Attribute wrapping IMH, or IMH itself
78 private Font font; // here for completeness, don't actually use
79 private byte imUnderline = -1; // same default as underline
80 private byte superscript; // 0
81 private byte underline = -1; // arrgh, value for ON is 0
82 private byte runDirection = -2; // BIDI.DIRECTION_DEFAULT_LEFT_TO_RIGHT
83 private byte bidiEmbedding; // 0
84 private byte kerning; // 0
85 private byte ligatures; // 0
86 private boolean strikethrough; // false
87 private boolean swapColors; // false
88
89 private AffineTransform baselineTransform; // derived from transform
90 private AffineTransform charTransform; // derived from transform
91
92 private static final AttributeValues DEFAULT = new AttributeValues();
93
94 // type-specific API
95 public String getFamily() { return family; }
96 public void setFamily(String f) { this.family = f; update(EFAMILY); }
97
98 public float getWeight() { return weight; }
99 public void setWeight(float f) { this.weight = f; update(EWEIGHT); }
100
101 public float getWidth() { return width; }
102 public void setWidth(float f) { this.width = f; update(EWIDTH); }
103
104 public float getPosture() { return posture; }
105 public void setPosture(float f) { this.posture = f; update(EPOSTURE); }
106
107 public float getSize() { return size; }
108 public void setSize(float f) { this.size = f; update(ESIZE); }
109
110 public AffineTransform getTransform() { return transform; }
111 public void setTransform(AffineTransform f) {
112 this.transform = (f == null || f.isIdentity())
113 ? DEFAULT.transform
114 : new AffineTransform(f);
115 updateDerivedTransforms();
116 update(ETRANSFORM);
117 }
118 public void setTransform(TransformAttribute f) {
119 this.transform = (f == null || f.isIdentity())
120 ? DEFAULT.transform
121 : f.getTransform();
122 updateDerivedTransforms();
123 update(ETRANSFORM);
124 }
125
126 public int getSuperscript() { return superscript; }
127 public void setSuperscript(int f) {
128 this.superscript = (byte)f; update(ESUPERSCRIPT); }
129
130 public Font getFont() { return font; }
131 public void setFont(Font f) { this.font = f; update(EFONT); }
132
133 public GraphicAttribute getCharReplacement() { return charReplacement; }
134 public void setCharReplacement(GraphicAttribute f) {
135 this.charReplacement = f; update(ECHAR_REPLACEMENT); }
136
137 public Paint getForeground() { return foreground; }
138 public void setForeground(Paint f) {
139 this.foreground = f; update(EFOREGROUND); }
140
141 public Paint getBackground() { return background; }
142 public void setBackground(Paint f) {
143 this.background = f; update(EBACKGROUND); }
144
145 public int getUnderline() { return underline; }
146 public void setUnderline(int f) {
147 this.underline = (byte)f; update(EUNDERLINE); }
148
149 public boolean getStrikethrough() { return strikethrough; }
150 public void setStrikethrough(boolean f) {
151 this.strikethrough = f; update(ESTRIKETHROUGH); }
152
153 public int getRunDirection() { return runDirection; }
154 public void setRunDirection(int f) {
155 this.runDirection = (byte)f; update(ERUN_DIRECTION); }
156
157 public int getBidiEmbedding() { return bidiEmbedding; }
158 public void setBidiEmbedding(int f) {
159 this.bidiEmbedding = (byte)f; update(EBIDI_EMBEDDING); }
160
161 public float getJustification() { return justification; }
162 public void setJustification(float f) {
163 this.justification = f; update(EJUSTIFICATION); }
164
165 public Object getInputMethodHighlight() { return imHighlight; }
166 public void setInputMethodHighlight(Annotation f) {
167 this.imHighlight = f; update(EINPUT_METHOD_HIGHLIGHT); }
168 public void setInputMethodHighlight(InputMethodHighlight f) {
169 this.imHighlight = f; update(EINPUT_METHOD_HIGHLIGHT); }
170
171 public int getInputMethodUnderline() { return imUnderline; }
172 public void setInputMethodUnderline(int f) {
173 this.imUnderline = (byte)f; update(EINPUT_METHOD_UNDERLINE); }
174
175 public boolean getSwapColors() { return swapColors; }
176 public void setSwapColors(boolean f) {
177 this.swapColors = f; update(ESWAP_COLORS); }
178
179 public NumericShaper getNumericShaping() { return numericShaping; }
180 public void setNumericShaping(NumericShaper f) {
181 this.numericShaping = f; update(ENUMERIC_SHAPING); }
182
183 public int getKerning() { return kerning; }
184 public void setKerning(int f) {
185 this.kerning = (byte)f; update(EKERNING); }
186
187 public float getTracking() { return tracking; }
188 public void setTracking(float f) {
189 this.tracking = (byte)f; update(ETRACKING); }
190
191 public int getLigatures() { return ligatures; }
192 public void setLigatures(int f) {
193 this.ligatures = (byte)f; update(ELIGATURES); }
194
195
196 public AffineTransform getBaselineTransform() { return baselineTransform; }
197 public AffineTransform getCharTransform() { return charTransform; }
198
199 // mask api
200
201 public static int getMask(EAttribute att) {
202 return att.mask;
203 }
204
205 public static int getMask(EAttribute ... atts) {
206 int mask = 0;
207 for (EAttribute a: atts) {
208 mask |= a.mask;
209 }
210 return mask;
211 }
212
213 public static final int MASK_ALL =
214 getMask(EAttribute.class.getEnumConstants());
215
216 public void unsetDefault() {
217 defined &= nondefault;
218 }
219
220 public void defineAll(int mask) {
221 defined |= mask;
222 if ((defined & EBASELINE_TRANSFORM.mask) != 0) {
223 throw new InternalError("can't define derived attribute");
224 }
225 }
226
227 public boolean allDefined(int mask) {
228 return (defined & mask) == mask;
229 }
230
231 public boolean anyDefined(int mask) {
232 return (defined & mask) != 0;
233 }
234
235 public boolean anyNonDefault(int mask) {
236 return (nondefault & mask) != 0;
237 }
238
239 // generic EAttribute API
240
241 public boolean isDefined(EAttribute a) {
242 return (defined & a.mask) != 0;
243 }
244
245 public boolean isNonDefault(EAttribute a) {
246 return (nondefault & a.mask) != 0;
247 }
248
249 public void setDefault(EAttribute a) {
250 if (a.att == null) {
251 throw new InternalError("can't set default derived attribute: " + a);
252 }
253 i_set(a, DEFAULT);
254 defined |= a.mask;
255 nondefault &= ~a.mask;
256 }
257
258 public void unset(EAttribute a) {
259 if (a.att == null) {
260 throw new InternalError("can't unset derived attribute: " + a);
261 }
262 i_set(a, DEFAULT);
263 defined &= ~a.mask;
264 nondefault &= ~a.mask;
265 }
266
267 public void set(EAttribute a, AttributeValues src) {
268 if (a.att == null) {
269 throw new InternalError("can't set derived attribute: " + a);
270 }
271 if (src == null || src == DEFAULT) {
272 setDefault(a);
273 } else {
274 if ((src.defined & a.mask) != 0) {
275 i_set(a, src);
276 update(a);
277 }
278 }
279 }
280
281 public void set(EAttribute a, Object o) {
282 if (a.att == null) {
283 throw new InternalError("can't set derived attribute: " + a);
284 }
285 if (o != null) {
286 try {
287 i_set(a, o);
288 update(a);
289 return;
290 } catch (Exception e) {
291 }
292 }
293 setDefault(a);
294 }
295
296 public Object get(EAttribute a) {
297 if (a.att == null) {
298 throw new InternalError("can't get derived attribute: " + a);
299 }
300 if ((nondefault & a.mask) != 0) {
301 return i_get(a);
302 }
303 return null;
304 }
305
306 // merging
307
308 public AttributeValues merge(Map<? extends Attribute, ?>map) {
309 return merge(map, MASK_ALL);
310 }
311
312 public AttributeValues merge(Map<? extends Attribute, ?>map,
313 int mask) {
314 if (map instanceof AttributeMap &&
315 ((AttributeMap) map).getValues() != null) {
316 merge(((AttributeMap)map).getValues(), mask);
317 } else if (map != null && !map.isEmpty()) {
318 for (Map.Entry<? extends Attribute, ?> e: map.entrySet()) {
319 try {
320 EAttribute ea = EAttribute.forAttribute(e.getKey());
321 if (ea!= null && (mask & ea.mask) != 0) {
322 set(ea, e.getValue());
323 }
324 } catch (ClassCastException cce) {
325 // IGNORED
326 }
327 }
328 }
329 return this;
330 }
331
332 public AttributeValues merge(AttributeValues src) {
333 return merge(src, MASK_ALL);
334 }
335
336 public AttributeValues merge(AttributeValues src, int mask) {
337 int m = mask & src.defined;
338 for (EAttribute ea: EAttribute.atts) {
339 if (m == 0) {
340 break;
341 }
342 if ((m & ea.mask) != 0) {
343 m &= ~ea.mask;
344 i_set(ea, src);
345 update(ea);
346 }
347 }
348 return this;
349 }
350
351 // creation API
352
353 public static AttributeValues fromMap(Map<? extends Attribute, ?> map) {
354 return fromMap(map, MASK_ALL);
355 }
356
357 public static AttributeValues fromMap(Map<? extends Attribute, ?> map,
358 int mask) {
359 return new AttributeValues().merge(map, mask);
360 }
361
362 public Map<TextAttribute, Object> toMap(Map<TextAttribute, Object> fill) {
363 if (fill == null) {
364 fill = new HashMap<TextAttribute, Object>();
365 }
366
367 for (int m = defined, i = 0; m != 0; ++i) {
368 EAttribute ea = EAttribute.atts[i];
369 if ((m & ea.mask) != 0) {
370 m &= ~ea.mask;
371 fill.put(ea.att, get(ea));
372 }
373 }
374
375 return fill;
376 }
377
378 // key must be serializable, so use String, not Object
379 private static final String DEFINED_KEY =
380 "sun.font.attributevalues.defined_key";
381
382 public static boolean is16Hashtable(Hashtable<Object, Object> ht) {
383 return ht.containsKey(DEFINED_KEY);
384 }
385
386 public static AttributeValues
387 fromSerializableHashtable(Hashtable<Object, Object> ht)
388 {
389 AttributeValues result = new AttributeValues();
390 if (ht != null && !ht.isEmpty()) {
391 for (Map.Entry<Object, Object> e: ht.entrySet()) {
392 Object key = e.getKey();
393 Object val = e.getValue();
394 if (key.equals(DEFINED_KEY)) {
395 result.defineAll(((Integer)val).intValue());
396 } else {
397 try {
398 EAttribute ea =
399 EAttribute.forAttribute((Attribute)key);
400 if (ea != null) {
401 result.set(ea, val);
402 }
403 }
404 catch (ClassCastException ex) {
405 }
406 }
407 }
408 }
409 return result;
410 }
411
412 public Hashtable<Object, Object> toSerializableHashtable() {
413 Hashtable ht = new Hashtable();
414 int hashkey = defined;
415 for (int m = defined, i = 0; m != 0; ++i) {
416 EAttribute ea = EAttribute.atts[i];
417 if ((m & ea.mask) != 0) {
418 m &= ~ea.mask;
419 Object o = get(ea);
420 if (o == null) {
421 // hashkey will handle it
422 } else if (o instanceof Serializable) { // check all...
423 ht.put(ea.att, o);
424 } else {
425 hashkey &= ~ea.mask;
426 }
427 }
428 }
429 ht.put(DEFINED_KEY, Integer.valueOf(hashkey));
430
431 return ht;
432 }
433
434 // boilerplate
435 public int hashCode() {
436 return defined << 8 ^ nondefault;
437 }
438
439 public boolean equals(Object rhs) {
440 try {
441 return equals((AttributeValues)rhs);
442 }
443 catch (ClassCastException e) {
444 }
445 return false;
446 }
447
448 public boolean equals(AttributeValues rhs) {
449 // test in order of most likely to differ and easiest to compare
450 // also assumes we're generally calling this only if family,
451 // size, weight, posture are the same
452
453 if (rhs == null) return false;
454 if (rhs == this) return true;
455
456 return defined == rhs.defined
457 && nondefault == rhs.nondefault
458 && underline == rhs.underline
459 && strikethrough == rhs.strikethrough
460 && superscript == rhs.superscript
461 && width == rhs.width
462 && kerning == rhs.kerning
463 && tracking == rhs.tracking
464 && ligatures == rhs.ligatures
465 && runDirection == rhs.runDirection
466 && bidiEmbedding == rhs.bidiEmbedding
467 && swapColors == rhs.swapColors
468 && equals(transform, rhs.transform)
469 && equals(foreground, rhs.foreground)
470 && equals(background, rhs.background)
471 && equals(numericShaping, rhs.numericShaping)
472 && equals(justification, rhs.justification)
473 && equals(charReplacement, rhs.charReplacement)
474 && size == rhs.size
475 && weight == rhs.weight
476 && posture == rhs.posture
477 && equals(family, rhs.family)
478 && equals(font, rhs.font)
479 && imUnderline == rhs.imUnderline
480 && equals(imHighlight, rhs.imHighlight);
481 }
482
483 public AttributeValues clone() {
484 try {
485 AttributeValues result = (AttributeValues)super.clone();
486 if (transform != null) { // AffineTransform is mutable
487 result.transform = new AffineTransform(transform);
488 result.updateDerivedTransforms();
489 }
490 // if transform is null, derived transforms are null
491 // so there's nothing to do
492 return result;
493 }
494 catch (CloneNotSupportedException e) {
495 // never happens
496 return null;
497 }
498 }
499
500 public String toString() {
501 StringBuilder b = new StringBuilder();
502 b.append('{');
503 for (int m = defined, i = 0; m != 0; ++i) {
504 EAttribute ea = EAttribute.atts[i];
505 if ((m & ea.mask) != 0) {
506 m &= ~ea.mask;
507 if (b.length() > 1) {
508 b.append(", ");
509 }
510 b.append(ea);
511 b.append('=');
512 switch (ea) {
513 case EFAMILY: b.append('"');
514 b.append(family);
515 b.append('"'); break;
516 case EWEIGHT: b.append(weight); break;
517 case EWIDTH: b.append(width); break;
518 case EPOSTURE: b.append(posture); break;
519 case ESIZE: b.append(size); break;
520 case ETRANSFORM: b.append(transform); break;
521 case ESUPERSCRIPT: b.append(superscript); break;
522 case EFONT: b.append(font); break;
523 case ECHAR_REPLACEMENT: b.append(charReplacement); break;
524 case EFOREGROUND: b.append(foreground); break;
525 case EBACKGROUND: b.append(background); break;
526 case EUNDERLINE: b.append(underline); break;
527 case ESTRIKETHROUGH: b.append(strikethrough); break;
528 case ERUN_DIRECTION: b.append(runDirection); break;
529 case EBIDI_EMBEDDING: b.append(bidiEmbedding); break;
530 case EJUSTIFICATION: b.append(justification); break;
531 case EINPUT_METHOD_HIGHLIGHT: b.append(imHighlight); break;
532 case EINPUT_METHOD_UNDERLINE: b.append(imUnderline); break;
533 case ESWAP_COLORS: b.append(swapColors); break;
534 case ENUMERIC_SHAPING: b.append(numericShaping); break;
535 case EKERNING: b.append(kerning); break;
536 case ELIGATURES: b.append(ligatures); break;
537 case ETRACKING: b.append(tracking); break;
538 default: throw new InternalError();
539 }
540 if ((nondefault & ea.mask) == 0) {
541 b.append('*');
542 }
543 }
544 }
545 b.append("[btx=" + baselineTransform + ", ctx=" + charTransform + "]");
546 b.append('}');
547 return b.toString();
548 }
549
550 // internal utilities
551
552 private static boolean equals(Object lhs, Object rhs) {
553 return lhs == null ? rhs == null : lhs.equals(rhs);
554 }
555
556 private void update(EAttribute a) {
557 defined |= a.mask;
558 if (i_validate(a)) {
559 if (i_equals(a, DEFAULT)) {
560 nondefault &= ~a.mask;
561 } else {
562 nondefault |= a.mask;
563 }
564 } else {
565 setDefault(a);
566 }
567 }
568
569 // dispatch
570
571 private void i_set(EAttribute a, AttributeValues src) {
572 switch (a) {
573 case EFAMILY: family = src.family; break;
574 case EWEIGHT: weight = src.weight; break;
575 case EWIDTH: width = src.width; break;
576 case EPOSTURE: posture = src.posture; break;
577 case ESIZE: size = src.size; break;
578 case ETRANSFORM: transform = src.transform; updateDerivedTransforms(); break;
579 case ESUPERSCRIPT: superscript = src.superscript; break;
580 case EFONT: font = src.font; break;
581 case ECHAR_REPLACEMENT: charReplacement = src.charReplacement; break;
582 case EFOREGROUND: foreground = src.foreground; break;
583 case EBACKGROUND: background = src.background; break;
584 case EUNDERLINE: underline = src.underline; break;
585 case ESTRIKETHROUGH: strikethrough = src.strikethrough; break;
586 case ERUN_DIRECTION: runDirection = src.runDirection; break;
587 case EBIDI_EMBEDDING: bidiEmbedding = src.bidiEmbedding; break;
588 case EJUSTIFICATION: justification = src.justification; break;
589 case EINPUT_METHOD_HIGHLIGHT: imHighlight = src.imHighlight; break;
590 case EINPUT_METHOD_UNDERLINE: imUnderline = src.imUnderline; break;
591 case ESWAP_COLORS: swapColors = src.swapColors; break;
592 case ENUMERIC_SHAPING: numericShaping = src.numericShaping; break;
593 case EKERNING: kerning = src.kerning; break;
594 case ELIGATURES: ligatures = src.ligatures; break;
595 case ETRACKING: tracking = src.tracking; break;
596 default: throw new InternalError();
597 }
598 }
599
600 private boolean i_equals(EAttribute a, AttributeValues src) {
601 switch (a) {
602 case EFAMILY: return equals(family, src.family);
603 case EWEIGHT: return weight == src.weight;
604 case EWIDTH: return width == src.width;
605 case EPOSTURE: return posture == src.posture;
606 case ESIZE: return size == src.size;
607 case ETRANSFORM: return equals(transform, src.transform);
608 case ESUPERSCRIPT: return superscript == src.superscript;
609 case EFONT: return equals(font, src.font);
610 case ECHAR_REPLACEMENT: return equals(charReplacement, src.charReplacement);
611 case EFOREGROUND: return equals(foreground, src.foreground);
612 case EBACKGROUND: return equals(background, src.background);
613 case EUNDERLINE: return underline == src.underline;
614 case ESTRIKETHROUGH: return strikethrough == src.strikethrough;
615 case ERUN_DIRECTION: return runDirection == src.runDirection;
616 case EBIDI_EMBEDDING: return bidiEmbedding == src.bidiEmbedding;
617 case EJUSTIFICATION: return justification == src.justification;
618 case EINPUT_METHOD_HIGHLIGHT: return equals(imHighlight, src.imHighlight);
619 case EINPUT_METHOD_UNDERLINE: return imUnderline == src.imUnderline;
620 case ESWAP_COLORS: return swapColors == src.swapColors;
621 case ENUMERIC_SHAPING: return equals(numericShaping, src.numericShaping);
622 case EKERNING: return kerning == src.kerning;
623 case ELIGATURES: return ligatures == src.ligatures;
624 case ETRACKING: return tracking == src.tracking;
625 default: throw new InternalError();
626 }
627 }
628
629 private void i_set(EAttribute a, Object o) {
630 switch (a) {
631 case EFAMILY: family = ((String)o).trim(); break;
632 case EWEIGHT: weight = ((Number)o).floatValue(); break;
633 case EWIDTH: width = ((Number)o).floatValue(); break;
634 case EPOSTURE: posture = ((Number)o).floatValue(); break;
635 case ESIZE: size = ((Number)o).floatValue(); break;
636 case ETRANSFORM: {
637 if (o instanceof TransformAttribute) {
638 TransformAttribute ta = (TransformAttribute)o;
639 if (ta.isIdentity()) {
640 transform = null;
641 } else {
642 transform = ta.getTransform();
643 }
644 } else {
645 transform = new AffineTransform((AffineTransform)o);
646 }
647 updateDerivedTransforms();
648 } break;
649 case ESUPERSCRIPT: superscript = (byte)((Integer)o).intValue(); break;
650 case EFONT: font = (Font)o; break;
651 case ECHAR_REPLACEMENT: charReplacement = (GraphicAttribute)o; break;
652 case EFOREGROUND: foreground = (Paint)o; break;
653 case EBACKGROUND: background = (Paint)o; break;
654 case EUNDERLINE: underline = (byte)((Integer)o).intValue(); break;
655 case ESTRIKETHROUGH: strikethrough = ((Boolean)o).booleanValue(); break;
656 case ERUN_DIRECTION: {
657 if (o instanceof Boolean) {
658 runDirection = (byte)(TextAttribute.RUN_DIRECTION_LTR.equals(o) ? 0 : 1);
659 } else {
660 runDirection = (byte)((Integer)o).intValue();
661 }
662 } break;
663 case EBIDI_EMBEDDING: bidiEmbedding = (byte)((Integer)o).intValue(); break;
664 case EJUSTIFICATION: justification = ((Number)o).floatValue(); break;
665 case EINPUT_METHOD_HIGHLIGHT: {
666 if (o instanceof Annotation) {
667 Annotation at = (Annotation)o;
668 imHighlight = (InputMethodHighlight)at.getValue();
669 } else {
670 imHighlight = (InputMethodHighlight)o;
671 }
672 } break;
673 case EINPUT_METHOD_UNDERLINE: imUnderline = (byte)((Integer)o).intValue();
674 break;
675 case ESWAP_COLORS: swapColors = ((Boolean)o).booleanValue(); break;
676 case ENUMERIC_SHAPING: numericShaping = (NumericShaper)o; break;
677 case EKERNING: kerning = (byte)((Integer)o).intValue(); break;
678 case ELIGATURES: ligatures = (byte)((Integer)o).intValue(); break;
679 case ETRACKING: tracking = ((Number)o).floatValue(); break;
680 default: throw new InternalError();
681 }
682 }
683
684 private Object i_get(EAttribute a) {
685 switch (a) {
686 case EFAMILY: return family;
687 case EWEIGHT: return Float.valueOf(weight);
688 case EWIDTH: return Float.valueOf(width);
689 case EPOSTURE: return Float.valueOf(posture);
690 case ESIZE: return Float.valueOf(size);
691 case ETRANSFORM:
692 return transform == null
693 ? TransformAttribute.IDENTITY
694 : new TransformAttribute(transform);
695 case ESUPERSCRIPT: return Integer.valueOf(superscript);
696 case EFONT: return font;
697 case ECHAR_REPLACEMENT: return charReplacement;
698 case EFOREGROUND: return foreground;
699 case EBACKGROUND: return background;
700 case EUNDERLINE: return Integer.valueOf(underline);
701 case ESTRIKETHROUGH: return Boolean.valueOf(strikethrough);
702 case ERUN_DIRECTION: {
703 switch (runDirection) {
704 // todo: figure out a way to indicate this value
705 // case -1: return Integer.valueOf(runDirection);
706 case 0: return TextAttribute.RUN_DIRECTION_LTR;
707 case 1: return TextAttribute.RUN_DIRECTION_RTL;
708 default: return null;
709 }
710 } // not reachable
711 case EBIDI_EMBEDDING: return Integer.valueOf(bidiEmbedding);
712 case EJUSTIFICATION: return Float.valueOf(justification);
713 case EINPUT_METHOD_HIGHLIGHT: return imHighlight;
714 case EINPUT_METHOD_UNDERLINE: return Integer.valueOf(imUnderline);
715 case ESWAP_COLORS: return Boolean.valueOf(swapColors);
716 case ENUMERIC_SHAPING: return numericShaping;
717 case EKERNING: return Integer.valueOf(kerning);
718 case ELIGATURES: return Integer.valueOf(ligatures);
719 case ETRACKING: return Float.valueOf(tracking);
720 default: throw new InternalError();
721 }
722 }
723
724 private boolean i_validate(EAttribute a) {
725 switch (a) {
726 case EFAMILY: if (family == null || family.length() == 0)
727 family = DEFAULT.family; return true;
728 case EWEIGHT: return weight > 0 && weight < 10;
729 case EWIDTH: return width >= .5f && width < 10;
730 case EPOSTURE: return posture >= -1 && posture <= 1;
731 case ESIZE: return size >= 0;
732 case ETRANSFORM: if (transform != null && transform.isIdentity())
733 transform = DEFAULT.transform; return true;
734 case ESUPERSCRIPT: return superscript >= -7 && superscript <= 7;
735 case EFONT: return true;
736 case ECHAR_REPLACEMENT: return true;
737 case EFOREGROUND: return true;
738 case EBACKGROUND: return true;
739 case EUNDERLINE: return underline >= -1 && underline < 6;
740 case ESTRIKETHROUGH: return true;
741 case ERUN_DIRECTION: return runDirection >= -2 && runDirection <= 1;
742 case EBIDI_EMBEDDING: return bidiEmbedding >= -61 && bidiEmbedding < 62;
743 case EJUSTIFICATION: justification = max(0, min (justification, 1));
744 return true;
745 case EINPUT_METHOD_HIGHLIGHT: return true;
746 case EINPUT_METHOD_UNDERLINE: return imUnderline >= -1 && imUnderline < 6;
747 case ESWAP_COLORS: return true;
748 case ENUMERIC_SHAPING: return true;
749 case EKERNING: return kerning >= 0 && kerning <= 1;
750 case ELIGATURES: return ligatures >= 0 && ligatures <= 1;
751 case ETRACKING: return tracking >= -1 && tracking <= 10;
752 default: throw new InternalError("unknown attribute: " + a);
753 }
754 }
755
756 // Until textlayout is fixed to use AttributeValues, we'll end up
757 // creating a map from the values for it. This is a compromise between
758 // creating the whole map and just checking a particular value.
759 // Plan to remove these.
760 public static float getJustification(Map<?, ?> map) {
761 if (map != null) {
762 if (map instanceof AttributeMap &&
763 ((AttributeMap) map).getValues() != null) {
764 return ((AttributeMap)map).getValues().justification;
765 }
766 Object obj = map.get(TextAttribute.JUSTIFICATION);
767 if (obj != null && obj instanceof Number) {
768 return max(0, min(1, ((Number)obj).floatValue()));
769 }
770 }
771 return DEFAULT.justification;
772 }
773
774 public static NumericShaper getNumericShaping(Map<?, ?> map) {
775 if (map != null) {
776 if (map instanceof AttributeMap &&
777 ((AttributeMap) map).getValues() != null) {
778 return ((AttributeMap)map).getValues().numericShaping;
779 }
780 Object obj = map.get(TextAttribute.NUMERIC_SHAPING);
781 if (obj != null && obj instanceof NumericShaper) {
782 return (NumericShaper)obj;
783 }
784 }
785 return DEFAULT.numericShaping;
786 }
787
788 /**
789 * If this has an imHighlight, create copy of this with those attributes
790 * applied to it. Otherwise return this unchanged.
791 */
792 public AttributeValues applyIMHighlight() {
793 if (imHighlight != null) {
794 InputMethodHighlight hl = null;
795 if (imHighlight instanceof InputMethodHighlight) {
796 hl = (InputMethodHighlight)imHighlight;
797 } else {
798 hl = (InputMethodHighlight)((Annotation)imHighlight).getValue();
799 }
800
801 Map imStyles = hl.getStyle();
802 if (imStyles == null) {
803 Toolkit tk = Toolkit.getDefaultToolkit();
804 imStyles = tk.mapInputMethodHighlight(hl);
805 }
806
807 if (imStyles != null) {
808 return clone().merge(imStyles);
809 }
810 }
811
812 return this;
813 }
814
815 public static AffineTransform getBaselineTransform(Map<?, ?> map) {
816 if (map != null) {
817 AttributeValues av = null;
818 if (map instanceof AttributeMap &&
819 ((AttributeMap) map).getValues() != null) {
820 av = ((AttributeMap)map).getValues();
821 } else if (map.get(TextAttribute.TRANSFORM) != null) {
822 av = AttributeValues.fromMap((Map<Attribute, ?>)map); // yuck
823 }
824 if (av != null) {
825 return av.baselineTransform;
826 }
827 }
828 return null;
829 }
830
831 public static AffineTransform getCharTransform(Map<?, ?> map) {
832 if (map != null) {
833 AttributeValues av = null;
834 if (map instanceof AttributeMap &&
835 ((AttributeMap) map).getValues() != null) {
836 av = ((AttributeMap)map).getValues();
837 } else if (map.get(TextAttribute.TRANSFORM) != null) {
838 av = AttributeValues.fromMap((Map<Attribute, ?>)map); // yuck
839 }
840 if (av != null) {
841 return av.charTransform;
842 }
843 }
844 return null;
845 }
846
847 public void updateDerivedTransforms() {
848 // this also updates the mask for the baseline transform
849 if (transform == null) {
850 baselineTransform = null;
851 charTransform = null;
852 } else {
853 charTransform = new AffineTransform(transform);
854 baselineTransform = extractXRotation(charTransform, true);
855
856 if (charTransform.isIdentity()) {
857 charTransform = null;
858 }
859
860 if (baselineTransform.isIdentity()) {
861 baselineTransform = null;
862 }
863 }
864
865 if (baselineTransform == null) {
866 nondefault &= ~EBASELINE_TRANSFORM.mask;
867 } else {
868 nondefault |= EBASELINE_TRANSFORM.mask;
869 }
870 }
871
872 public static AffineTransform extractXRotation(AffineTransform tx,
873 boolean andTranslation) {
874 return extractRotation(new Point2D.Double(1, 0), tx, andTranslation);
875 }
876
877 public static AffineTransform extractYRotation(AffineTransform tx,
878 boolean andTranslation) {
879 return extractRotation(new Point2D.Double(0, 1), tx, andTranslation);
880 }
881
882 private static AffineTransform extractRotation(Point2D.Double pt,
883 AffineTransform tx, boolean andTranslation) {
884
885 tx.deltaTransform(pt, pt);
886 AffineTransform rtx = AffineTransform.getRotateInstance(pt.x, pt.y);
887
888 try {
889 AffineTransform rtxi = rtx.createInverse();
890 tx.preConcatenate(rtxi);
891 if (andTranslation) {
892 double dx = tx.getTranslateX();
893 double dy = tx.getTranslateY();
894 if (dx != 0 || dy != 0) {
895 tx.setTransform(tx.getScaleX(), tx.getShearY(),
896 tx.getShearX(), tx.getScaleY(), 0, 0);
897 rtx.setTransform(rtx.getScaleX(), rtx.getShearY(),
898 rtx.getShearX(), rtx.getScaleY(), dx, dy);
899 }
900 }
901 }
902 catch (NoninvertibleTransformException e) {
903 return null;
904 }
905 return rtx;
906 }
907}