diff --git a/android/src/main/java/com/horcrux/svg/FontData.java b/android/src/main/java/com/horcrux/svg/FontData.java index 1be1d401..20c5f06e 100644 --- a/android/src/main/java/com/horcrux/svg/FontData.java +++ b/android/src/main/java/com/horcrux/svg/FontData.java @@ -3,12 +3,83 @@ package com.horcrux.svg; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableType; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + import static com.facebook.react.uimanager.ViewProps.FONT_FAMILY; import static com.facebook.react.uimanager.ViewProps.FONT_SIZE; import static com.facebook.react.uimanager.ViewProps.FONT_STYLE; import static com.facebook.react.uimanager.ViewProps.FONT_WEIGHT; import static com.horcrux.svg.TextProperties.*; +class AbsoluteFontWeight { + + static int normal = 400; + + private static final EnumMap fontWeightToInt; + + static { + final Map integerMap = new HashMap<>(); + integerMap.put(FontWeight.Normal, 400); + integerMap.put(FontWeight.Bold, 700); + integerMap.put(FontWeight.w100, 100); + integerMap.put(FontWeight.w200, 200); + integerMap.put(FontWeight.w300, 300); + integerMap.put(FontWeight.w400, 400); + integerMap.put(FontWeight.w500, 500); + integerMap.put(FontWeight.w600, 600); + integerMap.put(FontWeight.w700, 700); + integerMap.put(FontWeight.w800, 800); + integerMap.put(FontWeight.w900, 900); + fontWeightToInt = new EnumMap<>(integerMap); + } + + static int from(FontWeight fontWeight) { + Integer w = fontWeightToInt.get(fontWeight); + if (w != null) { + return w; + } else { + throw new IllegalArgumentException(); + } + } + + // https://drafts.csswg.org/css-fonts-4/#relative-weights + static int from(FontWeight fontWeight, int inherited) { + if (fontWeight == FontWeight.Bolder) { + return bolder(inherited); + } else if (fontWeight == FontWeight.Lighter) { + return lighter(inherited); + } else { + return from(fontWeight); + } + } + + private static int bolder(int inherited) { + if (inherited < 350) { + return 400; + } else if (inherited < 550) { + return 700; + } else if (inherited < 900) { + return 900; + } else { + return inherited; + } + } + + private static int lighter(int inherited) { + if (inherited < 100) { + return inherited; + } else if (inherited < 550) { + return 100; + } else if (inherited < 750) { + return 400; + } else { + return 700; + } + } +} + class FontData { static final double DEFAULT_FONT_SIZE = 12d; @@ -23,14 +94,19 @@ class FontData { private static final String LETTER_SPACING = "letterSpacing"; private static final String TEXT_DECORATION = "textDecoration"; private static final String FONT_FEATURE_SETTINGS = "fontFeatureSettings"; + private static final String FONT_VARIATION_SETTINGS = "fontVariationSettings"; private static final String FONT_VARIANT_LIGATURES = "fontVariantLigatures"; final double fontSize; final String fontFamily; final FontStyle fontStyle; final ReadableMap fontData; - final FontWeight fontWeight; + + FontWeight fontWeight; + int absoluteFontWeight; + final String fontFeatureSettings; + final String fontVariationSettings; final FontVariantLigatures fontVariantLigatures; final TextAnchor textAnchor; @@ -49,7 +125,9 @@ class FontData { fontFamily = ""; fontStyle = FontStyle.normal; fontWeight = FontWeight.Normal; + absoluteFontWeight = AbsoluteFontWeight.normal; fontFeatureSettings = ""; + fontVariationSettings = ""; fontVariantLigatures = FontVariantLigatures.normal; textAnchor = TextAnchor.start; @@ -71,6 +149,21 @@ class FontData { ); } + private void setInheritedWeight(FontData parent) { + fontWeight = parent.fontWeight; + absoluteFontWeight = parent.absoluteFontWeight; + } + + private void handleNumericWeight(FontData parent, double number) { + long weight = Math.round(number); + if (weight >= 1 && weight <= 1000) { + fontWeight = FontWeight.Normal; + absoluteFontWeight = (int)weight; + } else { + setInheritedWeight(parent); + } + } + FontData(ReadableMap font, FontData parent, double scale) { double parentFontSize = parent.fontSize; @@ -91,12 +184,31 @@ class FontData { fontSize = parentFontSize; } + if (font.hasKey(FONT_WEIGHT)) { + ReadableType fontSizeType = font.getType(FONT_WEIGHT); + if (fontSizeType == ReadableType.Number) { + handleNumericWeight(parent, font.getDouble(FONT_WEIGHT)); + } else { + String string = font.getString(FONT_WEIGHT); + if (FontWeight.hasEnum(string)) { + fontWeight = FontWeight.get(string); + absoluteFontWeight = AbsoluteFontWeight.from(fontWeight, parent.absoluteFontWeight); + } else if (string != null) { + handleNumericWeight(parent, Double.parseDouble(string)); + } else { + setInheritedWeight(parent); + } + } + } else { + setInheritedWeight(parent); + } + fontData = font.hasKey(FONT_DATA) ? font.getMap(FONT_DATA) : parent.fontData; fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily; fontStyle = font.hasKey(FONT_STYLE) ? FontStyle.valueOf(font.getString(FONT_STYLE)) : parent.fontStyle; - fontWeight = font.hasKey(FONT_WEIGHT) ? FontWeight.getEnum(font.getString(FONT_WEIGHT)) : parent.fontWeight; fontFeatureSettings = font.hasKey(FONT_FEATURE_SETTINGS) ? font.getString(FONT_FEATURE_SETTINGS) : parent.fontFeatureSettings; + fontVariationSettings = font.hasKey(FONT_VARIATION_SETTINGS) ? font.getString(FONT_VARIATION_SETTINGS) : parent.fontVariationSettings; fontVariantLigatures = font.hasKey(FONT_VARIANT_LIGATURES) ? FontVariantLigatures.valueOf(font.getString(FONT_VARIANT_LIGATURES)) : parent.fontVariantLigatures; textAnchor = font.hasKey(TEXT_ANCHOR) ? TextAnchor.valueOf(font.getString(TEXT_ANCHOR)) : parent.textAnchor; diff --git a/android/src/main/java/com/horcrux/svg/TSpanView.java b/android/src/main/java/com/horcrux/svg/TSpanView.java index 0b6987d7..50d31e80 100644 --- a/android/src/main/java/com/horcrux/svg/TSpanView.java +++ b/android/src/main/java/com/horcrux/svg/TSpanView.java @@ -164,6 +164,9 @@ class TSpanView extends TextView { paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings); } paint.setLetterSpacing((float)(letterSpacing / (font.fontSize * mScale))); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + paint.setFontVariationSettings("'wght' " + font.absoluteFontWeight + ", " + font.fontVariationSettings); + } } cachedAdvance = paint.measureText(line); @@ -343,6 +346,9 @@ class TSpanView extends TextView { String disableDiscretionaryLigatures = "'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, "; paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings); } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + paint.setFontVariationSettings("'wght' " + font.absoluteFontWeight + ", " + font.fontVariationSettings); + } } // OpenType.js font data ReadableMap fontData = font.fontData; diff --git a/android/src/main/java/com/horcrux/svg/TextProperties.java b/android/src/main/java/com/horcrux/svg/TextProperties.java index 3f4f1f14..013aedbe 100644 --- a/android/src/main/java/com/horcrux/svg/TextProperties.java +++ b/android/src/main/java/com/horcrux/svg/TextProperties.java @@ -114,10 +114,11 @@ class TextProperties { this.weight = weight; } - static FontWeight getEnum(String strVal) { - if(!weightToEnum.containsKey(strVal)) { - throw new IllegalArgumentException("Unknown String Value: " + strVal); - } + static boolean hasEnum(String strVal) { + return weightToEnum.containsKey(strVal); + } + + static FontWeight get(String strVal) { return weightToEnum.get(strVal); }