Typeface.java revision ed00bfdfae5d5cbc9e13f4e8affdece48f4c5b7f
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.graphics; 18 19import android.annotation.IntDef; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.content.res.AssetManager; 23import android.graphics.fonts.FontRequest; 24import android.graphics.fonts.FontResult; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.ParcelFileDescriptor; 28import android.os.ResultReceiver; 29import android.provider.FontsContract; 30import android.text.FontConfig; 31import android.util.Log; 32import android.util.LongSparseArray; 33import android.util.LruCache; 34import android.util.SparseArray; 35 36import com.android.internal.annotations.GuardedBy; 37 38import libcore.io.IoUtils; 39 40import org.xmlpull.v1.XmlPullParserException; 41 42import java.io.File; 43import java.io.FileInputStream; 44import java.io.FileNotFoundException; 45import java.io.IOException; 46import java.lang.annotation.Retention; 47import java.lang.annotation.RetentionPolicy; 48import java.nio.ByteBuffer; 49import java.nio.channels.FileChannel; 50import java.util.ArrayList; 51import java.util.HashMap; 52import java.util.List; 53import java.util.Map; 54 55/** 56 * The Typeface class specifies the typeface and intrinsic style of a font. 57 * This is used in the paint, along with optionally Paint settings like 58 * textSize, textSkewX, textScaleX to specify 59 * how text appears when drawn (and measured). 60 */ 61public class Typeface { 62 63 private static String TAG = "Typeface"; 64 65 /** The default NORMAL typeface object */ 66 public static final Typeface DEFAULT; 67 /** 68 * The default BOLD typeface object. Note: this may be not actually be 69 * bold, depending on what fonts are installed. Call getStyle() to know 70 * for sure. 71 */ 72 public static final Typeface DEFAULT_BOLD; 73 /** The NORMAL style of the default sans serif typeface. */ 74 public static final Typeface SANS_SERIF; 75 /** The NORMAL style of the default serif typeface. */ 76 public static final Typeface SERIF; 77 /** The NORMAL style of the default monospace typeface. */ 78 public static final Typeface MONOSPACE; 79 80 static Typeface[] sDefaults; 81 private static final LongSparseArray<SparseArray<Typeface>> sTypefaceCache = 82 new LongSparseArray<>(3); 83 @GuardedBy("sLock") 84 private static FontsContract sFontsContract; 85 @GuardedBy("sLock") 86 private static Handler mHandler; 87 88 /** 89 * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16. 90 */ 91 private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16); 92 93 static Typeface sDefaultTypeface; 94 static Map<String, Typeface> sSystemFontMap; 95 static FontFamily[] sFallbackFonts; 96 private static final Object sLock = new Object(); 97 98 static final String FONTS_CONFIG = "fonts.xml"; 99 100 /** 101 * @hide 102 */ 103 public long native_instance; 104 105 // Style 106 public static final int NORMAL = 0; 107 public static final int BOLD = 1; 108 public static final int ITALIC = 2; 109 public static final int BOLD_ITALIC = 3; 110 111 private int mStyle = 0; 112 113 private static void setDefault(Typeface t) { 114 sDefaultTypeface = t; 115 nativeSetDefault(t.native_instance); 116 } 117 118 /** Returns the typeface's intrinsic style attributes */ 119 public int getStyle() { 120 return mStyle; 121 } 122 123 /** Returns true if getStyle() has the BOLD bit set. */ 124 public final boolean isBold() { 125 return (mStyle & BOLD) != 0; 126 } 127 128 /** Returns true if getStyle() has the ITALIC bit set. */ 129 public final boolean isItalic() { 130 return (mStyle & ITALIC) != 0; 131 } 132 133 /** 134 * @hide 135 * Used by Resources to load a font resource of type font file. 136 */ 137 @Nullable 138 public static Typeface createFromResources(AssetManager mgr, String path, int cookie) { 139 if (sFallbackFonts != null) { 140 synchronized (sDynamicTypefaceCache) { 141 final String key = createAssetUid(mgr, path); 142 Typeface typeface = sDynamicTypefaceCache.get(key); 143 if (typeface != null) return typeface; 144 145 FontFamily fontFamily = new FontFamily(); 146 if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */)) { 147 fontFamily.freeze(); 148 FontFamily[] families = {fontFamily}; 149 typeface = createFromFamiliesWithDefault(families); 150 sDynamicTypefaceCache.put(key, typeface); 151 return typeface; 152 } 153 } 154 } 155 return null; 156 } 157 158 /** 159 * @hide 160 * Used by Resources to load a font resource of type xml. 161 */ 162 @Nullable 163 public static Typeface createFromResources(FontConfig config, AssetManager mgr, String path) { 164 if (sFallbackFonts != null) { 165 synchronized (sDynamicTypefaceCache) { 166 final String key = createAssetUid(mgr, path); 167 Typeface typeface = sDynamicTypefaceCache.get(key); 168 if (typeface != null) return typeface; 169 170 List<FontConfig.Family> families = config.getFamilies(); 171 if (families == null || families.isEmpty()) { 172 throw new RuntimeException("Font resource contained no fonts."); 173 } 174 if (families.size() > 1) { 175 throw new RuntimeException("Font resource contained more than one family."); 176 } 177 FontConfig.Family family = families.get(0); 178 179 FontFamily fontFamily = new FontFamily(); 180 List<FontConfig.Font> fonts = family.getFonts(); 181 for (int i = 0; i < fonts.size(); i++) { 182 FontConfig.Font font = fonts.get(i); 183 // TODO: Use style and weight info 184 if (!fontFamily.addFontFromAssetManager(mgr, font.getFontName(), 185 0 /* resourceCookie */, false /* isAsset */)) { 186 return null; 187 } 188 } 189 fontFamily.freeze(); 190 FontFamily[] familyChain = { fontFamily }; 191 typeface = createFromFamiliesWithDefault(familyChain); 192 sDynamicTypefaceCache.put(key, typeface); 193 return typeface; 194 } 195 } 196 return null; 197 } 198 199 /** 200 * @hide 201 */ 202 public static Typeface createFromCache(AssetManager mgr, String path) { 203 synchronized (sDynamicTypefaceCache) { 204 final String key = createAssetUid(mgr, path); 205 Typeface typeface = sDynamicTypefaceCache.get(key); 206 if (typeface != null) { 207 return typeface; 208 } 209 } 210 return null; 211 } 212 213 /** 214 * Create a typeface object given a font request. The font will be asynchronously fetched, 215 * therefore the result is delivered to the given callback. See {@link FontRequest}. 216 * Only one of the methods in callback will be invoked, depending on whether the request 217 * succeeds or fails. These calls will happen on the main thread. 218 * @param request A {@link FontRequest} object that identifies the provider and query for the 219 * request. May not be null. 220 * @param callback A callback that will be triggered when results are obtained. May not be null. 221 */ 222 public static void create(@NonNull FontRequest request, @NonNull FontRequestCallback callback) { 223 synchronized (sLock) { 224 if (sFontsContract == null) { 225 sFontsContract = new FontsContract(); 226 mHandler = new Handler(); 227 } 228 final ResultReceiver receiver = new ResultReceiver(null) { 229 @Override 230 public void onReceiveResult(int resultCode, Bundle resultData) { 231 mHandler.post(new Runnable() { 232 @Override 233 public void run() { 234 receiveResult(request, callback, resultCode, resultData); 235 } 236 }); 237 } 238 }; 239 sFontsContract.getFont(request, receiver); 240 } 241 } 242 243 private static void receiveResult(FontRequest request, FontRequestCallback callback, 244 int resultCode, Bundle resultData) { 245 if (resultCode == FontsContract.RESULT_CODE_PROVIDER_NOT_FOUND) { 246 callback.onTypefaceRequestFailed( 247 FontRequestCallback.FAIL_REASON_PROVIDER_NOT_FOUND); 248 return; 249 } 250 if (resultCode == FontsContract.RESULT_CODE_FONT_NOT_FOUND 251 || resultData == null) { 252 callback.onTypefaceRequestFailed( 253 FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); 254 return; 255 } 256 List<FontResult> resultList = 257 resultData.getParcelableArrayList(FontsContract.PARCEL_FONT_RESULTS); 258 if (resultList == null || resultList.isEmpty()) { 259 callback.onTypefaceRequestFailed( 260 FontRequestCallback.FAIL_REASON_FONT_NOT_FOUND); 261 return; 262 } 263 FontFamily fontFamily = new FontFamily(); 264 for (int i = 0; i < resultList.size(); ++i) { 265 FontResult result = resultList.get(i); 266 ParcelFileDescriptor fd = result.getFileDescriptor(); 267 if (fd == null) { 268 callback.onTypefaceRequestFailed( 269 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); 270 return; 271 } 272 try (FileInputStream is = new FileInputStream(fd.getFileDescriptor())) { 273 FileChannel fileChannel = is.getChannel(); 274 long fontSize = fileChannel.size(); 275 ByteBuffer fontBuffer = fileChannel.map( 276 FileChannel.MapMode.READ_ONLY, 0, fontSize); 277 int style = result.getStyle(); 278 int weight = (style & BOLD) != 0 ? 700 : 400; 279 // TODO: this method should be 280 // create(fd, ttcIndex, fontVariationSettings, style). 281 if (!fontFamily.addFontWeightStyle(fontBuffer, result.getTtcIndex(), 282 null, weight, (style & ITALIC) != 0)) { 283 Log.e(TAG, "Error creating font " + request.getQuery()); 284 callback.onTypefaceRequestFailed( 285 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); 286 return; 287 } 288 } catch (IOException e) { 289 Log.e(TAG, "Error reading font " + request.getQuery(), e); 290 callback.onTypefaceRequestFailed( 291 FontRequestCallback.FAIL_REASON_FONT_LOAD_ERROR); 292 return; 293 } finally { 294 IoUtils.closeQuietly(fd); 295 } 296 } 297 callback.onTypefaceRetrieved(Typeface.createFromFamiliesWithDefault( 298 new FontFamily[] {fontFamily})); 299 } 300 301 /** 302 * Interface used to receive asynchronously fetched typefaces. 303 */ 304 public interface FontRequestCallback { 305 /** 306 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given 307 * provider was not found on the device. 308 */ 309 int FAIL_REASON_PROVIDER_NOT_FOUND = 0; 310 /** 311 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the font 312 * returned by the provider was not loaded properly. 313 */ 314 int FAIL_REASON_FONT_LOAD_ERROR = 1; 315 /** 316 * Constant returned by {@link #onTypefaceRequestFailed(int)} signaling that the given 317 * provider did not return any results for the given query. 318 */ 319 int FAIL_REASON_FONT_NOT_FOUND = 2; 320 321 /** @hide */ 322 @IntDef({FAIL_REASON_PROVIDER_NOT_FOUND, FAIL_REASON_FONT_LOAD_ERROR, 323 FAIL_REASON_FONT_NOT_FOUND}) 324 @Retention(RetentionPolicy.SOURCE) 325 @interface FontRequestFailReason {} 326 327 /** 328 * Called then a Typeface request done via {@link Typeface#create(FontRequest, 329 * FontRequestCallback)} is complete. Note that this method will not be called if 330 * {@link #onTypefaceRequestFailed(int)} is called instead. 331 * @param typeface The Typeface object retrieved. 332 */ 333 void onTypefaceRetrieved(Typeface typeface); 334 335 /** 336 * Called when a Typeface request done via {@link Typeface#create(FontRequest, 337 * FontRequestCallback)} fails. 338 * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND}, 339 * {@link #FAIL_REASON_FONT_NOT_FOUND} or 340 * {@link #FAIL_REASON_FONT_LOAD_ERROR}. 341 */ 342 void onTypefaceRequestFailed(@FontRequestFailReason int reason); 343 } 344 345 /** 346 * Create a typeface object given a family name, and option style information. 347 * If null is passed for the name, then the "default" font will be chosen. 348 * The resulting typeface object can be queried (getStyle()) to discover what 349 * its "real" style characteristics are. 350 * 351 * @param familyName May be null. The name of the font family. 352 * @param style The style (normal, bold, italic) of the typeface. 353 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 354 * @return The best matching typeface. 355 */ 356 public static Typeface create(String familyName, int style) { 357 if (sSystemFontMap != null) { 358 return create(sSystemFontMap.get(familyName), style); 359 } 360 return null; 361 } 362 363 /** 364 * Create a typeface object that best matches the specified existing 365 * typeface and the specified Style. Use this call if you want to pick a new 366 * style from the same family of an existing typeface object. If family is 367 * null, this selects from the default font's family. 368 * 369 * @param family May be null. The name of the existing type face. 370 * @param style The style (normal, bold, italic) of the typeface. 371 * e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC 372 * @return The best matching typeface. 373 */ 374 public static Typeface create(Typeface family, int style) { 375 if (style < 0 || style > 3) { 376 style = 0; 377 } 378 long ni = 0; 379 if (family != null) { 380 // Return early if we're asked for the same face/style 381 if (family.mStyle == style) { 382 return family; 383 } 384 385 ni = family.native_instance; 386 } 387 388 Typeface typeface; 389 SparseArray<Typeface> styles = sTypefaceCache.get(ni); 390 391 if (styles != null) { 392 typeface = styles.get(style); 393 if (typeface != null) { 394 return typeface; 395 } 396 } 397 398 typeface = new Typeface(nativeCreateFromTypeface(ni, style)); 399 if (styles == null) { 400 styles = new SparseArray<Typeface>(4); 401 sTypefaceCache.put(ni, styles); 402 } 403 styles.put(style, typeface); 404 405 return typeface; 406 } 407 408 /** 409 * Returns one of the default typeface objects, based on the specified style 410 * 411 * @return the default typeface that corresponds to the style 412 */ 413 public static Typeface defaultFromStyle(int style) { 414 return sDefaults[style]; 415 } 416 417 /** 418 * Create a new typeface from the specified font data. 419 * 420 * @param mgr The application's asset manager 421 * @param path The file name of the font data in the assets directory 422 * @return The new typeface. 423 */ 424 public static Typeface createFromAsset(AssetManager mgr, String path) { 425 if (sFallbackFonts != null) { 426 synchronized (sDynamicTypefaceCache) { 427 final String key = createAssetUid(mgr, path); 428 Typeface typeface = sDynamicTypefaceCache.get(key); 429 if (typeface != null) return typeface; 430 431 FontFamily fontFamily = new FontFamily(); 432 if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */)) { 433 fontFamily.freeze(); 434 FontFamily[] families = { fontFamily }; 435 typeface = createFromFamiliesWithDefault(families); 436 sDynamicTypefaceCache.put(key, typeface); 437 return typeface; 438 } else { 439 fontFamily.abortCreation(); 440 } 441 } 442 } 443 throw new RuntimeException("Font asset not found " + path); 444 } 445 446 /** 447 * Creates a unique id for a given AssetManager and asset path. 448 * 449 * @param mgr AssetManager instance 450 * @param path The path for the asset. 451 * @return Unique id for a given AssetManager and asset path. 452 */ 453 private static String createAssetUid(final AssetManager mgr, String path) { 454 final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers(); 455 final StringBuilder builder = new StringBuilder(); 456 final int size = pkgs.size(); 457 for (int i = 0; i < size; i++) { 458 builder.append(pkgs.valueAt(i)); 459 builder.append("-"); 460 } 461 builder.append(path); 462 return builder.toString(); 463 } 464 465 /** 466 * Create a new typeface from the specified font file. 467 * 468 * @param path The path to the font data. 469 * @return The new typeface. 470 */ 471 public static Typeface createFromFile(File path) { 472 return createFromFile(path.getAbsolutePath()); 473 } 474 475 /** 476 * Create a new typeface from the specified font file. 477 * 478 * @param path The full path to the font data. 479 * @return The new typeface. 480 */ 481 public static Typeface createFromFile(String path) { 482 if (sFallbackFonts != null) { 483 FontFamily fontFamily = new FontFamily(); 484 if (fontFamily.addFont(path, 0 /* ttcIndex */)) { 485 fontFamily.freeze(); 486 FontFamily[] families = { fontFamily }; 487 return createFromFamiliesWithDefault(families); 488 } else { 489 fontFamily.abortCreation(); 490 } 491 } 492 throw new RuntimeException("Font not found " + path); 493 } 494 495 /** 496 * Create a new typeface from an array of font families. 497 * 498 * @param families array of font families 499 * @hide 500 */ 501 public static Typeface createFromFamilies(FontFamily[] families) { 502 long[] ptrArray = new long[families.length]; 503 for (int i = 0; i < families.length; i++) { 504 ptrArray[i] = families[i].mNativePtr; 505 } 506 return new Typeface(nativeCreateFromArray(ptrArray)); 507 } 508 509 /** 510 * Create a new typeface from an array of font families, including 511 * also the font families in the fallback list. 512 * 513 * @param families array of font families 514 * @hide 515 */ 516 public static Typeface createFromFamiliesWithDefault(FontFamily[] families) { 517 long[] ptrArray = new long[families.length + sFallbackFonts.length]; 518 for (int i = 0; i < families.length; i++) { 519 ptrArray[i] = families[i].mNativePtr; 520 } 521 for (int i = 0; i < sFallbackFonts.length; i++) { 522 ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr; 523 } 524 return new Typeface(nativeCreateFromArray(ptrArray)); 525 } 526 527 // don't allow clients to call this directly 528 private Typeface(long ni) { 529 if (ni == 0) { 530 throw new RuntimeException("native typeface cannot be made"); 531 } 532 533 native_instance = ni; 534 mStyle = nativeGetStyle(ni); 535 } 536 537 private static FontFamily makeFamilyFromParsed(FontConfig.Family family, 538 Map<String, ByteBuffer> bufferForPath) { 539 FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant()); 540 for (FontConfig.Font font : family.getFonts()) { 541 ByteBuffer fontBuffer = bufferForPath.get(font.getFontName()); 542 if (fontBuffer == null) { 543 try (FileInputStream file = new FileInputStream(font.getFontName())) { 544 FileChannel fileChannel = file.getChannel(); 545 long fontSize = fileChannel.size(); 546 fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); 547 bufferForPath.put(font.getFontName(), fontBuffer); 548 } catch (IOException e) { 549 Log.e(TAG, "Error mapping font file " + font.getFontName()); 550 continue; 551 } 552 } 553 if (!fontFamily.addFontWeightStyle(fontBuffer, font.getTtcIndex(), font.getAxes(), 554 font.getWeight(), font.isItalic())) { 555 Log.e(TAG, "Error creating font " + font.getFontName() + "#" + font.getTtcIndex()); 556 } 557 } 558 fontFamily.freeze(); 559 return fontFamily; 560 } 561 562 /* 563 * (non-Javadoc) 564 * 565 * This should only be called once, from the static class initializer block. 566 */ 567 private static void init() { 568 // Load font config and initialize Minikin state 569 File systemFontConfigLocation = getSystemFontConfigLocation(); 570 File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG); 571 try { 572 FileInputStream fontsIn = new FileInputStream(configFilename); 573 FontConfig fontConfig = FontListParser.parse(fontsIn); 574 575 Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>(); 576 577 List<FontFamily> familyList = new ArrayList<FontFamily>(); 578 // Note that the default typeface is always present in the fallback list; 579 // this is an enhancement from pre-Minikin behavior. 580 for (int i = 0; i < fontConfig.getFamilies().size(); i++) { 581 FontConfig.Family f = fontConfig.getFamilies().get(i); 582 if (i == 0 || f.getName() == null) { 583 familyList.add(makeFamilyFromParsed(f, bufferForPath)); 584 } 585 } 586 sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]); 587 setDefault(Typeface.createFromFamilies(sFallbackFonts)); 588 589 Map<String, Typeface> systemFonts = new HashMap<String, Typeface>(); 590 for (int i = 0; i < fontConfig.getFamilies().size(); i++) { 591 Typeface typeface; 592 FontConfig.Family f = fontConfig.getFamilies().get(i); 593 if (f.getName() != null) { 594 if (i == 0) { 595 // The first entry is the default typeface; no sense in 596 // duplicating the corresponding FontFamily. 597 typeface = sDefaultTypeface; 598 } else { 599 FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath); 600 FontFamily[] families = { fontFamily }; 601 typeface = Typeface.createFromFamiliesWithDefault(families); 602 } 603 systemFonts.put(f.getName(), typeface); 604 } 605 } 606 for (FontConfig.Alias alias : fontConfig.getAliases()) { 607 Typeface base = systemFonts.get(alias.getToName()); 608 Typeface newFace = base; 609 int weight = alias.getWeight(); 610 if (weight != 400) { 611 newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); 612 } 613 systemFonts.put(alias.getName(), newFace); 614 } 615 sSystemFontMap = systemFonts; 616 617 } catch (RuntimeException e) { 618 Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e); 619 // TODO: normal in non-Minikin case, remove or make error when Minikin-only 620 } catch (FileNotFoundException e) { 621 Log.e(TAG, "Error opening " + configFilename, e); 622 } catch (IOException e) { 623 Log.e(TAG, "Error reading " + configFilename, e); 624 } catch (XmlPullParserException e) { 625 Log.e(TAG, "XML parse exception for " + configFilename, e); 626 } 627 } 628 629 static { 630 init(); 631 // Set up defaults and typefaces exposed in public API 632 DEFAULT = create((String) null, 0); 633 DEFAULT_BOLD = create((String) null, Typeface.BOLD); 634 SANS_SERIF = create("sans-serif", 0); 635 SERIF = create("serif", 0); 636 MONOSPACE = create("monospace", 0); 637 638 sDefaults = new Typeface[] { 639 DEFAULT, 640 DEFAULT_BOLD, 641 create((String) null, Typeface.ITALIC), 642 create((String) null, Typeface.BOLD_ITALIC), 643 }; 644 645 } 646 647 private static File getSystemFontConfigLocation() { 648 return new File("/system/etc/"); 649 } 650 651 @Override 652 protected void finalize() throws Throwable { 653 try { 654 nativeUnref(native_instance); 655 native_instance = 0; // Other finalizers can still call us. 656 } finally { 657 super.finalize(); 658 } 659 } 660 661 @Override 662 public boolean equals(Object o) { 663 if (this == o) return true; 664 if (o == null || getClass() != o.getClass()) return false; 665 666 Typeface typeface = (Typeface) o; 667 668 return mStyle == typeface.mStyle && native_instance == typeface.native_instance; 669 } 670 671 @Override 672 public int hashCode() { 673 /* 674 * Modified method for hashCode with long native_instance derived from 675 * http://developer.android.com/reference/java/lang/Object.html 676 */ 677 int result = 17; 678 result = 31 * result + (int) (native_instance ^ (native_instance >>> 32)); 679 result = 31 * result + mStyle; 680 return result; 681 } 682 683 private static native long nativeCreateFromTypeface(long native_instance, int style); 684 private static native long nativeCreateWeightAlias(long native_instance, int weight); 685 private static native void nativeUnref(long native_instance); 686 private static native int nativeGetStyle(long native_instance); 687 private static native long nativeCreateFromArray(long[] familyArray); 688 private static native void nativeSetDefault(long native_instance); 689} 690