From dac969b9446c8d3d92b3c7dff185906ce11e554a Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Tue, 20 Jun 2017 22:04:41 +0300 Subject: [PATCH] First letterSpacing implementation attempt --- .../java/com/horcrux/svg/GlyphContext.java | 22 ++++++++++++++++++- .../java/com/horcrux/svg/TSpanShadowNode.java | 22 +++++++++++++++---- lib/attributes.js | 4 +++- lib/extract/extractText.js | 4 +++- lib/props.js | 2 ++ 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/GlyphContext.java b/android/src/main/java/com/horcrux/svg/GlyphContext.java index 5a9567ae..e87daa34 100644 --- a/android/src/main/java/com/horcrux/svg/GlyphContext.java +++ b/android/src/main/java/com/horcrux/svg/GlyphContext.java @@ -37,6 +37,8 @@ public class GlyphContext { private float mHeight; private int mContextLength = 0; private static final float DEFAULT_FONT_SIZE = 12f; + private static final float DEFAULT_KERNING = 0f; + private static final float DEFAULT_LETTER_SPACING = 0f; GlyphContext(float scale, float width, float height) { mScale = scale; @@ -171,7 +173,11 @@ public class GlyphContext { public ReadableMap getGlyphFont() { String fontFamily = null; float fontSize = DEFAULT_FONT_SIZE; + float kerning = DEFAULT_KERNING; + float letterSpacing = DEFAULT_LETTER_SPACING; boolean fontSizeSet = false; + boolean kerningSet = false; + boolean letterSpacingSet = false; String fontWeight = null; String fontStyle = null; @@ -189,6 +195,18 @@ public class GlyphContext { fontSizeSet = true; } + // TODO: add support for other length units + if (!kerningSet && font.hasKey("kerning")) { + kerning = Float.valueOf(font.getString("kerning")); + kerningSet = true; + } + + // TODO: add support for other length units + if (!letterSpacingSet && font.hasKey("letterSpacing")) { + letterSpacing = Float.valueOf(font.getString("letterSpacing")); + letterSpacingSet = true; + } + if (fontWeight == null && font.hasKey("fontWeight")) { fontWeight = font.getString("fontWeight"); } @@ -196,7 +214,7 @@ public class GlyphContext { fontStyle = font.getString("fontStyle"); } - if (fontFamily != null && fontSizeSet && fontWeight != null && fontStyle != null) { + if (fontFamily != null && fontSizeSet && kerningSet && letterSpacingSet && fontWeight != null && fontStyle != null) { break; } } @@ -206,6 +224,8 @@ public class GlyphContext { map.putDouble("fontSize", fontSize); map.putString("fontWeight", fontWeight); map.putString("fontStyle", fontStyle); + map.putDouble("kerning", kerning); + map.putDouble("letterSpacing", letterSpacing); return map; } diff --git a/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java b/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java index c5cd94dd..612c390f 100644 --- a/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/TSpanShadowNode.java @@ -39,6 +39,8 @@ public class TSpanShadowNode extends TextShadowNode { private static final String PROP_FONT_SIZE = "fontSize"; private static final String PROP_FONT_STYLE = "fontStyle"; private static final String PROP_FONT_WEIGHT = "fontWeight"; + private static final String PROP_KERNING = "kerning"; + private static final String PROP_LETTER_SPACING = "letterSpacing"; @ReactProp(name = "content") public void setContent(@Nullable String content) { @@ -101,12 +103,12 @@ public class TSpanShadowNode extends TextShadowNode { paint.getTextPath(letter, 0, 1, 0, 0, glyph); PointF glyphDelta = getGlyphDeltaFromContext(); - PointF glyphPoint = getGlyphPointFromContext(glyphPosition * 1.2f, width); + PointF glyphPoint = getGlyphPointFromContext(glyphPosition, width); glyphPosition += width; Matrix matrix = new Matrix(); if (mBezierTransformer != null) { - matrix = mBezierTransformer.getTransformAtDistance(glyphPoint.x + glyphDelta.x + width / 2); + matrix = mBezierTransformer.getTransformAtDistance(glyphPoint.x * 1.2f + glyphDelta.x + width / 2); if (textPathHasReachedEnd()) { break; @@ -132,10 +134,22 @@ public class TSpanShadowNode extends TextShadowNode { paint.setTextAlign(Paint.Align.LEFT); - float fontSize = (float)font.getDouble(PROP_FONT_SIZE); + float fontSize = (float)font.getDouble(PROP_FONT_SIZE) * mScale; + float kerning = (float)font.getDouble(PROP_KERNING); + float letterSpacing = (float)font.getDouble(PROP_LETTER_SPACING); - paint.setTextSize(fontSize * mScale); + if (mBezierTransformer == null) { + letterSpacing *= mScale; + } else { + // What is going on here? This helps get closer to how e.g. chrome renders things + // But, still off, depending on the font size and letter-spacing. + letterSpacing *= java.lang.Math.pow(120 / fontSize, 6); + } + paint.setTextSize(fontSize); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + paint.setLetterSpacing(letterSpacing / fontSize); // setLetterSpacing is only available from LOLLIPOP and on + } boolean isBold = font.hasKey(PROP_FONT_WEIGHT) && "bold".equals(font.getString(PROP_FONT_WEIGHT)); boolean isItalic = font.hasKey(PROP_FONT_STYLE) && "italic".equals(font.getString(PROP_FONT_STYLE)); diff --git a/lib/attributes.js b/lib/attributes.js index c7ad65d9..83c7c31c 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -23,7 +23,9 @@ function fontDiffer(a, b) { return a.fontSize !== b.fontSize || a.fontFamily !== b.fontFamily || a.fontStyle !== b.fontStyle || - a.fontWeight !== b.fontWeight; + a.fontWeight !== b.fontWeight || + a.kerning !== b.kerning || + a.letterSpacing !== b.letterSpacing; } const ViewBoxAttributes = { diff --git a/lib/extract/extractText.js b/lib/extract/extractText.js index 35fabcfd..17fca9d7 100644 --- a/lib/extract/extractText.js +++ b/lib/extract/extractText.js @@ -55,7 +55,9 @@ export function extractFont(props) { fontFamily: extractSingleFontFamily(props.fontFamily), fontSize: isNaN(fontSize) ? null : fontSize, fontWeight: props.fontWeight, - fontStyle: props.fontStyle + fontStyle: props.fontStyle, + kerning: props.kerning, + letterSpacing: props.letterSpacing, }; if (typeof props.font === 'string') { diff --git a/lib/props.js b/lib/props.js index e074a468..9ad226ab 100644 --- a/lib/props.js +++ b/lib/props.js @@ -53,6 +53,8 @@ const fontProps = { fontSize: numberProp, fontWeight: numberProp, fontStyle: PropTypes.string, + letterSpacing: PropTypes.string, + kerning: PropTypes.string, font: PropTypes.object };