diff --git a/android/src/main/java/com/horcrux/svg/GlyphContext.java b/android/src/main/java/com/horcrux/svg/GlyphContext.java index 75b6b7a9..13f23cca 100644 --- a/android/src/main/java/com/horcrux/svg/GlyphContext.java +++ b/android/src/main/java/com/horcrux/svg/GlyphContext.java @@ -32,40 +32,40 @@ class GlyphContext { // Unique input attribute lists (only added if node sets a value) private final ArrayList mXsContext = new ArrayList<>(); private final ArrayList mYsContext = new ArrayList<>(); - private final ArrayList mdXsContext = new ArrayList<>(); - private final ArrayList mdYsContext = new ArrayList<>(); + private final ArrayList mDXsContext = new ArrayList<>(); + private final ArrayList mDYsContext = new ArrayList<>(); private final ArrayList mRsContext = new ArrayList<>(); // Unique index into attribute list (one per unique list) private final ArrayList mXIndices = new ArrayList<>(); private final ArrayList mYIndices = new ArrayList<>(); - private final ArrayList mdXIndices = new ArrayList<>(); - private final ArrayList mdYIndices = new ArrayList<>(); + private final ArrayList mDXIndices = new ArrayList<>(); + private final ArrayList mDYIndices = new ArrayList<>(); private final ArrayList mRIndices = new ArrayList<>(); // Index of unique context used (one per node push/pop) private final ArrayList mXsIndices = new ArrayList<>(); private final ArrayList mYsIndices = new ArrayList<>(); - private final ArrayList mdXsIndices = new ArrayList<>(); - private final ArrayList mdYsIndices = new ArrayList<>(); + private final ArrayList mDXsIndices = new ArrayList<>(); + private final ArrayList mDYsIndices = new ArrayList<>(); private final ArrayList mRsIndices = new ArrayList<>(); // Current stack (one per node push/pop) private final ArrayList mFontContext = new ArrayList<>(); private final ArrayList mNodes = new ArrayList<>(); - // Cached per push context - private double fontSize = DEFAULT_FONT_SIZE; + // Cleared on push context, cached on getFontSize + private float mFontSize = DEFAULT_FONT_SIZE; // Current accumulated values // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate // syntax is the same as that for - private float mx; - private float my; + private float mX; + private float mY; // https://www.w3.org/TR/SVG/types.html#Length - private float mdx; - private float mdy; + private float mDX; + private float mDY; // Current SVGLengthList // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList @@ -81,10 +81,10 @@ class GlyphContext { // https://www.w3.org/TR/SVG/types.html#DataTypeLengths // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute - private String[] mdXs = new String[]{}; + private String[] mDXs = new String[]{}; // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute - private String[] mdYs = new String[]{}; + private String[] mDYs = new String[]{}; // Current SVGLengthList // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers @@ -95,26 +95,34 @@ class GlyphContext { // Current attribute list index private int mXsIndex; private int mYsIndex; - private int mdXsIndex; - private int mdYsIndex; + private int mDXsIndex; + private int mDYsIndex; private int mRsIndex; // Current value index in current attribute list private int mXIndex = -1; private int mYIndex = -1; - private int mdXIndex = -1; - private int mdYIndex = -1; + private int mDXIndex = -1; + private int mDYIndex = -1; private int mRIndex = -1; // Stack length and last index - private int mContextLength; - private int top = -1; + private int mIndexTop; + private int mTop = -1; // Constructor parameters private final float mScale; private final float mWidth; private final float mHeight; + private void pushIndices() { + mXsIndices.add(mXsIndex); + mYsIndices.add(mYsIndex); + mDXsIndices.add(mDXsIndex); + mDYsIndices.add(mDYsIndex); + mRsIndices.add(mRsIndex); + } + GlyphContext(float scale, float width, float height) { mScale = scale; mWidth = width; @@ -122,27 +130,55 @@ class GlyphContext { mXsContext.add(mXs); mYsContext.add(mYs); - mdXsContext.add(mdXs); - mdYsContext.add(mdYs); + mDXsContext.add(mDXs); + mDYsContext.add(mDYs); mRsContext.add(mRs); mXIndices.add(mXIndex); mYIndices.add(mYIndex); - mdXIndices.add(mdXIndex); - mdYIndices.add(mdYIndex); + mDXIndices.add(mDXIndex); + mDYIndices.add(mDYIndex); mRIndices.add(mRIndex); pushIndices(); } - void pushContext(GroupShadowNode node, @Nullable ReadableMap font) { + private void reset() { + mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0; + mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1; + mX = mY = mDX = mDY = 0; + } + + private void pushNodeAndFont(GroupShadowNode node, @Nullable ReadableMap font) { mFontContext.add(font); mNodes.add(node); - mContextLength++; - pushIndices(); - top++; + mFontSize = -1; + mIndexTop++; + mTop++; + } - fontSize = getFontSize(); + void pushContext(GroupShadowNode node, @Nullable ReadableMap font) { + pushNodeAndFont(node, font); + pushIndices(); + } + + private String[] getStringArrayFromReadableArray(ReadableArray readableArray) { + int size = readableArray.size(); + String[] strings = new String[size]; + for (int i = 0; i < size; i++) { + strings[i] = readableArray.getString(i); + } + return strings; + } + + private float[] getFloatArrayFromReadableArray(ReadableArray readableArray) { + int size = readableArray.size(); + float[] floats = new float[size]; + for (int i = 0; i < size; i++) { + String string = readableArray.getString(i); + floats[i] = Float.valueOf(string); + } + return floats; } void pushContext( @@ -159,12 +195,7 @@ class GlyphContext { this.reset(); } - mFontContext.add(font); - mNodes.add(node); - mContextLength++; - top++; - - fontSize = getFontSize(); + pushNodeAndFont(node, font); if (x != null && x.size() != 0) { mXsIndex++; @@ -183,19 +214,19 @@ class GlyphContext { } if (deltaX != null && deltaX.size() != 0) { - mdXsIndex++; - mdXIndex = -1; - mdXIndices.add(mdXIndex); - mdXs = getStringArrayFromReadableArray(deltaX); - mdXsContext.add(mdXs); + mDXsIndex++; + mDXIndex = -1; + mDXIndices.add(mDXIndex); + mDXs = getStringArrayFromReadableArray(deltaX); + mDXsContext.add(mDXs); } if (deltaY != null && deltaY.size() != 0) { - mdYsIndex++; - mdYIndex = -1; - mdYIndices.add(mdYIndex); - mdYs = getStringArrayFromReadableArray(deltaY); - mdYsContext.add(mdYs); + mDYsIndex++; + mDYIndex = -1; + mDYIndices.add(mDYIndex); + mDYs = getStringArrayFromReadableArray(deltaY); + mDYsContext.add(mDYs); } if (rotate != null && rotate.size() != 0) { @@ -209,44 +240,30 @@ class GlyphContext { pushIndices(); } - private void pushIndices() { - mXsIndices.add(mXsIndex); - mYsIndices.add(mYsIndex); - mdXsIndices.add(mdXsIndex); - mdYsIndices.add(mdYsIndex); - mRsIndices.add(mRsIndex); - } - - private void reset() { - mXsIndex = mYsIndex = mdXsIndex = mdYsIndex = mRsIndex = 0; - mXIndex = mYIndex = mdXIndex = mdYIndex = mRIndex = -1; - mx = my = mdx = mdy = 0; - } - void popContext() { - mXsIndices.remove(mContextLength); - mYsIndices.remove(mContextLength); - mdXsIndices.remove(mContextLength); - mdYsIndices.remove(mContextLength); - mRsIndices.remove(mContextLength); + mXsIndices.remove(mIndexTop); + mYsIndices.remove(mIndexTop); + mDXsIndices.remove(mIndexTop); + mDYsIndices.remove(mIndexTop); + mRsIndices.remove(mIndexTop); - mContextLength--; - top--; + mFontContext.remove(mTop); + mNodes.remove(mTop); - mNodes.remove(mContextLength); - mFontContext.remove(mContextLength); + mIndexTop--; + mTop--; int x = mXsIndex; int y = mYsIndex; - int dx = mdXsIndex; - int dy = mdYsIndex; + int dx = mDXsIndex; + int dy = mDYsIndex; int r = mRsIndex; - mXsIndex = mXsIndices.get(mContextLength); - mYsIndex = mYsIndices.get(mContextLength); - mdXsIndex = mdXsIndices.get(mContextLength); - mdYsIndex = mdYsIndices.get(mContextLength); - mRsIndex = mRsIndices.get(mContextLength); + mXsIndex = mXsIndices.get(mIndexTop); + mYsIndex = mYsIndices.get(mIndexTop); + mDXsIndex = mDXsIndices.get(mIndexTop); + mDYsIndex = mDYsIndices.get(mIndexTop); + mRsIndex = mRsIndices.get(mIndexTop); if (x != mXsIndex) { mXsContext.remove(x); @@ -258,15 +275,15 @@ class GlyphContext { mYs = mYsContext.get(mYsIndex); mYIndex = mYIndices.get(mYsIndex); } - if (dx != mdXsIndex) { - mdXsContext.remove(dx); - mdXs = mdXsContext.get(mdXsIndex); - mdXIndex = mdXIndices.get(mdXsIndex); + if (dx != mDXsIndex) { + mDXsContext.remove(dx); + mDXs = mDXsContext.get(mDXsIndex); + mDXIndex = mDXIndices.get(mDXsIndex); } - if (dy != mdYsIndex) { - mdYsContext.remove(dy); - mdYs = mdYsContext.get(mdYsIndex); - mdYIndex = mdYIndices.get(mdYsIndex); + if (dy != mDYsIndex) { + mDYsContext.remove(dy); + mDYs = mDYsContext.get(mDYsIndex); + mDYIndex = mDYIndices.get(mDYsIndex); } if (r != mRsIndex) { mRsContext.remove(r); @@ -282,20 +299,44 @@ class GlyphContext { } } + private float getActualFontSize() { + for (int index = mTop; index >= 0; index--) { + ReadableMap font = mFontContext.get(index); + + if (mFontContext.get(index).hasKey(FONT_SIZE)) { + String string = font.getString(FONT_SIZE); + return PropHelper.fromRelativeToFloat(string, mHeight, 0, 1, DEFAULT_FONT_SIZE); + } + } + + if (mTop > -1) { + return mNodes.get(0).getFontSizeFromParentContext(); + } + + return DEFAULT_FONT_SIZE; + } + + float getFontSize() { + if (mFontSize == -1) { + mFontSize = getActualFontSize(); + } + return mFontSize; + } + float nextX(float glyphWidth) { incrementIndices(mXIndices, mXsIndex); int nextIndex = mXIndex + 1; if (nextIndex < mXs.length) { - mdx = 0; + mDX = 0; mXIndex = nextIndex; String val = mXs[nextIndex]; - mx = PropHelper.fromRelativeToFloat(val, mWidth, 0, mScale, fontSize); + mX = PropHelper.fromRelativeToFloat(val, mWidth, 0, mScale, getFontSize()); } - mx += glyphWidth; + mX += glyphWidth; - return mx; + return mX; } float nextY() { @@ -303,41 +344,41 @@ class GlyphContext { int nextIndex = mYIndex + 1; if (nextIndex < mYs.length) { - mdy = 0; + mDY = 0; mYIndex = nextIndex; String val = mYs[nextIndex]; - my = PropHelper.fromRelativeToFloat(val, mHeight, 0, mScale, fontSize); + mY = PropHelper.fromRelativeToFloat(val, mHeight, 0, mScale, getFontSize()); } - return my; + return mY; } float nextDeltaX() { - incrementIndices(mdXIndices, mdXsIndex); + incrementIndices(mDXIndices, mDXsIndex); - int nextIndex = mdXIndex + 1; - if (nextIndex < mdXs.length) { - mdXIndex = nextIndex; - String string = mdXs[nextIndex]; - float val = PropHelper.fromRelativeToFloat(string, mWidth, 0, 1, fontSize); - mdx += val * mScale; + int nextIndex = mDXIndex + 1; + if (nextIndex < mDXs.length) { + mDXIndex = nextIndex; + String string = mDXs[nextIndex]; + float val = PropHelper.fromRelativeToFloat(string, mWidth, 0, 1, getFontSize()); + mDX += val * mScale; } - return mdx; + return mDX; } float nextDeltaY() { - incrementIndices(mdYIndices, mdYsIndex); + incrementIndices(mDYIndices, mDYsIndex); - int nextIndex = mdYIndex + 1; - if (nextIndex < mdYs.length) { - mdYIndex = nextIndex; - String string = mdYs[nextIndex]; - float val = PropHelper.fromRelativeToFloat(string, mHeight, 0, 1, fontSize); - mdy += val * mScale; + int nextIndex = mDYIndex + 1; + if (nextIndex < mDYs.length) { + mDYIndex = nextIndex; + String string = mDYs[nextIndex]; + float val = PropHelper.fromRelativeToFloat(string, mHeight, 0, 1, getFontSize()); + mDY += val * mScale; } - return mdy; + return mDY; } float nextRotation() { @@ -356,25 +397,9 @@ class GlyphContext { return mHeight; } - double getFontSize() { - for (int index = top; index >= 0; index--) { - ReadableMap font = mFontContext.get(index); - - if (mFontContext.get(index).hasKey(FONT_SIZE)) { - return font.getDouble(FONT_SIZE); - } - } - - if (top > -1) { - return mNodes.get(0).getFontSizeFromParentContext(); - } - - return DEFAULT_FONT_SIZE; - } - ReadableMap getFont() { WritableMap map = Arguments.createMap(); - map.putDouble(FONT_SIZE, fontSize); + map.putDouble(FONT_SIZE, getFontSize()); boolean letterSpacingSet = false; boolean fontFamilySet = false; @@ -382,7 +407,7 @@ class GlyphContext { boolean fontStyleSet = false; boolean kerningSet = false; - for (int index = top; index >= 0; index--) { + for (int index = mTop; index >= 0; index--) { ReadableMap font = mFontContext.get(index); if (!letterSpacingSet && font.hasKey(LETTER_SPACING)) { @@ -424,23 +449,4 @@ class GlyphContext { return map; } - - private String[] getStringArrayFromReadableArray(ReadableArray readableArray) { - int size = readableArray.size(); - String[] strings = new String[size]; - for (int i = 0; i < size; i++) { - strings[i] = readableArray.getString(i); - } - return strings; - } - - private float[] getFloatArrayFromReadableArray(ReadableArray readableArray) { - int size = readableArray.size(); - float[] floats = new float[size]; - for (int i = 0; i < size; i++) { - String string = readableArray.getString(i); - floats[i] = Float.valueOf(string); - } - return floats; - } } diff --git a/android/src/main/java/com/horcrux/svg/TextShadowNode.java b/android/src/main/java/com/horcrux/svg/TextShadowNode.java index b85930ab..113d4acc 100644 --- a/android/src/main/java/com/horcrux/svg/TextShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/TextShadowNode.java @@ -41,9 +41,9 @@ class TextShadowNode extends GroupShadowNode { private int mTextDecoration = TEXT_DECORATION_NONE; private @Nullable ReadableArray mPositionX; private @Nullable ReadableArray mPositionY; + private @Nullable ReadableArray mRotate; private @Nullable ReadableArray mDeltaX; private @Nullable ReadableArray mDeltaY; - private @Nullable ReadableArray mRotate; @ReactProp(name = "textAnchor") public void setTextAnchor(int textAnchor) { diff --git a/android/src/main/java/com/horcrux/svg/VirtualNode.java b/android/src/main/java/com/horcrux/svg/VirtualNode.java index 989ea320..12407fb6 100644 --- a/android/src/main/java/com/horcrux/svg/VirtualNode.java +++ b/android/src/main/java/com/horcrux/svg/VirtualNode.java @@ -44,8 +44,8 @@ abstract class VirtualNode extends LayoutShadowNode { 0, 0, 1 }; float mOpacity = 1f; - private double mFontSize = -1; - private double mParentFontSize = -1; + private float mFontSize = -1; + private float mParentFontSize = -1; Matrix mMatrix = new Matrix(); private int mClipRule; @@ -136,7 +136,7 @@ abstract class VirtualNode extends LayoutShadowNode { return mTextRoot; } - double getFontSizeFromContext() { + float getFontSizeFromContext() { if (mFontSize != -1) { return mFontSize; } @@ -149,7 +149,7 @@ abstract class VirtualNode extends LayoutShadowNode { return mFontSize; } - double getFontSizeFromParentContext() { + float getFontSizeFromParentContext() { if (mParentFontSize != -1) { return mParentFontSize; } diff --git a/lib/extract/extractText.js b/lib/extract/extractText.js index 3eba521a..9b6db3d3 100644 --- a/lib/extract/extractText.js +++ b/lib/extract/extractText.js @@ -2,7 +2,7 @@ import _ from 'lodash'; import React, {Children} from 'react'; import TSpan from '../../elements/TSpan'; -const fontRegExp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm%]*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i; +const fontRegExp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?[ptexm%])*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i; const fontFamilyPrefix = /^[\s"']*/; const fontFamilySuffix = /[\s"']*$/; const spaceReg = /\s+/; @@ -42,12 +42,12 @@ function parseFontString(font) { return null; } let fontFamily = extractSingleFontFamily(match[3]); - let fontSize = +match[2] || 12; + let fontSize = match[2] || "12"; let isBold = /bold/.exec(match[1]); let isItalic = /italic/.exec(match[1]); cachedFontObjectsFromString[font] = { - fontFamily: fontFamily, - fontSize: fontSize, + fontSize, + fontFamily, fontWeight: isBold ? 'bold' : 'normal', fontStyle: isItalic ? 'italic' : 'normal' }; @@ -56,15 +56,14 @@ function parseFontString(font) { export function extractFont(props) { let font = props.font; - let fontSize = +props.fontSize; let ownedFont = { fontFamily: extractSingleFontFamily(props.fontFamily), - fontSize: isNaN(fontSize) ? null : fontSize, + letterSpacing: props.letterSpacing, fontWeight: props.fontWeight, fontStyle: props.fontStyle, + fontSize: props.fontSize, kerning: props.kerning, - letterSpacing: props.letterSpacing, }; if (typeof props.font === 'string') {