[go: nahoru, domu]

blob: ff9e7ceb946512f97841b9fefc09a3e5fd613dee [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.core.graphics;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.os.CancellationSignal;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.core.content.res.FontResourcesParserCompat.FontFamilyFilesResourceEntry;
import androidx.core.content.res.FontResourcesParserCompat.FontFileResourceEntry;
import androidx.core.provider.FontsContractCompat.FontInfo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
/**
* Implementation of the Typeface compat methods for API 14 and above.
* @hide
*/
@RestrictTo(LIBRARY_GROUP_PREFIX)
class TypefaceCompatBaseImpl {
private static final String TAG = "TypefaceCompatBaseImpl";
private static final int INVALID_KEY = 0;
/**
* Maps a unique identifier from a Typeface to it's family
*/
@SuppressLint("BanConcurrentHashMap")
private java.util.concurrent.ConcurrentHashMap<Long, FontFamilyFilesResourceEntry>
mFontFamilies = new java.util.concurrent.ConcurrentHashMap<>();
private interface StyleExtractor<T> {
int getWeight(T t);
boolean isItalic(T t);
}
private static <T> T findBestFont(T[] fonts, int style, StyleExtractor<T> extractor) {
final int targetWeight = (style & Typeface.BOLD) == 0 ? 400 : 700;
final boolean isTargetItalic = (style & Typeface.ITALIC) != 0;
T best = null;
int bestScore = Integer.MAX_VALUE; // smaller is better
for (final T font : fonts) {
final int score = (Math.abs(extractor.getWeight(font) - targetWeight) * 2)
+ (extractor.isItalic(font) == isTargetItalic ? 0 : 1);
if (best == null || bestScore > score) {
best = font;
bestScore = score;
}
}
return best;
}
private static long getUniqueKey(@Nullable final Typeface typeface) {
if (typeface == null) {
return INVALID_KEY;
}
try {
final Field field = Typeface.class.getDeclaredField("native_instance");
field.setAccessible(true);
final Number num = (Number) field.get(typeface);
return num.longValue();
} catch (NoSuchFieldException e) {
Log.e(TAG, "Could not retrieve font from family.", e);
return INVALID_KEY;
} catch (IllegalAccessException e) {
Log.e(TAG, "Could not retrieve font from family.", e);
return INVALID_KEY;
}
}
protected FontInfo findBestInfo(FontInfo[] fonts, int style) {
return findBestFont(fonts, style, new StyleExtractor<FontInfo>() {
@Override
public int getWeight(FontInfo info) {
return info.getWeight();
}
@Override
public boolean isItalic(FontInfo info) {
return info.isItalic();
}
});
}
// Caller must close the stream.
protected Typeface createFromInputStream(Context context, InputStream is) {
final File tmpFile = TypefaceCompatUtil.getTempFile(context);
if (tmpFile == null) {
return null;
}
try {
if (!TypefaceCompatUtil.copyToFile(tmpFile, is)) {
return null;
}
return Typeface.createFromFile(tmpFile.getPath());
} catch (RuntimeException e) {
// This was thrown from Typeface.createFromFile when a Typeface could not be loaded,
// such as due to an invalid ttf or unreadable file. We don't want to throw that
// exception anymore.
return null;
} finally {
tmpFile.delete();
}
}
@Nullable
public Typeface createFromFontInfo(Context context,
@Nullable CancellationSignal cancellationSignal, @NonNull FontInfo[] fonts, int style) {
// When we load from file, we can only load one font so just take the first one.
if (fonts.length < 1) {
return null;
}
FontInfo font = findBestInfo(fonts, style);
InputStream is = null;
try {
is = context.getContentResolver().openInputStream(font.getUri());
return createFromInputStream(context, is);
} catch (IOException e) {
return null;
} finally {
TypefaceCompatUtil.closeQuietly(is);
}
}
private FontFileResourceEntry findBestEntry(FontFamilyFilesResourceEntry entry, int style) {
return findBestFont(entry.getEntries(), style, new StyleExtractor<FontFileResourceEntry>() {
@Override
public int getWeight(FontFileResourceEntry entry) {
return entry.getWeight();
}
@Override
public boolean isItalic(FontFileResourceEntry entry) {
return entry.isItalic();
}
});
}
@Nullable
public Typeface createFromFontFamilyFilesResourceEntry(Context context,
FontFamilyFilesResourceEntry entry, Resources resources, int style) {
FontFileResourceEntry best = findBestEntry(entry, style);
if (best == null) {
return null;
}
final Typeface typeface = TypefaceCompat.createFromResourcesFontFile(
context, resources, best.getResourceId(), best.getFileName(), style);
addFontFamily(typeface, entry);
return typeface;
}
/**
* Used by Resources to load a font resource of type font file.
*/
@Nullable
public Typeface createFromResourcesFontFile(
Context context, Resources resources, int id, String path, int style) {
final File tmpFile = TypefaceCompatUtil.getTempFile(context);
if (tmpFile == null) {
return null;
}
try {
if (!TypefaceCompatUtil.copyToFile(tmpFile, resources, id)) {
return null;
}
return Typeface.createFromFile(tmpFile.getPath());
} catch (RuntimeException e) {
// This was thrown from Typeface.createFromFile when a Typeface could not be loaded.
// such as due to an invalid ttf or unreadable file. We don't want to throw that
// exception anymore.
return null;
} finally {
tmpFile.delete();
}
}
/**
* Retrieves the font family resource entries given a unique identifier for a Typeface
*/
@Nullable
FontFamilyFilesResourceEntry getFontFamily(final Typeface typeface) {
final long key = getUniqueKey(typeface);
if (key == INVALID_KEY) {
return null;
}
return mFontFamilies.get(key);
}
private void addFontFamily(final Typeface typeface, final FontFamilyFilesResourceEntry entry) {
final long key = getUniqueKey(typeface);
if (key != INVALID_KEY) {
mFontFamilies.put(key, entry);
}
}
}