package com.ichi2.anki; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Typeface; import android.widget.Toast; import com.ichi2.libanki.Utils; import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import timber.log.Timber; public class AnkiFont { private String mName; private String mFamily; private List<String> mAttributes; private String mPath; private Boolean mIsDefault; private Boolean mIsOverride; private static final String fAssetPathPrefix = "/android_asset/fonts/"; private static Set<String> corruptFonts = new HashSet<>(); private AnkiFont(String name, String family, List<String> attributes, String path) { mName = name; mFamily = family; mAttributes = attributes; mPath = path; mIsDefault = false; mIsOverride = false; } /** * Factory for AnkiFont creation. Creates a typeface wrapper from a font file representing. * * @param ctx Activity context, needed to access assets * @param path Path to typeface file, needed when this is a custom font. * @param fromAssets True if the font is to be found in assets of application * @return A new AnkiFont object or null if the file can't be interpreted as typeface. */ public static AnkiFont createAnkiFont(Context ctx, String path, boolean fromAssets) { File fontfile = new File(path); String name = Utils.splitFilename(fontfile.getName())[0]; String family = name; List<String> attributes = new ArrayList<>(); if (fromAssets) { path = fAssetPathPrefix.concat(fontfile.getName()); } Typeface tf = getTypeface(ctx, path); if (tf == null) { // unable to create typeface return null; } if (tf.isBold() || name.toLowerCase(Locale.US).contains("bold")) { attributes.add("font-weight: bolder;"); family = family.replaceFirst("(?i)-?Bold", ""); } else if (name.toLowerCase(Locale.US).contains("light")) { attributes.add("font-weight: lighter;"); family = family.replaceFirst("(?i)-?Light", ""); } else { attributes.add("font-weight: normal;"); } if (tf.isItalic() || name.toLowerCase(Locale.US).contains("italic")) { attributes.add("font-style: italic;"); family = family.replaceFirst("(?i)-?Italic", ""); } else if (name.toLowerCase(Locale.US).contains("oblique")) { attributes.add("font-style: oblique;"); family = family.replaceFirst("(?i)-?Oblique", ""); } else { attributes.add("font-style: normal;"); } if (name.toLowerCase(Locale.US).contains("condensed") || name.toLowerCase(Locale.US).contains("narrow")) { attributes.add("font-stretch: condensed;"); family = family.replaceFirst("(?i)-?Condensed", ""); family = family.replaceFirst("(?i)-?Narrow(er)?", ""); } else if (name.toLowerCase(Locale.US).contains("expanded") || name.toLowerCase(Locale.US).contains("wide")) { attributes.add("font-stretch: expanded;"); family = family.replaceFirst("(?i)-?Expanded", ""); family = family.replaceFirst("(?i)-?Wide(r)?", ""); } AnkiFont createdFont = new AnkiFont(name, family, attributes, path); // determine if override font or default font SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(ctx); String defaultFont = preferences.getString("defaultFont", ""); boolean overrideFont = preferences.getString("overrideFontBehavior", "0").equals("1"); if (defaultFont.equalsIgnoreCase(name)) { if (overrideFont) { createdFont.setAsOverride(); } else{ createdFont.setAsDefault(); } } return createdFont; } public String getDeclaration() { return "@font-face {" + getCSS(false) + " src: url(\"file://" + mPath + "\");}"; } public String getCSS(boolean override) { StringBuilder sb = new StringBuilder("font-family: \"").append(mFamily); if (override) { sb.append("\" !important;"); } else { sb.append("\";"); } for (String attr : mAttributes) { sb.append(" ").append(attr); if (override) { if (sb.charAt(sb.length() - 1) == ';') { sb.deleteCharAt(sb.length() - 1); sb.append(" !important;"); } else { Timber.d("AnkiFont.getCSS() - unable to set a font attribute important while override is set."); } } } return sb.toString(); } public String getName() { return mName; } public String getPath() { return mPath; } public static Typeface getTypeface(Context ctx, String path) { try { if (path.startsWith(fAssetPathPrefix)) { return Typeface.createFromAsset(ctx.getAssets(), path.replaceFirst("/android_asset/", "")); } else { return Typeface.createFromFile(path); } } catch (RuntimeException e) { Timber.w(e, "Runtime error in getTypeface for File: %s", path); if (!corruptFonts.contains(path)) { // Show warning toast String name = new File(path).getName(); Resources res = AnkiDroidApp.getAppResources(); Toast toast = Toast.makeText(ctx, res.getString(R.string.corrupt_font, name), Toast.LENGTH_LONG); toast.show(); // Don't warn again in this session corruptFonts.add(path); } return null; } } private void setAsDefault() { mIsDefault = true; mIsOverride = false; } private void setAsOverride() { mIsOverride = true; mIsDefault = false; } }