From 155fa4b6aed74b508f54278c9acbd67dfd260cec Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Tue, 20 Jun 2017 03:15:01 +0300 Subject: [PATCH] Implement support for em in dx and dy attributes and font props for G element --- .../java/com/horcrux/svg/GlyphContext.java | 31 ++++++++- .../java/com/horcrux/svg/GroupShadowNode.java | 67 +++++++++++++++++++ .../java/com/horcrux/svg/TextShadowNode.java | 58 +--------------- elements/G.js | 9 ++- lib/attributes.js | 7 +- lib/extract/extractText.js | 9 ++- 6 files changed, 117 insertions(+), 64 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/GlyphContext.java b/android/src/main/java/com/horcrux/svg/GlyphContext.java index 88c8e16f..5a9567ae 100644 --- a/android/src/main/java/com/horcrux/svg/GlyphContext.java +++ b/android/src/main/java/com/horcrux/svg/GlyphContext.java @@ -53,6 +53,24 @@ public class GlyphContext { mYContext = new ArrayList<>(); } + public void pushContext(@Nullable ReadableMap font) { + PointF location = mCurrentLocation; + + mDeltaContext.add(mCurrentDelta); + + mCurrentDelta = clonePointF(mCurrentDelta); + + mLocationContext.add(location); + mFontContext.add(font); + mDeltaXContext.add(new ArrayList()); + mDeltaYContext.add(new ArrayList()); + mXContext.add(location.x); + mYContext.add(location.y); + + mCurrentLocation = clonePointF(location); + mContextLength++; + } + public void pushContext(@Nullable ReadableMap font, @Nullable ReadableArray deltaX, @Nullable ReadableArray deltaY, @Nullable String positionX, @Nullable String positionY) { PointF location = mCurrentLocation; @@ -194,10 +212,21 @@ public class GlyphContext { private ArrayList getFloatArrayListFromReadableArray(ReadableArray readableArray) { ArrayList arrayList = new ArrayList<>(); + ReadableMap font = getGlyphFont(); + float fontSize = (float)font.getDouble("fontSize"); if (readableArray != null) { for (int i = 0; i < readableArray.size(); i++) { - arrayList.add((float)readableArray.getDouble(i)); + switch (readableArray.getType(i)) { + case String: + String val = readableArray.getString(i); + arrayList.add(Float.valueOf(val.substring(0, val.length() - 2)) * fontSize); + break; + + case Number: + arrayList.add((float)readableArray.getDouble(i)); + break; + } } } diff --git a/android/src/main/java/com/horcrux/svg/GroupShadowNode.java b/android/src/main/java/com/horcrux/svg/GroupShadowNode.java index c4ece197..b0d176ab 100644 --- a/android/src/main/java/com/horcrux/svg/GroupShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/GroupShadowNode.java @@ -14,8 +14,11 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; +import android.graphics.PointF; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.ReactShadowNode; +import com.facebook.react.uimanager.annotations.ReactProp; import javax.annotation.Nullable; @@ -24,8 +27,70 @@ import javax.annotation.Nullable; * Shadow node for virtual Group view */ public class GroupShadowNode extends RenderableShadowNode { + protected @Nullable ReadableMap mFont; + + private GlyphContext mGlyphContext; + private GroupShadowNode mTextRoot; + + @ReactProp(name = "font") + public void setFont(@Nullable ReadableMap font) { + mFont = font; + markUpdated(); + } + + protected GroupShadowNode getTextRoot() { + if (mTextRoot == null) { + mTextRoot = this; + + while (mTextRoot != null) { + if (mTextRoot.getClass() == GroupShadowNode.class) { + break; + } + + ReactShadowNode parent = mTextRoot.getParent(); + + if (!(parent instanceof GroupShadowNode)) { + //todo: throw exception here + mTextRoot = null; + } else { + mTextRoot = (GroupShadowNode)parent; + } + } + } + + return mTextRoot; + } + + protected void setupGlyphContext() { + mGlyphContext = new GlyphContext(mScale, getCanvasWidth(), getCanvasHeight()); + } + + protected GlyphContext getGlyphContext() { + return mGlyphContext; + } + + protected void pushGlyphContext() { + getTextRoot().getGlyphContext().pushContext(mFont); + } + + protected void popGlyphContext() { + getTextRoot().getGlyphContext().popContext(); + } + + protected ReadableMap getFontFromContext() { + return getTextRoot().getGlyphContext().getGlyphFont(); + } + + protected PointF getGlyphPointFromContext(float offset, float glyphWidth) { + return getTextRoot().getGlyphContext().getNextGlyphPoint(offset, glyphWidth); + } + + protected PointF getGlyphDeltaFromContext() { + return getTextRoot().getGlyphContext().getNextGlyphDelta(); + } public void draw(final Canvas canvas, final Paint paint, final float opacity) { + setupGlyphContext(); if (opacity > MIN_OPACITY_FOR_DRAW) { clip(canvas, paint); drawGroup(canvas, paint, opacity); @@ -33,6 +98,7 @@ public class GroupShadowNode extends RenderableShadowNode { } protected void drawGroup(final Canvas canvas, final Paint paint, final float opacity) { + pushGlyphContext(); final SvgViewShadowNode svg = getSvgShadowNode(); final GroupShadowNode self = this; traverseChildren(new NodeRunnable() { @@ -57,6 +123,7 @@ public class GroupShadowNode extends RenderableShadowNode { return true; } }); + popGlyphContext(); } protected void drawPath(Canvas canvas, Paint paint, float opacity) { diff --git a/android/src/main/java/com/horcrux/svg/TextShadowNode.java b/android/src/main/java/com/horcrux/svg/TextShadowNode.java index a38c855a..651318f3 100644 --- a/android/src/main/java/com/horcrux/svg/TextShadowNode.java +++ b/android/src/main/java/com/horcrux/svg/TextShadowNode.java @@ -43,10 +43,6 @@ public class TextShadowNode extends GroupShadowNode { private @Nullable ReadableArray mDeltaY; private @Nullable String mPositionX; private @Nullable String mPositionY; - private @Nullable ReadableMap mFont; - - private GlyphContext mGlyphContext; - private TextShadowNode mTextRoot; @ReactProp(name = "textAnchor", defaultInt = TEXT_ANCHOR_AUTO) public void setTextAnchor(int textAnchor) { @@ -108,12 +104,6 @@ public class TextShadowNode extends GroupShadowNode { return groupPath; } - protected void drawGroup(Canvas canvas, Paint paint, float opacity) { - pushGlyphContext(); - super.drawGroup(canvas, paint, opacity); - popGlyphContext(); - } - private int getTextAnchor() { return mTextAnchor; } @@ -135,33 +125,6 @@ public class TextShadowNode extends GroupShadowNode { return anchor; } - private TextShadowNode getTextRoot() { - if (mTextRoot == null) { - mTextRoot = this; - - while (mTextRoot != null) { - if (mTextRoot.getClass() == TextShadowNode.class) { - break; - } - - ReactShadowNode parent = mTextRoot.getParent(); - - if (!(parent instanceof TextShadowNode)) { - //todo: throw exception here - mTextRoot = null; - } else { - mTextRoot = (TextShadowNode)parent; - } - } - } - - return mTextRoot; - } - - private void setupGlyphContext() { - mGlyphContext = new GlyphContext(mScale, getCanvasWidth(), getCanvasHeight()); - } - protected void releaseCachedPath() { traverseChildren(new NodeRunnable() { public boolean run(VirtualNode node) { @@ -180,30 +143,11 @@ public class TextShadowNode extends GroupShadowNode { return groupPath; } - protected GlyphContext getGlyphContext() { - return mGlyphContext; - } - + @Override protected void pushGlyphContext() { getTextRoot().getGlyphContext().pushContext(mFont, mDeltaX, mDeltaY, mPositionX, mPositionY); } - protected void popGlyphContext() { - getTextRoot().getGlyphContext().popContext(); - } - - protected ReadableMap getFontFromContext() { - return getTextRoot().getGlyphContext().getGlyphFont(); - } - - protected PointF getGlyphPointFromContext(float offset, float glyphWidth) { - return getTextRoot().getGlyphContext().getNextGlyphPoint(offset, glyphWidth); - } - - protected PointF getGlyphDeltaFromContext() { - return getTextRoot().getGlyphContext().getNextGlyphDelta(); - } - private Matrix getAlignMatrix(Path path) { RectF box = new RectF(); path.computeBounds(box, true); diff --git a/elements/G.js b/elements/G.js index 300309d3..92c1a66e 100644 --- a/elements/G.js +++ b/elements/G.js @@ -1,14 +1,18 @@ import React from 'react'; import createReactNativeComponentClass from 'react-native/Libraries/Renderer/src/renderers/native/createReactNativeComponentClass'; import Shape from './Shape'; -import {pathProps} from '../lib/props'; +import {pathProps, fontProps} from '../lib/props'; import {GroupAttributes} from '../lib/attributes'; import extractProps from '../lib/extract/extractProps'; +import {extractFont} from '../lib/extract/extractText'; export default class extends Shape{ static displayName = 'G'; - static propTypes = pathProps; + static propTypes = { + ...pathProps, + ...fontProps, + }; setNativeProps = (...args) => { this.root.setNativeProps(...args); @@ -19,6 +23,7 @@ export default class extends Shape{ return {this.root = ele;}} > {props.children} diff --git a/lib/attributes.js b/lib/attributes.js index 575be4fe..c7ad65d9 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -74,7 +74,12 @@ const RenderableAttributes = { ...FillAndStrokeAttributes }; -const GroupAttributes = RenderableAttributes; +const GroupAttributes = { + font: { + diff: fontDiffer + }, + ...RenderableAttributes +}; const UseAttributes = { href: true, diff --git a/lib/extract/extractText.js b/lib/extract/extractText.js index b7e628d9..35fabcfd 100644 --- a/lib/extract/extractText.js +++ b/lib/extract/extractText.js @@ -47,7 +47,7 @@ function parseFontString(font) { return cachedFontObjectsFromString[font]; } -function extractFont(props) { +export function extractFont(props) { let font = props.font; let fontSize = +props.fontSize; @@ -68,8 +68,11 @@ function extractFont(props) { function parseDelta(delta) { if (typeof delta === 'string') { - if (isNaN(+delta)) { - return delta.trim().replace(commaReg, ' ').split(spaceReg).map(d => +d || 0); + const trim = delta.trim(); + if (trim.slice(-2) === 'em') { + return [trim]; + } else if (isNaN(+delta)) { + return trim.replace(commaReg, ' ').split(spaceReg).map(d => +d || 0); } else { return [+delta]; }