1 /* 2 * Copyright (c) 2004, 2014, 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. 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 37 package sun.font; 38 39 import static sun.font.EAttribute.*; 40 import static java.lang.Math.*; 41 42 import java.awt.Font; 43 import java.awt.Paint; 44 import java.awt.Toolkit; 45 import java.awt.font.GraphicAttribute; 46 import java.awt.font.NumericShaper; 47 import java.awt.font.TextAttribute; 48 import java.awt.font.TransformAttribute; 49 import java.awt.geom.AffineTransform; 50 import java.awt.geom.NoninvertibleTransformException; 51 import java.awt.geom.Point2D; 52 import java.awt.im.InputMethodHighlight; 53 import java.io.Serializable; 54 import java.text.Annotation; 55 import java.text.AttributedCharacterIterator.Attribute; 56 import java.util.Map; 57 import java.util.HashMap; 58 import java.util.Hashtable; 59 60 public 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<Object, Object> 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=").append(baselineTransform).append(", ctx=").append(charTransform).append(']'); 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<TextAttribute, ?> 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 @SuppressWarnings("unchecked") 816 public static AffineTransform getBaselineTransform(Map<?, ?> map) { 817 if (map != null) { 818 AttributeValues av = null; 819 if (map instanceof AttributeMap && 820 ((AttributeMap) map).getValues() != null) { 821 av = ((AttributeMap)map).getValues(); 822 } else if (map.get(TextAttribute.TRANSFORM) != null) { 823 av = AttributeValues.fromMap((Map<Attribute, ?>)map); // yuck 824 } 825 if (av != null) { 826 return av.baselineTransform; 827 } 828 } 829 return null; 830 } 831 832 @SuppressWarnings("unchecked") 833 public static AffineTransform getCharTransform(Map<?, ?> map) { 834 if (map != null) { 835 AttributeValues av = null; 836 if (map instanceof AttributeMap && 837 ((AttributeMap) map).getValues() != null) { 838 av = ((AttributeMap)map).getValues(); 839 } else if (map.get(TextAttribute.TRANSFORM) != null) { 840 av = AttributeValues.fromMap((Map<Attribute, ?>)map); // yuck 841 } 842 if (av != null) { 843 return av.charTransform; 844 } 845 } 846 return null; 847 } 848 849 public void updateDerivedTransforms() { 850 // this also updates the mask for the baseline transform 851 if (transform == null) { 852 baselineTransform = null; 853 charTransform = null; 854 } else { 855 charTransform = new AffineTransform(transform); 856 baselineTransform = extractXRotation(charTransform, true); 857 858 if (charTransform.isIdentity()) { 859 charTransform = null; 860 } 861 862 if (baselineTransform.isIdentity()) { 863 baselineTransform = null; 864 } 865 } 866 867 if (baselineTransform == null) { 868 nondefault &= ~EBASELINE_TRANSFORM.mask; 869 } else { 870 nondefault |= EBASELINE_TRANSFORM.mask; 871 } 872 } 873 874 public static AffineTransform extractXRotation(AffineTransform tx, 875 boolean andTranslation) { 876 return extractRotation(new Point2D.Double(1, 0), tx, andTranslation); 877 } 878 879 public static AffineTransform extractYRotation(AffineTransform tx, 880 boolean andTranslation) { 881 return extractRotation(new Point2D.Double(0, 1), tx, andTranslation); 882 } 883 884 private static AffineTransform extractRotation(Point2D.Double pt, 885 AffineTransform tx, boolean andTranslation) { 886 887 tx.deltaTransform(pt, pt); 888 AffineTransform rtx = AffineTransform.getRotateInstance(pt.x, pt.y); 889 890 try { 891 AffineTransform rtxi = rtx.createInverse(); 892 double dx = tx.getTranslateX(); 893 double dy = tx.getTranslateY(); 894 tx.preConcatenate(rtxi); 895 if (andTranslation) { 896 if (dx != 0 || dy != 0) { 897 tx.setTransform(tx.getScaleX(), tx.getShearY(), 898 tx.getShearX(), tx.getScaleY(), 0, 0); 899 rtx.setTransform(rtx.getScaleX(), rtx.getShearY(), 900 rtx.getShearX(), rtx.getScaleY(), dx, dy); 901 } 902 } 903 } 904 catch (NoninvertibleTransformException e) { 905 return null; 906 } 907 return rtx; 908 } 909 }