Implement FontData class instead of WritableMap / ReadableMap

This commit is contained in:
Mikael Sand
2017-07-24 18:02:26 +03:00
parent e2128400e3
commit 7c5a7ceed3
5 changed files with 134 additions and 136 deletions
@@ -0,0 +1,97 @@
package com.horcrux.svg;
import com.facebook.react.bridge.ReadableMap;
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_WEIGHT;
class FontData {
static final double DEFAULT_FONT_SIZE = 12d;
private static final double DEFAULT_KERNING = 0d;
private static final double DEFAULT_WORD_SPACING = 0d;
private static final double DEFAULT_LETTER_SPACING = 0d;
private static final String KERNING = "kerning";
private static final String TEXT_ANCHOR = "textAnchor";
private static final String WORD_SPACING = "wordSpacing";
private static final String LETTER_SPACING = "letterSpacing";
private static final String TEXT_DECORATION = "textDecoration";
final double fontSize;
final String fontStyle;
final String fontFamily;
final String fontWeight;
final String textAnchor;
final String textDecoration;
final double kerning;
final double wordSpacing;
final double letterSpacing;
final boolean manualKerning;
static final FontData Defaults = new FontData();
private FontData() {
fontStyle = "";
fontFamily = "";
fontWeight = "";
textAnchor = "start";
textDecoration = "none";
manualKerning = false;
kerning = DEFAULT_KERNING;
fontSize = DEFAULT_FONT_SIZE;
wordSpacing = DEFAULT_WORD_SPACING;
letterSpacing = DEFAULT_LETTER_SPACING;
}
private double toAbsolute(String string, double scale, double fontSize) {
return PropHelper.fromRelative(
string,
0,
0,
scale,
fontSize
);
}
FontData(ReadableMap font, FontData parent, double scale) {
double parentFontSize = parent.fontSize;
if (font.hasKey(FONT_SIZE)) {
String string = font.getString(FONT_SIZE);
fontSize = PropHelper.fromRelative(
string,
parentFontSize,
0,
1,
parentFontSize
);
} else {
fontSize = parentFontSize;
}
fontStyle = font.hasKey(FONT_SIZE) ? font.getString(FONT_SIZE) : parent.fontStyle;
fontFamily = font.hasKey(FONT_FAMILY) ? font.getString(FONT_FAMILY) : parent.fontFamily;
fontWeight = font.hasKey(FONT_WEIGHT) ? font.getString(FONT_WEIGHT) : parent.fontWeight;
textAnchor = font.hasKey(TEXT_ANCHOR) ? font.getString(TEXT_ANCHOR) : parent.textAnchor;
textDecoration = font.hasKey(TEXT_DECORATION) ? font.getString(TEXT_DECORATION) : parent.textDecoration;
final boolean hasKerning = font.hasKey(KERNING);
manualKerning = hasKerning || parent.manualKerning;
// https://www.w3.org/TR/SVG11/text.html#SpacingProperties
// https://drafts.csswg.org/css-text-3/#spacing
// calculated values for units in: kerning, word-spacing, and, letter-spacing.
kerning = hasKerning ? toAbsolute(font.getString(KERNING), scale, fontSize) : parent.kerning;
wordSpacing = font.hasKey(WORD_SPACING) ? toAbsolute(font.getString(WORD_SPACING), scale, fontSize) : parent.wordSpacing;
letterSpacing = font.hasKey(LETTER_SPACING) ? toAbsolute(font.getString(LETTER_SPACING), scale, fontSize) : parent.letterSpacing;
}
}
@@ -9,38 +9,20 @@
package com.horcrux.svg;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import java.util.ArrayList;
import javax.annotation.Nullable;
import static com.horcrux.svg.TextShadowNode.KERNING;
import static com.horcrux.svg.TextShadowNode.TEXT_ANCHOR;
import static com.horcrux.svg.TextShadowNode.WORD_SPACING;
import static com.horcrux.svg.TextShadowNode.LETTER_SPACING;
import static com.horcrux.svg.TextShadowNode.TEXT_DECORATION;
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_FAMILY;
import static com.facebook.react.uimanager.ViewProps.FONT_WEIGHT;
import static com.horcrux.svg.FontData.DEFAULT_FONT_SIZE;
// https://www.w3.org/TR/SVG/text.html#TSpanElement
class GlyphContext {
static final double DEFAULT_FONT_SIZE = 12d;
// Empty font context map
private static final WritableMap DEFAULT_MAP = Arguments.createMap();
static {
DEFAULT_MAP.putDouble(FONT_SIZE, DEFAULT_FONT_SIZE);
}
// Current stack (one per node push/pop)
private final ArrayList<ReadableMap> mFontContext = new ArrayList<>();
private final ArrayList<FontData> mFontContext = new ArrayList<>();
// Unique input attribute lists (only added if node sets a value)
private final ArrayList<String[]> mXsContext = new ArrayList<>();
@@ -65,7 +47,7 @@ class GlyphContext {
// Calculated on push context, percentage and em length depends on parent font size
private double mFontSize = DEFAULT_FONT_SIZE;
private ReadableMap topFont = DEFAULT_MAP;
private FontData topFont = FontData.Defaults;
// Current accumulated values
// https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
@@ -137,8 +119,6 @@ class GlyphContext {
mWidth = width;
mHeight = height;
mFontContext.add(DEFAULT_MAP);
mXsContext.add(mXs);
mYsContext.add(mYs);
mDXsContext.add(mDXs);
@@ -151,6 +131,8 @@ class GlyphContext {
mDYIndices.add(mDYIndex);
mRIndices.add(mRIndex);
mFontContext.add(topFont);
pushIndices();
}
@@ -160,54 +142,30 @@ class GlyphContext {
mX = mY = mDX = mDY = 0;
}
ReadableMap getFont() {
FontData getFont() {
return topFont;
}
private ReadableMap getTopOrParentFont(GroupShadowNode child) {
private FontData getTopOrParentFont(GroupShadowNode child) {
if (mTop > 0) {
return topFont;
} else {
GroupShadowNode parentRoot = child.getParentTextRoot();
while (parentRoot != null) {
ReadableMap map = parentRoot.getGlyphContext().getFont();
if (map != DEFAULT_MAP) {
FontData map = parentRoot.getGlyphContext().getFont();
if (map != FontData.Defaults) {
return map;
}
parentRoot = parentRoot.getParentTextRoot();
}
return DEFAULT_MAP;
}
}
private static void put(String key, WritableMap map, ReadableMap font, ReadableMap parent) {
if (font.hasKey(key)) {
map.putString(key, font.getString(key));
} else if (parent.hasKey(key)) {
map.putString(key, parent.getString(key));
}
}
private void putD(String key, WritableMap map, ReadableMap font, ReadableMap parent) {
if (font.hasKey(key)) {
String string = font.getString(key);
double value = PropHelper.fromRelative(
string,
0,
0,
mScale,
mFontSize
);
map.putDouble(key, value);
} else if (parent.hasKey(key)) {
map.putDouble(key, parent.getDouble(key));
return FontData.Defaults;
}
}
private void pushNodeAndFont(GroupShadowNode node, @Nullable ReadableMap font) {
ReadableMap parent = getTopOrParentFont(node);
FontData parent = getTopOrParentFont(node);
mTop++;
if (font == null) {
@@ -215,42 +173,10 @@ class GlyphContext {
return;
}
WritableMap map = Arguments.createMap();
mFontContext.add(map);
topFont = map;
FontData data = new FontData(font, parent, mScale);
mFontContext.add(data);
topFont = data;
double parentFontSize = parent.getDouble(FONT_SIZE);
if (font.hasKey(FONT_SIZE)) {
String string = font.getString(FONT_SIZE);
double value = PropHelper.fromRelative(
string,
parentFontSize,
0,
1,
parentFontSize
);
map.putDouble(FONT_SIZE, value);
mFontSize = value;
} else {
mFontSize = parentFontSize;
}
map.putDouble(FONT_SIZE, mFontSize);
put(FONT_STYLE, map, font, parent);
put(FONT_FAMILY, map, font, parent);
put(FONT_WEIGHT, map, font, parent);
put(TEXT_ANCHOR, map, font, parent);
put(TEXT_DECORATION, map, font, parent);
// https://www.w3.org/TR/SVG11/text.html#SpacingProperties
// https://drafts.csswg.org/css-text-3/#spacing
// calculated values for units in: kerning, word-spacing, and, letter-spacing.
putD(KERNING, map, font, parent);
putD(WORD_SPACING, map, font, parent);
putD(LETTER_SPACING, map, font, parent);
}
void pushContext(GroupShadowNode node, @Nullable ReadableMap font) {
@@ -19,7 +19,6 @@ import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.graphics.Typeface;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.ReactShadowNode;
import com.facebook.react.uimanager.annotations.ReactProp;
@@ -28,11 +27,6 @@ import javax.annotation.Nullable;
import static android.graphics.PathMeasure.POSITION_MATRIX_FLAG;
import static android.graphics.PathMeasure.TANGENT_MATRIX_FLAG;
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.facebook.react.uimanager.ViewProps.FONT_FAMILY;
/**
* Shadow node for virtual TSpan view
*/
@@ -45,10 +39,6 @@ class TSpanShadowNode extends TextShadowNode {
private static final String OTF = ".otf";
private static final String TTF = ".ttf";
private static final double DEFAULT_KERNING = 0d;
private static final double DEFAULT_WORD_SPACING = 0d;
private static final double DEFAULT_LETTER_SPACING = 0d;
private Path mCache;
private @Nullable String mContent;
private TextPathShadowNode textPath;
@@ -119,14 +109,13 @@ class TSpanShadowNode extends TextShadowNode {
}
GlyphContext gc = getTextRootGlyphContext();
ReadableMap font = gc.getFont();
FontData font = gc.getFont();
applyTextPropertiesToPaint(paint, font);
double distance = 0;
double renderMethodScaling = 1;
final double textMeasure = paint.measureText(line);
double offset = font.hasKey(TEXT_ANCHOR) ?
getTextAnchorShift(textMeasure, font.getString(TEXT_ANCHOR)) : 0;
double offset = getTextAnchorShift(textMeasure, font.textAnchor);
PathMeasure pm = null;
if (textPath != null) {
@@ -176,17 +165,12 @@ class TSpanShadowNode extends TextShadowNode {
*
* */
final boolean hasKerning = font.hasKey(KERNING);
double kerning = hasKerning ? font.getDouble(KERNING) : DEFAULT_KERNING;
final boolean autoKerning = !hasKerning;
final boolean autoKerning = !font.manualKerning;
double kerning = font.kerning;
final double wordSpacing = font.hasKey(WORD_SPACING) ?
font.getDouble(WORD_SPACING)
: DEFAULT_WORD_SPACING;
final double wordSpacing = font.wordSpacing;
final double letterSpacing = font.hasKey(LETTER_SPACING) ?
font.getDouble(LETTER_SPACING)
: DEFAULT_LETTER_SPACING;
final double letterSpacing = font.letterSpacing;
for (int index = 0; index < length; index++) {
glyph = new Path();
@@ -249,31 +233,27 @@ class TSpanShadowNode extends TextShadowNode {
return path;
}
private void applyTextPropertiesToPaint(Paint paint, ReadableMap font) {
private void applyTextPropertiesToPaint(Paint paint, FontData font) {
AssetManager assetManager = getThemedContext().getResources().getAssets();
double fontSize = font.getDouble(FONT_SIZE) * mScale;
double fontSize = font.fontSize * mScale;
boolean isBold = font.hasKey(FONT_WEIGHT) &&
BOLD.equals(font.getString(FONT_WEIGHT));
boolean isBold = BOLD.equals(font.fontWeight);
boolean isItalic = font.hasKey(FONT_STYLE) &&
ITALIC.equals(font.getString(FONT_STYLE));
boolean isItalic = ITALIC.equals(font.fontStyle);
boolean underlineText = false;
boolean strikeThruText = false;
if (font.hasKey(TEXT_DECORATION)) {
String decoration = font.getString(TEXT_DECORATION);
switch (decoration) {
case TEXT_DECORATION_UNDERLINE:
underlineText = true;
break;
String decoration = font.textDecoration;
switch (decoration) {
case TEXT_DECORATION_UNDERLINE:
underlineText = true;
break;
case TEXT_DECORATION_LINE_THROUGH:
strikeThruText = true;
break;
}
case TEXT_DECORATION_LINE_THROUGH:
strikeThruText = true;
break;
}
int fontStyle;
@@ -288,16 +268,17 @@ class TSpanShadowNode extends TextShadowNode {
}
Typeface typeface = null;
final String fontFamily = font.fontFamily;
try {
String path = FONTS + font.getString(FONT_FAMILY) + OTF;
String path = FONTS + fontFamily + OTF;
typeface = Typeface.createFromAsset(assetManager, path);
} catch (Exception ignored) {
try {
String path = FONTS + font.getString(FONT_FAMILY) + TTF;
String path = FONTS + fontFamily + TTF;
typeface = Typeface.createFromAsset(assetManager, path);
} catch (Exception ignored2) {
try {
typeface = Typeface.create(font.getString(FONT_FAMILY), fontStyle);
typeface = Typeface.create(fontFamily, fontStyle);
} catch (Exception ignored3) {
}
}
@@ -37,12 +37,6 @@ class TextShadowNode extends GroupShadowNode {
static final String TEXT_DECORATION_LINE_THROUGH = "line-through";
// static final String TEXT_DECORATION_BLINK = "blink";
static final String KERNING = "kerning";
static final String TEXT_ANCHOR = "textAnchor";
static final String WORD_SPACING = "wordSpacing";
static final String LETTER_SPACING = "letterSpacing";
static final String TEXT_DECORATION = "textDecoration";
private @Nullable ReadableArray mPositionX;
private @Nullable ReadableArray mPositionY;
private @Nullable ReadableArray mRotate;
@@ -26,7 +26,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
import javax.annotation.Nullable;
import static com.horcrux.svg.GlyphContext.DEFAULT_FONT_SIZE;
import static com.horcrux.svg.FontData.DEFAULT_FONT_SIZE;
abstract class VirtualNode extends LayoutShadowNode {
/*