Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2017 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 
     17 package android.support.v4.graphics;
     18 
     19 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
     20 
     21 import android.content.Context;
     22 import android.content.res.Resources;
     23 import android.graphics.Typeface;
     24 import android.os.CancellationSignal;
     25 import android.support.annotation.NonNull;
     26 import android.support.annotation.Nullable;
     27 import android.support.annotation.RequiresApi;
     28 import android.support.annotation.RestrictTo;
     29 import android.support.v4.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
     30 import android.support.v4.content.res.FontResourcesParserCompat.FontFileResourceEntry;
     31 import android.support.v4.provider.FontsContractCompat.FontInfo;
     32 
     33 import java.io.File;
     34 import java.io.IOException;
     35 import java.io.InputStream;
     36 
     37 /**
     38  * Implementation of the Typeface compat methods for API 14 and above.
     39  * @hide
     40  */
     41 @RestrictTo(LIBRARY_GROUP)
     42 @RequiresApi(14)
     43 class TypefaceCompatBaseImpl implements TypefaceCompat.TypefaceCompatImpl {
     44     private static final String TAG = "TypefaceCompatBaseImpl";
     45     private static final String CACHE_FILE_PREFIX = "cached_font_";
     46 
     47     private interface StyleExtractor<T> {
     48         int getWeight(T t);
     49         boolean isItalic(T t);
     50     }
     51 
     52     private static <T> T findBestFont(T[] fonts, int style, StyleExtractor<T> extractor) {
     53         final int targetWeight = (style & Typeface.BOLD) == 0 ? 400 : 700;
     54         final boolean isTargetItalic = (style & Typeface.ITALIC) != 0;
     55 
     56         T best = null;
     57         int bestScore = Integer.MAX_VALUE;  // smaller is better
     58 
     59         for (final T font : fonts) {
     60             final int score = (Math.abs(extractor.getWeight(font) - targetWeight) * 2)
     61                     + (extractor.isItalic(font) == isTargetItalic ? 0 : 1);
     62 
     63             if (best == null || bestScore > score) {
     64                 best = font;
     65                 bestScore = score;
     66             }
     67         }
     68         return best;
     69     }
     70 
     71     protected FontInfo findBestInfo(FontInfo[] fonts, int style) {
     72         return findBestFont(fonts, style, new StyleExtractor<FontInfo>() {
     73             @Override
     74             public int getWeight(FontInfo info) {
     75                 return info.getWeight();
     76             }
     77 
     78             @Override
     79             public boolean isItalic(FontInfo info) {
     80                 return info.isItalic();
     81             }
     82         });
     83     }
     84 
     85     // Caller must close the stream.
     86     protected Typeface createFromInputStream(Context context, InputStream is) {
     87         final File tmpFile = TypefaceCompatUtil.getTempFile(context);
     88         if (tmpFile == null) {
     89             return null;
     90         }
     91         try {
     92             if (!TypefaceCompatUtil.copyToFile(tmpFile, is)) {
     93                 return null;
     94             }
     95             return Typeface.createFromFile(tmpFile.getPath());
     96         } catch (RuntimeException e) {
     97             // This was thrown from Typeface.createFromFile when a Typeface could not be loaded,
     98             // such as due to an invalid ttf or unreadable file. We don't want to throw that
     99             // exception anymore.
    100             return null;
    101         } finally {
    102             tmpFile.delete();
    103         }
    104     }
    105 
    106     @Override
    107     public Typeface createFromFontInfo(Context context,
    108             @Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
    109         // When we load from file, we can only load one font so just take the first one.
    110         if (fonts.length < 1) {
    111             return null;
    112         }
    113         FontInfo font = findBestInfo(fonts, style);
    114         InputStream is = null;
    115         try {
    116             is = context.getContentResolver().openInputStream(font.getUri());
    117             return createFromInputStream(context, is);
    118         } catch (IOException e) {
    119             return null;
    120         } finally {
    121             TypefaceCompatUtil.closeQuietly(is);
    122         }
    123     }
    124 
    125     private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry, int style) {
    126         return findBestFont(entry.getEntries(), style, new StyleExtractor<FontFileResourceEntry>() {
    127             @Override
    128             public int getWeight(FontFileResourceEntry entry) {
    129                 return entry.getWeight();
    130             }
    131 
    132             @Override
    133             public boolean isItalic(FontFileResourceEntry entry) {
    134                 return entry.isItalic();
    135             }
    136         });
    137     }
    138 
    139     @Nullable
    140     @Override
    141     public Typeface createFromFontFamilyFilesResourceEntry(Context context,
    142             FontFamilyFilesResourceEntry entry, Resources resources, int style) {
    143         FontFileResourceEntry best = findBestEntry(entry, style);
    144         if (best == null) {
    145             return null;
    146         }
    147         return TypefaceCompat.createFromResourcesFontFile(
    148                 context, resources, best.getResourceId(), best.getFileName(), style);
    149     }
    150 
    151     /**
    152      * Used by Resources to load a font resource of type font file.
    153      */
    154     @Nullable
    155     @Override
    156     public Typeface createFromResourcesFontFile(
    157             Context context, Resources resources, int id, String path, int style) {
    158         final File tmpFile = TypefaceCompatUtil.getTempFile(context);
    159         if (tmpFile == null) {
    160             return null;
    161         }
    162         try {
    163             if (!TypefaceCompatUtil.copyToFile(tmpFile, resources, id)) {
    164                 return null;
    165             }
    166             return Typeface.createFromFile(tmpFile.getPath());
    167         } catch (RuntimeException e) {
    168             // This was thrown from Typeface.createFromFile when a Typeface could not be loaded.
    169             // such as due to an invalid ttf or unreadable file. We don't want to throw that
    170             // exception anymore.
    171             return null;
    172         } finally {
    173             tmpFile.delete();
    174         }
    175     }
    176 }
    177