1491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri/* 2491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * Copyright (C) 2017 The Android Open Source Project 3491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * 4491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * Licensed under the Apache License, Version 2.0 (the "License"); 5491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * you may not use this file except in compliance with the License. 6491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * You may obtain a copy of the License at 7491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * 8491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * http://www.apache.org/licenses/LICENSE-2.0 9491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * 10491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * Unless required by applicable law or agreed to in writing, software 11491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * distributed under the License is distributed on an "AS IS" BASIS, 12491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * See the License for the specific language governing permissions and 14491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * limitations under the License. 15491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri */ 16491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri 17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.graphics; 18491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri 19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri 21491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarriimport android.content.Context; 22543fd2946ded1593b28553879e74ca4393eddd2eClara Bayarriimport android.content.res.Resources; 23491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarriimport android.graphics.Typeface; 2408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonakaimport android.os.CancellationSignal; 259dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikas 26ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull; 27ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.Nullable; 28ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RestrictTo; 29ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry; 30ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry; 31ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.provider.FontsContractCompat.FontInfo; 32491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri 33491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarriimport java.io.File; 3408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonakaimport java.io.IOException; 3508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonakaimport java.io.InputStream; 36491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri 37491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri/** 38491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * Implementation of the Typeface compat methods for API 14 and above. 39491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri * @hide 40491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri */ 41491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri@RestrictTo(LIBRARY_GROUP) 427ca26b25eae95dd45532cd092c9189b8bab625d3Jake Whartonclass TypefaceCompatBaseImpl { 43491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri private static final String TAG = "TypefaceCompatBaseImpl"; 44543fd2946ded1593b28553879e74ca4393eddd2eClara Bayarri private static final String CACHE_FILE_PREFIX = "cached_font_"; 45491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri 4608df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka private interface StyleExtractor<T> { 4708df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka int getWeight(T t); 4808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka boolean isItalic(T t); 4908df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 5008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka 5108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka private static <T> T findBestFont(T[] fonts, int style, StyleExtractor<T> extractor) { 5208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka final int targetWeight = (style & Typeface.BOLD) == 0 ? 400 : 700; 5308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka final boolean isTargetItalic = (style & Typeface.ITALIC) != 0; 5408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka 5508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka T best = null; 5608df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka int bestScore = Integer.MAX_VALUE; // smaller is better 5708df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka 5808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka for (final T font : fonts) { 5908df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka final int score = (Math.abs(extractor.getWeight(font) - targetWeight) * 2) 6008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka + (extractor.isItalic(font) == isTargetItalic ? 0 : 1); 6108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka 6208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka if (best == null || bestScore > score) { 6308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka best = font; 6408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka bestScore = score; 6508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 660b03693667d95d2202dfbb24866665ff061acce1Seigo Nonaka } 6708df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return best; 6808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 6908df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka 7008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka protected FontInfo findBestInfo(FontInfo[] fonts, int style) { 7108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return findBestFont(fonts, style, new StyleExtractor<FontInfo>() { 7208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka @Override 7308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka public int getWeight(FontInfo info) { 7408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return info.getWeight(); 7508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 7608df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka 7708df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka @Override 7808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka public boolean isItalic(FontInfo info) { 7908df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return info.isItalic(); 8008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 8108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka }); 8208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 8308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka 8408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka // Caller must close the stream. 8508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka protected Typeface createFromInputStream(Context context, InputStream is) { 862c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka final File tmpFile = TypefaceCompatUtil.getTempFile(context); 872c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka if (tmpFile == null) { 88491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri return null; 89491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri } 900b03693667d95d2202dfbb24866665ff061acce1Seigo Nonaka try { 9108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka if (!TypefaceCompatUtil.copyToFile(tmpFile, is)) { 922c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka return null; 93491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri } 942c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka return Typeface.createFromFile(tmpFile.getPath()); 952c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka } catch (RuntimeException e) { 962c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka // This was thrown from Typeface.createFromFile when a Typeface could not be loaded, 972c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka // such as due to an invalid ttf or unreadable file. We don't want to throw that 982c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka // exception anymore. 995099c05aeb731f6e4b592e0e7e282761de059cfdSeigo Nonaka return null; 100f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka } finally { 1012c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka tmpFile.delete(); 102543fd2946ded1593b28553879e74ca4393eddd2eClara Bayarri } 103543fd2946ded1593b28553879e74ca4393eddd2eClara Bayarri } 104543fd2946ded1593b28553879e74ca4393eddd2eClara Bayarri 10508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka public Typeface createFromFontInfo(Context context, 10608df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) { 10708df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka // When we load from file, we can only load one font so just take the first one. 10808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka if (fonts.length < 1) { 10908df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return null; 11008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 11108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka FontInfo font = findBestInfo(fonts, style); 11208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka InputStream is = null; 11308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka try { 11408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka is = context.getContentResolver().openInputStream(font.getUri()); 11508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return createFromInputStream(context, is); 11608df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } catch (IOException e) { 11708df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return null; 11808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } finally { 11908df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka TypefaceCompatUtil.closeQuietly(is); 12008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 12108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 122f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka 12308df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry, int style) { 12408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return findBestFont(entry.getEntries(), style, new StyleExtractor<FontFileResourceEntry>() { 12508df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka @Override 12608df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka public int getWeight(FontFileResourceEntry entry) { 12708df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return entry.getWeight(); 12808df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka } 129f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka 13008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka @Override 13108df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka public boolean isItalic(FontFileResourceEntry entry) { 13208df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka return entry.isItalic(); 133f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka } 13408df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka }); 135f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka } 136f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka 137543fd2946ded1593b28553879e74ca4393eddd2eClara Bayarri @Nullable 138b2959d2604c82a2b73e81eac3e19f430e7ac4a1aSeigo Nonaka public Typeface createFromFontFamilyFilesResourceEntry(Context context, 1392c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka FontFamilyFilesResourceEntry entry, Resources resources, int style) { 14008df2afeec46f363ef5f17146750bdf011412e56Seigo Nonaka FontFileResourceEntry best = findBestEntry(entry, style); 141f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka if (best == null) { 142f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka return null; 143f69ef36b9ff270c87e41177551ef4692f9aff965Seigo Nonaka } 1442c4cbf16a9705a4fcb22a8de5cd8795745b01aa4Seigo Nonaka return TypefaceCompat.createFromResourcesFontFile( 14511e831738feba2bb6c5338358812a373bda9991aClara Bayarri context, resources, best.getResourceId(), best.getFileName(), style); 14611e831738feba2bb6c5338358812a373bda9991aClara Bayarri } 14711e831738feba2bb6c5338358812a373bda9991aClara Bayarri 14811e831738feba2bb6c5338358812a373bda9991aClara Bayarri /** 14911e831738feba2bb6c5338358812a373bda9991aClara Bayarri * Used by Resources to load a font resource of type font file. 15011e831738feba2bb6c5338358812a373bda9991aClara Bayarri */ 15111e831738feba2bb6c5338358812a373bda9991aClara Bayarri @Nullable 15211e831738feba2bb6c5338358812a373bda9991aClara Bayarri public Typeface createFromResourcesFontFile( 15311e831738feba2bb6c5338358812a373bda9991aClara Bayarri Context context, Resources resources, int id, String path, int style) { 15411e831738feba2bb6c5338358812a373bda9991aClara Bayarri final File tmpFile = TypefaceCompatUtil.getTempFile(context); 15511e831738feba2bb6c5338358812a373bda9991aClara Bayarri if (tmpFile == null) { 15611e831738feba2bb6c5338358812a373bda9991aClara Bayarri return null; 15711e831738feba2bb6c5338358812a373bda9991aClara Bayarri } 15811e831738feba2bb6c5338358812a373bda9991aClara Bayarri try { 15911e831738feba2bb6c5338358812a373bda9991aClara Bayarri if (!TypefaceCompatUtil.copyToFile(tmpFile, resources, id)) { 16011e831738feba2bb6c5338358812a373bda9991aClara Bayarri return null; 16111e831738feba2bb6c5338358812a373bda9991aClara Bayarri } 16211e831738feba2bb6c5338358812a373bda9991aClara Bayarri return Typeface.createFromFile(tmpFile.getPath()); 16311e831738feba2bb6c5338358812a373bda9991aClara Bayarri } catch (RuntimeException e) { 16411e831738feba2bb6c5338358812a373bda9991aClara Bayarri // This was thrown from Typeface.createFromFile when a Typeface could not be loaded. 16511e831738feba2bb6c5338358812a373bda9991aClara Bayarri // such as due to an invalid ttf or unreadable file. We don't want to throw that 16611e831738feba2bb6c5338358812a373bda9991aClara Bayarri // exception anymore. 16711e831738feba2bb6c5338358812a373bda9991aClara Bayarri return null; 16811e831738feba2bb6c5338358812a373bda9991aClara Bayarri } finally { 16911e831738feba2bb6c5338358812a373bda9991aClara Bayarri tmpFile.delete(); 17011e831738feba2bb6c5338358812a373bda9991aClara Bayarri } 171543fd2946ded1593b28553879e74ca4393eddd2eClara Bayarri } 172491b729fa49127c75acb267b95cc5f0ec1b5f1e3Clara Bayarri} 173