indices, int topIndex) {
- for (int index = topIndex; index >= 0; index--) {
- int xIndex = indices.get(index);
- indices.set(index, xIndex + 1);
- }
- }
-
- // https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
-
- /**
- * Get font size from context.
- *
- * ‘font-size’
- * Value: < absolute-size > | < relative-size > | < length > | < percentage > | inherit
- * Initial: medium
- * Applies to: text content elements
- * Inherited: yes, the computed value is inherited
- * Percentages: refer to parent element's font size
- * Media: visual
- * Animatable: yes
- *
- * This property refers to the size of the font from baseline to
- * baseline when multiple lines of text are set solid in a multiline
- * layout environment.
- *
- * For SVG, if a < length > is provided without a unit identifier
- * (e.g., an unqualified number such as 128), the SVG user agent
- * processes the < length > as a height value in the current user
- * coordinate system.
- *
- * If a < length > is provided with one of the unit identifiers
- * (e.g., 12pt or 10%), then the SVG user agent converts the
- * < length > into a corresponding value in the current user
- * coordinate system by applying the rules described in Units.
- *
- * Except for any additional information provided in this specification,
- * the normative definition of the property is in CSS2 ([CSS2], section 15.2.4).
- */
- double getFontSize() {
- return mFontSize;
- }
-
- double nextX(double advance) {
- incrementIndices(mXIndices, mXsIndex);
-
- int nextIndex = mXIndex + 1;
- if (nextIndex < mXs.length) {
- mDX = 0;
- mXIndex = nextIndex;
- SVGLength string = mXs[nextIndex];
- mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
- }
-
- mX += advance;
-
- return mX;
- }
-
- double nextY() {
- incrementIndices(mYIndices, mYsIndex);
-
- int nextIndex = mYIndex + 1;
- if (nextIndex < mYs.length) {
- mDY = 0;
- mYIndex = nextIndex;
- SVGLength string = mYs[nextIndex];
- mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
- }
-
- return mY;
- }
-
- double nextDeltaX() {
- incrementIndices(mDXIndices, mDXsIndex);
-
- int nextIndex = mDXIndex + 1;
- if (nextIndex < mDXs.length) {
- mDXIndex = nextIndex;
- SVGLength string = mDXs[nextIndex];
- double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
- mDX += val;
- }
-
- return mDX;
- }
-
- double nextDeltaY() {
- incrementIndices(mDYIndices, mDYsIndex);
-
- int nextIndex = mDYIndex + 1;
- if (nextIndex < mDYs.length) {
- mDYIndex = nextIndex;
- SVGLength string = mDYs[nextIndex];
- double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
- mDY += val;
- }
-
- return mDY;
- }
-
- double nextRotation() {
- incrementIndices(mRIndices, mRsIndex);
-
- mRIndex = Math.min(mRIndex + 1, mRs.length - 1);
-
- return mRs[mRIndex];
- }
-
- float getWidth() {
- return mWidth;
- }
-
- float getHeight() {
- return mHeight;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/GlyphPathBag.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/GlyphPathBag.java
deleted file mode 100644
index 3281c12d9a3fb..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/GlyphPathBag.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.graphics.Paint;
-import android.graphics.Path;
-
-import java.util.ArrayList;
-
-class GlyphPathBag {
- private final ArrayList paths = new ArrayList<>();
- private final int[][] data = new int[256][];
- private final Paint paint;
-
- GlyphPathBag(Paint paint) {
- this.paint = paint;
- // Make indexed-by-one, to allow zero to represent non-cached
- paths.add(new Path());
- }
-
- Path getOrCreateAndCache(char ch, String current) {
- int index = getIndex(ch);
- Path cached;
-
- if (index != 0) {
- cached = paths.get(index);
- } else {
- cached = new Path();
- paint.getTextPath(current, 0, 1, 0, 0, cached);
-
- int[] bin = data[ch >> 8];
- if (bin == null) {
- bin = data[ch >> 8] = new int[256];
- }
- bin[ch & 0xFF] = paths.size();
-
- paths.add(cached);
- }
-
- Path glyph = new Path();
- glyph.addPath(cached);
- return glyph;
- }
-
- private int getIndex(char ch) {
- int[] bin = data[ch >> 8];
- if (bin == null) return 0;
- return bin[ch & 0xFF];
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/GroupView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/GroupView.java
deleted file mode 100644
index f556bec2d410e..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/GroupView.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.os.Build;
-import android.view.View;
-
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.views.view.ReactViewGroup;
-
-import javax.annotation.Nullable;
-
-@SuppressLint("ViewConstructor")
-class GroupView extends RenderableView {
- @Nullable ReadableMap mFont;
- private GlyphContext mGlyphContext;
-
- public GroupView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "font")
- public void setFont(@Nullable ReadableMap font) {
- mFont = font;
- invalidate();
- }
-
- void setupGlyphContext(Canvas canvas) {
- RectF clipBounds = new RectF(canvas.getClipBounds());
- if (mMatrix != null) {
- mMatrix.mapRect(clipBounds);
- }
- if (mTransform != null) {
- mTransform.mapRect(clipBounds);
- }
- mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
- }
-
- GlyphContext getGlyphContext() {
- return mGlyphContext;
- }
-
- private static T requireNonNull(T obj) {
- if (obj == null)
- throw new NullPointerException();
- return obj;
- }
-
- GlyphContext getTextRootGlyphContext() {
- return requireNonNull(getTextRoot()).getGlyphContext();
- }
-
- void pushGlyphContext() {
- getTextRootGlyphContext().pushContext(this, mFont);
- }
-
- void popGlyphContext() {
- getTextRootGlyphContext().popContext();
- }
-
- void draw(final Canvas canvas, final Paint paint, final float opacity) {
- setupGlyphContext(canvas);
- clip(canvas, paint);
- drawGroup(canvas, paint, opacity);
- }
-
- void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
- pushGlyphContext();
- final SvgView svg = getSvgView();
- final GroupView self = this;
- final RectF groupRect = new RectF();
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof MaskView) {
- continue;
- }
- if (child instanceof VirtualView) {
- VirtualView node = ((VirtualView)child);
- if ("none".equals(node.mDisplay)) {
- continue;
- }
- if (node instanceof RenderableView) {
- ((RenderableView)node).mergeProperties(self);
- }
-
- int count = node.saveAndSetupCanvas(canvas, mCTM);
- node.render(canvas, paint, opacity * mOpacity);
- RectF r = node.getClientRect();
- if (r != null) {
- groupRect.union(r);
- }
-
- node.restoreCanvas(canvas, count);
-
- if (node instanceof RenderableView) {
- ((RenderableView)node).resetProperties();
- }
-
- if (node.isResponsible()) {
- svg.enableTouchEvents();
- }
- } else if (child instanceof SvgView) {
- SvgView svgView = (SvgView)child;
- svgView.drawChildren(canvas);
- if (svgView.isResponsible()) {
- svg.enableTouchEvents();
- }
- }
- }
- this.setClientRect(groupRect);
- popGlyphContext();
- }
-
- void drawPath(Canvas canvas, Paint paint, float opacity) {
- super.draw(canvas, paint, opacity);
- }
-
- @Override
- Path getPath(final Canvas canvas, final Paint paint) {
- if (mPath != null) {
- return mPath;
- }
- mPath = new Path();
-
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof MaskView) {
- continue;
- }
- if (node instanceof VirtualView) {
- VirtualView n = (VirtualView)node;
- Matrix transform = n.mMatrix;
- mPath.addPath(n.getPath(canvas, paint), transform);
- }
- }
-
- return mPath;
- }
-
- Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) {
- final Path path = new Path();
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- final Path.Op pop = Path.Op.valueOf(op.name());
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof MaskView) {
- continue;
- }
- if (node instanceof VirtualView) {
- VirtualView n = (VirtualView)node;
- Matrix transform = n.mMatrix;
- Path p2;
- if (n instanceof GroupView) {
- p2 = ((GroupView)n).getPath(canvas, paint, op);
- } else {
- p2 = n.getPath(canvas, paint);
- }
- p2.transform(transform);
- path.op(p2, pop);
- }
- }
- } else {
- Rect clipBounds = canvas.getClipBounds();
- final Region bounds = new Region(clipBounds);
- final Region r = new Region();
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof MaskView) {
- continue;
- }
- if (node instanceof VirtualView) {
- VirtualView n = (VirtualView)node;
- Matrix transform = n.mMatrix;
- Path p2;
- if (n instanceof GroupView) {
- p2 = ((GroupView)n).getPath(canvas, paint, op);
- } else {
- p2 = n.getPath(canvas, paint);
- }
- if (transform != null) {
- p2.transform(transform);
- }
- Region r2 = new Region();
- r2.setPath(p2, bounds);
- r.op(r2, op);
- }
- }
- path.addPath(r.getBoundaryPath());
- }
-
- return path;
- }
-
- @Override
- int hitTest(final float[] src) {
- if (!mInvertible || !mTransformInvertible) {
- return -1;
- }
-
- float[] dst = new float[2];
- mInvMatrix.mapPoints(dst, src);
- mInvTransform.mapPoints(dst);
-
- int x = Math.round(dst[0]);
- int y = Math.round(dst[1]);
-
- Path clipPath = getClipPath();
- if (clipPath != null) {
- if (mClipRegionPath != clipPath) {
- mClipRegionPath = clipPath;
- mClipBounds = new RectF();
- clipPath.computeBounds(mClipBounds, true);
- mClipRegion = getRegion(clipPath, mClipBounds);
- }
- if (!mClipRegion.contains(x, y)) {
- return -1;
- }
- }
-
- for (int i = getChildCount() - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (child instanceof VirtualView) {
- if (child instanceof MaskView) {
- continue;
- }
-
- VirtualView node = (VirtualView) child;
-
- int hitChild = node.hitTest(dst);
- if (hitChild != -1) {
- return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId();
- }
- } else if (child instanceof SvgView) {
- SvgView node = (SvgView) child;
-
- int hitChild = node.reactTagForTouch(dst[0], dst[1]);
- if (hitChild != child.getId()) {
- return hitChild;
- }
- }
- }
-
- return -1;
- }
-
- void saveDefinition() {
- if (mName != null) {
- getSvgView().defineTemplate(this, mName);
- }
-
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof VirtualView) {
- ((VirtualView)node).saveDefinition();
- }
- }
- }
-
- @Override
- void resetProperties() {
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof RenderableView) {
- ((RenderableView)node).resetProperties();
- }
- }
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/ImageView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/ImageView.java
deleted file mode 100644
index 18c2e32a46580..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/ImageView.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.net.Uri;
-
-import com.facebook.common.executors.UiThreadImmediateExecutorService;
-import com.facebook.common.logging.FLog;
-import com.facebook.common.references.CloseableReference;
-import com.facebook.datasource.DataSource;
-import com.facebook.drawee.backends.pipeline.Fresco;
-import com.facebook.imagepipeline.core.ImagePipeline;
-import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
-import com.facebook.imagepipeline.image.CloseableBitmap;
-import com.facebook.imagepipeline.image.CloseableImage;
-import com.facebook.imagepipeline.request.ImageRequest;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.views.imagehelper.ImageSource;
-import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-@SuppressLint("ViewConstructor")
-class ImageView extends RenderableView {
- private SVGLength mX;
- private SVGLength mY;
- private SVGLength mW;
- private SVGLength mH;
- private String uriString;
- private int mImageWidth;
- private int mImageHeight;
- private String mAlign;
- private int mMeetOrSlice;
- private final AtomicBoolean mLoading = new AtomicBoolean(false);
-
- public ImageView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "x")
- public void setX(Dynamic x) {
- mX = SVGLength.from(x);
- invalidate();
- }
-
- @ReactProp(name = "y")
- public void setY(Dynamic y) {
- mY = SVGLength.from(y);
- invalidate();
- }
-
- @ReactProp(name = "width")
- public void setWidth(Dynamic width) {
- mW = SVGLength.from(width);
- invalidate();
- }
-
- @ReactProp(name = "height")
- public void setHeight(Dynamic height) {
- mH = SVGLength.from(height);
- invalidate();
- }
-
- @ReactProp(name = "src")
- public void setSrc(@Nullable ReadableMap src) {
- if (src != null) {
- uriString = src.getString("uri");
-
- if (uriString == null || uriString.isEmpty()) {
- //TODO: give warning about this
- return;
- }
-
- if (src.hasKey("width") && src.hasKey("height")) {
- mImageWidth = src.getInt("width");
- mImageHeight = src.getInt("height");
- } else {
- mImageWidth = 0;
- mImageHeight = 0;
- }
- Uri mUri = Uri.parse(uriString);
- if (mUri.getScheme() == null) {
- ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
- }
- }
- }
-
- @ReactProp(name = "align")
- public void setAlign(String align) {
- mAlign = align;
- invalidate();
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(int meetOrSlice) {
- mMeetOrSlice = meetOrSlice;
- invalidate();
- }
-
- @Override
- void draw(final Canvas canvas, final Paint paint, final float opacity) {
- if (!mLoading.get()) {
- ImagePipeline imagePipeline = Fresco.getImagePipeline();
- ImageSource imageSource = new ImageSource(mContext, uriString);
- ImageRequest request = ImageRequest.fromUri(imageSource.getUri());
- boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request);
-
- if (inMemoryCache) {
- tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity);
- } else {
- loadBitmap(imagePipeline, request);
- }
- }
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- mPath = new Path();
- mPath.addRect(getRect(), Path.Direction.CW);
- return mPath;
- }
-
- private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) {
- mLoading.set(true);
- final DataSource> dataSource
- = imagePipeline.fetchDecodedImage(request, mContext);
- BaseBitmapDataSubscriber subscriber = new BaseBitmapDataSubscriber() {
- @Override
- public void onNewResultImpl(Bitmap bitmap) {
- mLoading.set(false);
- SvgView view = getSvgView();
- if (view != null) {
- view.invalidate();
- }
- }
-
- @Override
- public void onFailureImpl(DataSource dataSource) {
- // No cleanup required here.
- // TODO: more details about this failure
- mLoading.set(false);
- FLog.w(ReactConstants.TAG, dataSource.getFailureCause(), "RNSVG: fetchDecodedImage failed!");
- }
- };
- dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
- }
-
- @Nonnull
- private RectF getRect() {
- double x = relativeOnWidth(mX);
- double y = relativeOnHeight(mY);
- double w = relativeOnWidth(mW);
- double h = relativeOnHeight(mH);
- if (w == 0) {
- w = mImageWidth * mScale;
- }
- if (h == 0) {
- h = mImageHeight * mScale;
- }
-
- return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h));
- }
-
- private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) {
- if (mImageWidth == 0 || mImageHeight == 0) {
- mImageWidth = bitmap.getWidth();
- mImageHeight = bitmap.getHeight();
- }
-
- RectF renderRect = getRect();
- RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
- Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
- transform.mapRect(vbRect);
-
- canvas.clipPath(getPath(canvas, paint));
-
- Path clipPath = getClipPath(canvas, paint);
- if (clipPath != null) {
- canvas.clipPath(clipPath);
- }
-
- Paint alphaPaint = new Paint();
- alphaPaint.setAlpha((int) (opacity * 255));
- canvas.drawBitmap(bitmap, null, vbRect, alphaPaint);
- mCTM.mapRect(vbRect);
- this.setClientRect(vbRect);
- }
-
- private void tryRenderFromBitmapCache(ImagePipeline imagePipeline, ImageRequest request, Canvas canvas, Paint paint, float opacity) {
- final DataSource> dataSource
- = imagePipeline.fetchImageFromBitmapCache(request, mContext);
-
- try {
- final CloseableReference imageReference = dataSource.getResult();
- if (imageReference == null) {
- return;
- }
-
- try {
- CloseableImage closeableImage = imageReference.get();
- if (!(closeableImage instanceof CloseableBitmap)) {
- return;
- }
-
- CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
- final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
-
- if (bitmap == null) {
- return;
- }
-
- doRender(canvas, paint, bitmap, opacity);
-
- } catch (Exception e) {
- throw new IllegalStateException(e);
- } finally {
- CloseableReference.closeSafely(imageReference);
- }
-
- } catch (Exception e) {
- throw new IllegalStateException(e);
- } finally {
- dataSource.close();
- }
- }
-
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/LineView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/LineView.java
deleted file mode 100644
index ca43530644692..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/LineView.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-@SuppressLint("ViewConstructor")
-class LineView extends RenderableView {
- private SVGLength mX1;
- private SVGLength mY1;
- private SVGLength mX2;
- private SVGLength mY2;
-
- public LineView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "x1")
- public void setX1(Dynamic x1) {
- mX1 = SVGLength.from(x1);
- invalidate();
- }
-
- @ReactProp(name = "y1")
- public void setY1(Dynamic y1) {
- mY1 = SVGLength.from(y1);
- invalidate();
- }
-
- @ReactProp(name = "x2")
- public void setX2(Dynamic x2) {
- mX2 = SVGLength.from(x2);
- invalidate();
- }
-
- @ReactProp(name = "y2")
- public void setY2(Dynamic y2) {
- mY2 = SVGLength.from(y2);
- invalidate();
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- Path path = new Path();
- double x1 = relativeOnWidth(mX1);
- double y1 = relativeOnHeight(mY1);
- double x2 = relativeOnWidth(mX2);
- double y2 = relativeOnHeight(mY2);
-
- path.moveTo((float) x1, (float) y1);
- path.lineTo((float) x2, (float) y2);
- return path;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/LinearGradientView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/LinearGradientView.java
deleted file mode 100644
index b1f7edfaba55e..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/LinearGradientView.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Matrix;
-
-import com.facebook.common.logging.FLog;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-import javax.annotation.Nullable;
-
-@SuppressLint("ViewConstructor")
-class LinearGradientView extends DefinitionView {
-
- private SVGLength mX1;
- private SVGLength mY1;
- private SVGLength mX2;
- private SVGLength mY2;
- private ReadableArray mGradient;
- private Brush.BrushUnits mGradientUnits;
-
- private static final float[] sRawMatrix = new float[]{
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
- };
- private Matrix mMatrix = null;
-
- public LinearGradientView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "x1")
- public void setX1(Dynamic x1) {
- mX1 = SVGLength.from(x1);
- invalidate();
- }
-
- @ReactProp(name = "y1")
- public void setY1(Dynamic y1) {
- mY1 = SVGLength.from(y1);
- invalidate();
- }
-
- @ReactProp(name = "x2")
- public void setX2(Dynamic x2) {
- mX2 = SVGLength.from(x2);
- invalidate();
- }
-
- @ReactProp(name = "y2")
- public void setY2(Dynamic y2) {
- mY2 = SVGLength.from(y2);
- invalidate();
- }
-
- @ReactProp(name = "gradient")
- public void setGradient(ReadableArray gradient) {
- mGradient = gradient;
- invalidate();
- }
-
- @ReactProp(name = "gradientUnits")
- public void setGradientUnits(int gradientUnits) {
- switch (gradientUnits) {
- case 0:
- mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
- break;
- case 1:
- mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
- break;
- }
- invalidate();
- }
-
- @ReactProp(name = "gradientTransform")
- public void setGradientTransform(@Nullable ReadableArray matrixArray) {
- if (matrixArray != null) {
- int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
- if (matrixSize == 6) {
- if (mMatrix == null) {
- mMatrix = new Matrix();
- }
- mMatrix.setValues(sRawMatrix);
- } else if (matrixSize != -1) {
- FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
- }
- } else {
- mMatrix = null;
- }
-
- invalidate();
- }
-
- @Override
- void saveDefinition() {
- if (mName != null) {
- SVGLength[] points = new SVGLength[]{mX1, mY1, mX2, mY2};
- Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits);
- brush.setGradientColors(mGradient);
- if (mMatrix != null) {
- brush.setGradientTransform(mMatrix);
- }
-
- SvgView svg = getSvgView();
- if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
- brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
- }
-
- svg.defineBrush(brush, mName);
- }
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/MarkerView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/MarkerView.java
deleted file mode 100644
index a7d4656bc0eb2..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/MarkerView.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.view.View;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-@SuppressLint("ViewConstructor")
-class MarkerView extends GroupView {
-
- private SVGLength mRefX;
- private SVGLength mRefY;
- private SVGLength mMarkerWidth;
- private SVGLength mMarkerHeight;
- private String mMarkerUnits;
- private String mOrient;
-
- private float mMinX;
- private float mMinY;
- private float mVbWidth;
- private float mVbHeight;
- String mAlign;
- int mMeetOrSlice;
-
- Matrix markerTransform = new Matrix();
-
- public MarkerView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "refX")
- public void setRefX(Dynamic refX) {
- mRefX = SVGLength.from(refX);
- invalidate();
- }
-
- @ReactProp(name = "refY")
- public void setRefY(Dynamic refY) {
- mRefY = SVGLength.from(refY);
- invalidate();
- }
-
- @ReactProp(name = "markerWidth")
- public void setMarkerWidth(Dynamic markerWidth) {
- mMarkerWidth = SVGLength.from(markerWidth);
- invalidate();
- }
-
- @ReactProp(name = "markerHeight")
- public void setMarkerHeight(Dynamic markerHeight) {
- mMarkerHeight = SVGLength.from(markerHeight);
- invalidate();
- }
-
- @ReactProp(name = "markerUnits")
- public void setMarkerUnits(String markerUnits) {
- mMarkerUnits = markerUnits;
- invalidate();
- }
-
- @ReactProp(name = "orient")
- public void setOrient(String orient) {
- mOrient = orient;
- invalidate();
- }
-
- @ReactProp(name = "minX")
- public void setMinX(float minX) {
- mMinX = minX;
- invalidate();
- }
-
- @ReactProp(name = "minY")
- public void setMinY(float minY) {
- mMinY = minY;
- invalidate();
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(float vbWidth) {
- mVbWidth = vbWidth;
- invalidate();
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(float vbHeight) {
- mVbHeight = vbHeight;
- invalidate();
- }
-
- @ReactProp(name = "align")
- public void setAlign(String align) {
- mAlign = align;
- invalidate();
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(int meetOrSlice) {
- mMeetOrSlice = meetOrSlice;
- invalidate();
- }
-
- @Override
- void saveDefinition() {
- if (mName != null) {
- SvgView svg = getSvgView();
- svg.defineMarker(this, mName);
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof VirtualView) {
- ((VirtualView)node).saveDefinition();
- }
- }
- }
- }
-
- void renderMarker(Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) {
- int count = saveAndSetupCanvas(canvas, mCTM);
-
- markerTransform.reset();
- Point origin = position.origin;
- markerTransform.setTranslate((float)origin.x * mScale, (float)origin.y * mScale);
-
- double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
- float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle);
- markerTransform.preRotate(degrees);
-
- boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
- if (useStrokeWidth) {
- markerTransform.preScale(strokeWidth, strokeWidth);
- }
-
- double width = relativeOnWidth(mMarkerWidth) / mScale;
- double height = relativeOnHeight(mMarkerHeight) / mScale;
- RectF eRect = new RectF(0, 0, (float)width, (float)height);
- if (mAlign != null) {
- RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
- Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
- float[] values = new float[9];
- viewBoxMatrix.getValues(values);
- markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
- }
-
- double x = relativeOnWidth(mRefX);
- double y = relativeOnHeight(mRefY);
- markerTransform.preTranslate((float)-x, (float)-y);
-
- canvas.concat(markerTransform);
-
- drawGroup(canvas, paint, opacity);
-
- restoreCanvas(canvas, count);
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/MaskView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/MaskView.java
deleted file mode 100644
index 0ffe8e61e89ef..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/MaskView.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Matrix;
-
-import com.facebook.common.logging.FLog;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-import javax.annotation.Nullable;
-
-@SuppressLint("ViewConstructor")
-class MaskView extends GroupView {
-
- SVGLength mX;
- SVGLength mY;
- SVGLength mW;
- SVGLength mH;
-
- // TODO implement proper support for units
- @SuppressWarnings({"FieldCanBeLocal", "unused"})
- private Brush.BrushUnits mMaskUnits;
- @SuppressWarnings({"FieldCanBeLocal", "unused"})
- private Brush.BrushUnits mMaskContentUnits;
-
- private static final float[] sRawMatrix = new float[]{
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
- };
- private Matrix mMatrix = null;
-
- public MaskView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "x")
- public void setX(Dynamic x) {
- mX = SVGLength.from(x);
- invalidate();
- }
-
- @ReactProp(name = "y")
- public void setY(Dynamic y) {
- mY = SVGLength.from(y);
- invalidate();
- }
-
- @ReactProp(name = "width")
- public void setWidth(Dynamic width) {
- mW = SVGLength.from(width);
- invalidate();
- }
-
- @ReactProp(name = "height")
- public void setHeight(Dynamic height) {
- mH = SVGLength.from(height);
- invalidate();
- }
-
- @ReactProp(name = "maskUnits")
- public void setMaskUnits(int maskUnits) {
- switch (maskUnits) {
- case 0:
- mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
- break;
- case 1:
- mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
- break;
- }
- invalidate();
- }
-
- @ReactProp(name = "maskContentUnits")
- public void setMaskContentUnits(int maskContentUnits) {
- switch (maskContentUnits) {
- case 0:
- mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
- break;
- case 1:
- mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
- break;
- }
- invalidate();
- }
-
- @ReactProp(name = "maskTransform")
- public void setMaskTransform(@Nullable ReadableArray matrixArray) {
- if (matrixArray != null) {
- int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
- if (matrixSize == 6) {
- if (mMatrix == null) {
- mMatrix = new Matrix();
- }
- mMatrix.setValues(sRawMatrix);
- } else if (matrixSize != -1) {
- FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
- }
- } else {
- mMatrix = null;
- }
-
- invalidate();
- }
-
- @Override
- void saveDefinition() {
- if (mName != null) {
- SvgView svg = getSvgView();
- svg.defineMask(this, mName);
- }
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PathParser.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PathParser.java
deleted file mode 100644
index 4df1768126f70..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PathParser.java
+++ /dev/null
@@ -1,610 +0,0 @@
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.graphics.Path;
-import android.graphics.RectF;
-
-import java.util.ArrayList;
-
-class PathElement {
- ElementType type;
- Point[] points;
- PathElement(ElementType type, Point[] points) {
- this.type = type;
- this.points = points;
- }
-}
-
-class PathParser {
- static float mScale;
-
- private static int i;
- private static int l;
- private static String s;
- private static Path mPath;
- static ArrayList elements;
-
- private static float mPenX;
- private static float mPenY;
- private static float mPivotX;
- private static float mPivotY;
- private static float mPenDownX;
- private static float mPenDownY;
- private static boolean mPenDown;
-
- static Path parse(String d) {
- elements = new ArrayList<>();
- mPath = new Path();
- if(d == null){
- return mPath;
- }
- char prev_cmd = ' ';
- l = d.length();
- s = d;
- i = 0;
-
- mPenX = 0f;
- mPenY = 0f;
- mPivotX = 0f;
- mPivotY = 0f;
- mPenDownX = 0f;
- mPenDownY = 0f;
- mPenDown = false;
-
- while (i < l) {
- skip_spaces();
-
- if (i >= l) {
- break;
- }
-
- boolean has_prev_cmd = prev_cmd != ' ';
- char first_char = s.charAt(i);
-
- if (!has_prev_cmd && first_char != 'M' && first_char != 'm') {
- // The first segment must be a MoveTo.
- throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
- }
-
- // TODO: simplify
- boolean is_implicit_move_to;
- char cmd;
- if (is_cmd(first_char)) {
- is_implicit_move_to = false;
- cmd = first_char;
- i += 1;
- } else if (is_number_start(first_char) && has_prev_cmd) {
- if (prev_cmd == 'Z' || prev_cmd == 'z') {
- // ClosePath cannot be followed by a number.
- throw new Error(String.format("Unexpected number after 'z' (s=%s)", s));
- }
-
- if (prev_cmd == 'M' || prev_cmd == 'm') {
- // 'If a moveto is followed by multiple pairs of coordinates,
- // the subsequent pairs are treated as implicit lineto commands.'
- // So we parse them as LineTo.
- is_implicit_move_to = true;
- if (is_absolute(prev_cmd)) {
- cmd = 'L';
- } else {
- cmd = 'l';
- }
- } else {
- is_implicit_move_to = false;
- cmd = prev_cmd;
- }
- } else {
- throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
- }
-
- boolean absolute = is_absolute(cmd);
- switch (cmd) {
- case 'm': {
- move(parse_list_number(), parse_list_number());
- break;
- }
- case 'M': {
- moveTo(parse_list_number(), parse_list_number());
- break;
- }
- case 'l': {
- line(parse_list_number(), parse_list_number());
- break;
- }
- case 'L': {
- lineTo(parse_list_number(), parse_list_number());
- break;
- }
- case 'h': {
- line(parse_list_number(), 0);
- break;
- }
- case 'H': {
- lineTo(parse_list_number(), mPenY);
- break;
- }
- case 'v': {
- line(0, parse_list_number());
- break;
- }
- case 'V': {
- lineTo(mPenX, parse_list_number());
- break;
- }
- case 'c': {
- curve(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
- break;
- }
- case 'C': {
- curveTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
- break;
- }
- case 's': {
- smoothCurve(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
- break;
- }
- case 'S': {
- smoothCurveTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
- break;
- }
- case 'q': {
- quadraticBezierCurve(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
- break;
- }
- case 'Q': {
- quadraticBezierCurveTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
- break;
- }
- case 't': {
- smoothQuadraticBezierCurve(parse_list_number(), parse_list_number());
- break;
- }
- case 'T': {
- smoothQuadraticBezierCurveTo(parse_list_number(), parse_list_number());
- break;
- }
- case 'a': {
- arc(parse_list_number(), parse_list_number(), parse_list_number(), parse_flag(), parse_flag(), parse_list_number(), parse_list_number());
- break;
- }
- case 'A': {
- arcTo(parse_list_number(), parse_list_number(), parse_list_number(), parse_flag(), parse_flag(), parse_list_number(), parse_list_number());
- break;
- }
- case 'z':
- case 'Z': {
- close();
- break;
- }
- default: {
- throw new Error(String.format("Unexpected comand '%c' (s=%s)", cmd, s));
- }
- }
-
-
- if (is_implicit_move_to) {
- if (absolute) {
- prev_cmd = 'M';
- } else {
- prev_cmd = 'm';
- }
- } else {
- prev_cmd = cmd;
- }
-
- }
-
- return mPath;
- }
-
- private static void move(float x, float y) {
- moveTo(x + mPenX, y + mPenY);
- }
-
- private static void moveTo(float x, float y) {
- //FLog.w(ReactConstants.TAG, "move x: " + x + " y: " + y);
- mPenDownX = mPivotX = mPenX = x;
- mPenDownY = mPivotY = mPenY = y;
- mPath.moveTo(x * mScale, y * mScale);
- elements.add(new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[]{new Point(x,y)}));
- }
-
- private static void line(float x, float y) {
- lineTo(x + mPenX, y + mPenY);
- }
-
- private static void lineTo(float x, float y) {
- //FLog.w(ReactConstants.TAG, "line x: " + x + " y: " + y);
- setPenDown();
- mPivotX = mPenX = x;
- mPivotY = mPenY = y;
- mPath.lineTo(x * mScale, y * mScale);
- elements.add(new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[]{new Point(x,y)}));
- }
-
- private static void curve(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
- curveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY, ex + mPenX, ey + mPenY);
- }
-
- private static void curveTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
- //FLog.w(ReactConstants.TAG, "curve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: " + ey);
- mPivotX = c2x;
- mPivotY = c2y;
- cubicTo(c1x, c1y, c2x, c2y, ex, ey);
- }
-
- private static void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
- setPenDown();
- mPenX = ex;
- mPenY = ey;
- mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale);
- elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)}));
- }
-
- private static void smoothCurve(float c1x, float c1y, float ex, float ey) {
- smoothCurveTo(c1x + mPenX, c1y + mPenY, ex + mPenX, ey + mPenY);
- }
-
- private static void smoothCurveTo(float c1x, float c1y, float ex, float ey) {
- //FLog.w(ReactConstants.TAG, "smoothcurve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: " + ey);
- float c2x = c1x;
- float c2y = c1y;
- c1x = (mPenX * 2) - mPivotX;
- c1y = (mPenY * 2) - mPivotY;
- mPivotX = c2x;
- mPivotY = c2y;
- cubicTo(c1x, c1y, c2x, c2y, ex, ey);
- }
-
- private static void quadraticBezierCurve(float c1x, float c1y, float c2x, float c2y) {
- quadraticBezierCurveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY);
- }
-
- private static void quadraticBezierCurveTo(float c1x, float c1y, float c2x, float c2y) {
- //FLog.w(ReactConstants.TAG, "quad c1x: " + c1x + " c1y: " + c1y + "c2x: " + c2x + " c2y: " + c2y);
- mPivotX = c1x;
- mPivotY = c1y;
- float ex = c2x;
- float ey = c2y;
- c2x = (ex + c1x * 2) / 3;
- c2y = (ey + c1y * 2) / 3;
- c1x = (mPenX + c1x * 2) / 3;
- c1y = (mPenY + c1y * 2) / 3;
- cubicTo(c1x, c1y, c2x, c2y, ex, ey);
- }
-
- private static void smoothQuadraticBezierCurve(float c1x, float c1y) {
- smoothQuadraticBezierCurveTo(c1x + mPenX, c1y + mPenY);
- }
-
- private static void smoothQuadraticBezierCurveTo(float c1x, float c1y) {
- //FLog.w(ReactConstants.TAG, "smoothquad c1x: " + c1x + " c1y: " + c1y);
- float c2x = c1x;
- float c2y = c1y;
- c1x = (mPenX * 2) - mPivotX;
- c1y = (mPenY * 2) - mPivotY;
- quadraticBezierCurveTo(c1x, c1y, c2x, c2y);
- }
-
- private static void arc(float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) {
- arcTo(rx, ry, rotation, outer, clockwise, x + mPenX, y + mPenY);
- }
-
- private static void arcTo(float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) {
- //FLog.w(ReactConstants.TAG, "arc rx: " + rx + " ry: " + ry + " rotation: " + rotation + " outer: " + outer + " clockwise: " + clockwise + " x: " + x + " y: " + y);
- float tX = mPenX;
- float tY = mPenY;
-
- ry = Math.abs(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry);
- rx = Math.abs(rx == 0 ? (x - tX) : rx);
-
- if (rx == 0 || ry == 0 || (x == tX && y == tY)) {
- lineTo(x, y);
- return;
- }
-
- float rad = (float) Math.toRadians(rotation);
- float cos = (float) Math.cos(rad);
- float sin = (float) Math.sin(rad);
- x -= tX;
- y -= tY;
-
- // Ellipse Center
- float cx = cos * x / 2 + sin * y / 2;
- float cy = -sin * x / 2 + cos * y / 2;
- float rxry = rx * rx * ry * ry;
- float rycx = ry * ry * cx * cx;
- float rxcy = rx * rx * cy * cy;
- float a = rxry - rxcy - rycx;
-
- if (a < 0) {
- a = (float) Math.sqrt(1 - a / rxry);
- rx *= a;
- ry *= a;
- cx = x / 2;
- cy = y / 2;
- } else {
- a = (float) Math.sqrt(a / (rxcy + rycx));
-
- if (outer == clockwise) {
- a = -a;
- }
- float cxd = -a * cy * rx / ry;
- float cyd = a * cx * ry / rx;
- cx = cos * cxd - sin * cyd + x / 2;
- cy = sin * cxd + cos * cyd + y / 2;
- }
-
- // Rotation + Scale Transform
- float xx = cos / rx;
- float yx = sin / rx;
- float xy = -sin / ry;
- float yy = cos / ry;
-
- // Start and End Angle
- float sa = (float) Math.atan2(xy * -cx + yy * -cy, xx * -cx + yx * -cy);
- float ea = (float) Math.atan2(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy));
-
- cx += tX;
- cy += tY;
- x += tX;
- y += tY;
-
- setPenDown();
-
- mPenX = mPivotX = x;
- mPenY = mPivotY = y;
-
- if (rx != ry || rad != 0f) {
- arcToBezier(cx, cy, rx, ry, sa, ea, clockwise, rad);
- } else {
-
- float start = (float) Math.toDegrees(sa);
- float end = (float) Math.toDegrees(ea);
- float sweep = Math.abs((start - end) % 360);
-
- if (outer) {
- if (sweep < 180) {
- sweep = 360 - sweep;
- }
- } else {
- if (sweep > 180) {
- sweep = 360 - sweep;
- }
- }
-
- if (!clockwise) {
- sweep = -sweep;
- }
-
- RectF oval = new RectF(
- (cx - rx) * mScale,
- (cy - rx) * mScale,
- (cx + rx) * mScale,
- (cy + rx) * mScale);
-
- mPath.arcTo(oval, start, sweep);
- elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(x, y)}));
- }
- }
-
- private static void close() {
- if (mPenDown) {
- mPenX = mPenDownX;
- mPenY = mPenDownY;
- mPenDown = false;
- mPath.close();
- elements.add(new PathElement(ElementType.kCGPathElementCloseSubpath, new Point[]{new Point(mPenX, mPenY)}));
- }
- }
-
- private static void arcToBezier(float cx, float cy, float rx, float ry, float sa, float ea, boolean clockwise, float rad) {
- // Inverse Rotation + Scale Transform
- float cos = (float) Math.cos(rad);
- float sin = (float) Math.sin(rad);
- float xx = cos * rx;
- float yx = -sin * ry;
- float xy = sin * rx;
- float yy = cos * ry;
-
- // Bezier Curve Approximation
- float arc = ea - sa;
- if (arc < 0 && clockwise) {
- arc += Math.PI * 2;
- } else if (arc > 0 && !clockwise) {
- arc -= Math.PI * 2;
- }
-
- int n = (int) Math.ceil(Math.abs(round(arc / (Math.PI / 2))));
-
- float step = arc / n;
- float k = (float) ((4 / 3.0) * Math.tan(step / 4));
-
- float x = (float) Math.cos(sa);
- float y = (float) Math.sin(sa);
-
- for (int i = 0; i < n; i++) {
- float cp1x = x - k * y;
- float cp1y = y + k * x;
-
- sa += step;
- x = (float) Math.cos(sa);
- y = (float) Math.sin(sa);
-
- float cp2x = x + k * y;
- float cp2y = y - k * x;
-
- float c1x = (cx + xx * cp1x + yx * cp1y);
- float c1y = (cy + xy * cp1x + yy * cp1y);
- float c2x = (cx + xx * cp2x + yx * cp2y);
- float c2y = (cy + xy * cp2x + yy * cp2y);
- float ex = (cx + xx * x + yx * y);
- float ey = (cy + xy * x + yy * y);
-
- mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale);
- elements.add(new PathElement(ElementType.kCGPathElementAddCurveToPoint, new Point[]{new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)}));
- }
- }
-
- private static void setPenDown() {
- if (!mPenDown) {
- mPenDownX = mPenX;
- mPenDownY = mPenY;
- mPenDown = true;
- }
- }
-
- private static double round(double val) {
- double multiplier = Math.pow(10, 4);
- return Math.round(val * multiplier) / multiplier;
- }
-
- private static void skip_spaces() {
- while (i < l && Character.isWhitespace(s.charAt(i))) i++;
- }
-
- private static boolean is_cmd(char c) {
- switch (c) {
- case 'M':
- case 'm':
- case 'Z':
- case 'z':
- case 'L':
- case 'l':
- case 'H':
- case 'h':
- case 'V':
- case 'v':
- case 'C':
- case 'c':
- case 'S':
- case 's':
- case 'Q':
- case 'q':
- case 'T':
- case 't':
- case 'A':
- case 'a':
- return true;
- }
- return false;
- }
-
- private static boolean is_number_start(char c) {
- return (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+';
- }
-
- private static boolean is_absolute(char c) {
- return Character.isUpperCase(c);
- }
-
- // By the SVG spec 'large-arc' and 'sweep' must contain only one char
- // and can be written without any separators, e.g.: 10 20 30 01 10 20.
- private static boolean parse_flag() {
- skip_spaces();
-
- char c = s.charAt(i);
- switch (c) {
- case '0':
- case '1': {
- i += 1;
- if (i < l && s.charAt(i) == ',') {
- i += 1;
- }
- skip_spaces();
- break;
- }
- default:
- throw new Error(String.format("Unexpected flag '%c' (i=%d, s=%s)", c, i, s));
- }
-
- return c == '1';
- }
-
- private static float parse_list_number() {
- if (i == l) {
- throw new Error(String.format("Unexpected end (s=%s)", s));
- }
-
- float n = parse_number();
- skip_spaces();
- parse_list_separator();
-
- return n;
- }
-
- private static float parse_number() {
- // Strip off leading whitespaces.
- skip_spaces();
-
- if (i == l) {
- throw new Error(String.format("Unexpected end (s=%s)", s));
- }
-
- int start = i;
-
- char c = s.charAt(i);
-
- // Consume sign.
- if (c == '-' || c == '+') {
- i += 1;
- c = s.charAt(i);
- }
-
- // Consume integer.
- if (c >= '0' && c <= '9') {
- skip_digits();
- if (i < l) {
- c = s.charAt(i);
- }
- } else if (c != '.') {
- throw new Error(String.format("Invalid number formating character '%c' (i=%d, s=%s)", c ,i, s));
- }
-
- // Consume fraction.
- if (c == '.') {
- i += 1;
- skip_digits();
- if (i < l) {
- c = s.charAt(i);
- }
- }
-
- if ((c == 'e' || c == 'E') && i + 1 < l) {
- char c2 = s.charAt(i + 1);
- // Check for `em`/`ex`.
- if (c2 != 'm' && c2 != 'x') {
- i += 1;
- c = s.charAt(i);
-
- if (c == '+' || c == '-') {
- i += 1;
- skip_digits();
- } else if (c >= '0' && c <= '9') {
- skip_digits();
- } else {
- throw new Error(String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s));
- }
- }
- }
-
- String num = s.substring(start, i);
- float n = Float.parseFloat(num);
-
- // inf, nan, etc. are an error.
- if (Float.isInfinite(n) || Float.isNaN(n)) {
- throw new Error(String.format("Invalid number '%s' (start=%d, i=%d, s=%s)", num, start, i, s));
- }
-
- return n;
- }
-
- private static void parse_list_separator() {
- if (i < l && s.charAt(i) == ',') {
- i += 1;
- }
- }
-
- private static void skip_digits() {
- while (i < l && Character.isDigit(s.charAt(i))) i++;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PathView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PathView.java
deleted file mode 100644
index 44098a4b1d83b..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PathView.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-@SuppressLint("ViewConstructor")
-class PathView extends RenderableView {
- private Path mPath;
-
- public PathView(ReactContext reactContext) {
- super(reactContext);
- PathParser.mScale = mScale;
- mPath = new Path();
- }
-
- @ReactProp(name = "d")
- public void setD(String d) {
- mPath = PathParser.parse(d);
- elements = PathParser.elements;
- invalidate();
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- return mPath;
- }
-
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PatternView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PatternView.java
deleted file mode 100644
index df284eb99f16d..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PatternView.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-
-import com.facebook.common.logging.FLog;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-import javax.annotation.Nullable;
-
-@SuppressLint("ViewConstructor")
-class PatternView extends GroupView {
-
- private SVGLength mX;
- private SVGLength mY;
- private SVGLength mW;
- private SVGLength mH;
- private Brush.BrushUnits mPatternUnits;
- private Brush.BrushUnits mPatternContentUnits;
-
- private float mMinX;
- private float mMinY;
- private float mVbWidth;
- private float mVbHeight;
- String mAlign;
- int mMeetOrSlice;
-
- private static final float[] sRawMatrix = new float[]{
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
- };
- private Matrix mMatrix = null;
-
- public PatternView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "x")
- public void setX(Dynamic x) {
- mX = SVGLength.from(x);
- invalidate();
- }
-
- @ReactProp(name = "y")
- public void setY(Dynamic y) {
- mY = SVGLength.from(y);
- invalidate();
- }
-
- @ReactProp(name = "width")
- public void setWidth(Dynamic width) {
- mW = SVGLength.from(width);
- invalidate();
- }
-
- @ReactProp(name = "height")
- public void setHeight(Dynamic height) {
- mH = SVGLength.from(height);
- invalidate();
- }
-
- @ReactProp(name = "patternUnits")
- public void setPatternUnits(int patternUnits) {
- switch (patternUnits) {
- case 0:
- mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
- break;
- case 1:
- mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
- break;
- }
- invalidate();
- }
-
- @ReactProp(name = "patternContentUnits")
- public void setPatternContentUnits(int patternContentUnits) {
- switch (patternContentUnits) {
- case 0:
- mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
- break;
- case 1:
- mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
- break;
- }
- invalidate();
- }
-
- @ReactProp(name = "patternTransform")
- public void setPatternTransform(@Nullable ReadableArray matrixArray) {
- if (matrixArray != null) {
- int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
- if (matrixSize == 6) {
- if (mMatrix == null) {
- mMatrix = new Matrix();
- }
- mMatrix.setValues(sRawMatrix);
- } else if (matrixSize != -1) {
- FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
- }
- } else {
- mMatrix = null;
- }
-
- invalidate();
- }
-
- @ReactProp(name = "minX")
- public void setMinX(float minX) {
- mMinX = minX;
- invalidate();
- }
-
- @ReactProp(name = "minY")
- public void setMinY(float minY) {
- mMinY = minY;
- invalidate();
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(float vbWidth) {
- mVbWidth = vbWidth;
- invalidate();
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(float vbHeight) {
- mVbHeight = vbHeight;
- invalidate();
- }
-
- @ReactProp(name = "align")
- public void setAlign(String align) {
- mAlign = align;
- invalidate();
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(int meetOrSlice) {
- mMeetOrSlice = meetOrSlice;
- invalidate();
- }
-
-
- RectF getViewBox() {
- return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
- }
-
- @Override
- void saveDefinition() {
- if (mName != null) {
- SVGLength[] points = new SVGLength[]{mX,mY,mW,mH};
- Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits);
- brush.setContentUnits(mPatternContentUnits);
- brush.setPattern(this);
-
- if (mMatrix != null) {
- brush.setGradientTransform(mMatrix);
- }
-
- SvgView svg = getSvgView();
- if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE || mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
- brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
- }
-
- svg.defineBrush(brush, mName);
- }
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PropHelper.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PropHelper.java
deleted file mode 100644
index e4cb5d90b22d0..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/PropHelper.java
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import com.facebook.react.bridge.ReadableArray;
-
-/**
- * Contains static helper methods for accessing props.
- */
-class PropHelper {
-
- private static final int inputMatrixDataSize = 6;
-
- /**
- * Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}.
- * Writes result to the array passed in {@param into}.
- * This method will write exactly six items to the output array from the input array.
- *
- * If the input array has a different size, then only the size is returned;
- * Does not check output array size. Ensure space for at least six elements.
- *
- * @param value input array
- * @param sRawMatrix output matrix
- * @param mScale current resolution scaling
- * @return size of input array
- */
- static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) {
- int fromSize = value.size();
- if (fromSize != inputMatrixDataSize) {
- return fromSize;
- }
-
- sRawMatrix[0] = (float) value.getDouble(0);
- sRawMatrix[1] = (float) value.getDouble(2);
- sRawMatrix[2] = (float) value.getDouble(4) * mScale;
- sRawMatrix[3] = (float) value.getDouble(1);
- sRawMatrix[4] = (float) value.getDouble(3);
- sRawMatrix[5] = (float) value.getDouble(5) * mScale;
-
- return inputMatrixDataSize;
- }
-
- /**
- * Converts length string into px / user units
- * in the current user coordinate system
- *
- * @param length length string
- * @param relative relative size for percentages
- * @param scale scaling parameter
- * @param fontSize current font size
- * @return value in the current user coordinate system
- */
- static double fromRelative(String length, double relative, double scale, double fontSize) {
- /*
- TODO list
-
- unit relative to
- em font size of the element
- ex x-height of the element’s font
- ch width of the "0" (ZERO, U+0030) glyph in the element’s font
- rem font size of the root element
- vw 1% of viewport’s width
- vh 1% of viewport’s height
- vmin 1% of viewport’s smaller dimension
- vmax 1% of viewport’s larger dimension
-
- relative-size [ larger | smaller ]
- absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
-
- https://www.w3.org/TR/css3-values/#relative-lengths
- https://www.w3.org/TR/css3-values/#absolute-lengths
- https://drafts.csswg.org/css-cascade-4/#computed-value
- https://drafts.csswg.org/css-fonts-3/#propdef-font-size
- https://drafts.csswg.org/css2/fonts.html#propdef-font-size
- */
- length = length.trim();
- int stringLength = length.length();
- int percentIndex = stringLength - 1;
- if (stringLength == 0 || length.equals("normal")) {
- return 0d;
- } else if (length.codePointAt(percentIndex) == '%') {
- return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative;
- } else {
- int twoLetterUnitIndex = stringLength - 2;
- if (twoLetterUnitIndex > 0) {
- String lastTwo = length.substring(twoLetterUnitIndex);
- int end = twoLetterUnitIndex;
- double unit = 1;
-
- switch (lastTwo) {
- case "px":
- break;
-
- case "em":
- unit = fontSize;
- break;
-
- /*
- "1pt" equals "1.25px" (and therefore 1.25 user units)
- "1pc" equals "15px" (and therefore 15 user units)
- "1mm" would be "3.543307px" (3.543307 user units)
- "1cm" equals "35.43307px" (and therefore 35.43307 user units)
- "1in" equals "90px" (and therefore 90 user units)
- */
-
- case "pt":
- unit = 1.25d;
- break;
-
- case "pc":
- unit = 15;
- break;
-
- case "mm":
- unit = 3.543307d;
- break;
-
- case "cm":
- unit = 35.43307d;
- break;
-
- case "in":
- unit = 90;
- break;
-
- default:
- end = stringLength;
- }
-
- return Double.valueOf(length.substring(0, end)) * unit * scale;
- } else {
- return Double.valueOf(length) * scale;
- }
- }
- }
- /**
- * Converts SVGLength into px / user units
- * in the current user coordinate system
- *
- * @param length length string
- * @param relative relative size for percentages
- * @param offset offset for all units
- * @param scale scaling parameter
- * @param fontSize current font size
- * @return value in the current user coordinate system
- */
- static double fromRelative(SVGLength length, double relative, double offset, double scale, double fontSize) {
- /*
- TODO list
-
- unit relative to
- em font size of the element
- ex x-height of the element’s font
- ch width of the "0" (ZERO, U+0030) glyph in the element’s font
- rem font size of the root element
- vw 1% of viewport’s width
- vh 1% of viewport’s height
- vmin 1% of viewport’s smaller dimension
- vmax 1% of viewport’s larger dimension
-
- relative-size [ larger | smaller ]
- absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
-
- https://www.w3.org/TR/css3-values/#relative-lengths
- https://www.w3.org/TR/css3-values/#absolute-lengths
- https://drafts.csswg.org/css-cascade-4/#computed-value
- https://drafts.csswg.org/css-fonts-3/#propdef-font-size
- https://drafts.csswg.org/css2/fonts.html#propdef-font-size
- */
- if (length == null) {
- return offset;
- }
- SVGLength.UnitType unitType = length.unit;
- double value = length.value;
- double unit = 1;
- switch (unitType) {
- case NUMBER:
- case PX:
- break;
-
- case PERCENTAGE:
- return value / 100 * relative + offset;
-
- case EMS:
- unit = fontSize;
- break;
- case EXS:
- unit = fontSize / 2;
- break;
-
- case CM:
- unit = 35.43307;
- break;
- case MM:
- unit = 3.543307;
- break;
- case IN:
- unit = 90;
- break;
- case PT:
- unit = 1.25;
- break;
- case PC:
- unit = 15;
- break;
-
- default:
- case UNKNOWN:
- return value * scale + offset;
- }
- return value * unit * scale + offset;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RNSVGMarkerPosition.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RNSVGMarkerPosition.java
deleted file mode 100644
index 3f9da040ffdce..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RNSVGMarkerPosition.java
+++ /dev/null
@@ -1,180 +0,0 @@
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import java.util.ArrayList;
-
-enum RNSVGMarkerType {
- kStartMarker,
- kMidMarker,
- kEndMarker
-}
-
-enum ElementType {
- kCGPathElementAddCurveToPoint,
- kCGPathElementAddQuadCurveToPoint,
- kCGPathElementMoveToPoint,
- kCGPathElementAddLineToPoint,
- kCGPathElementCloseSubpath
-}
-
-class Point {
- double x;
- double y;
- Point(double x, double y){
- this.x = x;
- this.y = y;
- }
-}
-
-class SegmentData {
- Point start_tangent; // Tangent in the start point of the segment.
- Point end_tangent; // Tangent in the end point of the segment.
- Point position; // The end point of the segment.
-}
-
-class RNSVGMarkerPosition {
-
- static private ArrayList positions_;
- static private int element_index_;
- static private Point origin_;
- static private Point subpath_start_;
- static private Point in_slope_;
- static private Point out_slope_;
- @SuppressWarnings("unused")
- static private boolean auto_start_reverse_; // TODO
-
- RNSVGMarkerType type;
- Point origin;
- double angle;
-
- private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) {
- this.type = type;
- this.origin = origin;
- this.angle = angle;
- }
-
- static ArrayList fromPath(ArrayList elements) {
- positions_ = new ArrayList<>();
- element_index_ = 0;
- origin_ = new Point(0, 0);
- subpath_start_ = new Point(0, 0);
- for (PathElement e : elements) {
- UpdateFromPathElement(e);
- }
- PathIsDone();
- return positions_;
- }
-
- private static void PathIsDone() {
- double angle = CurrentAngle(RNSVGMarkerType.kEndMarker);
- positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle));
- }
-
- private static double BisectingAngle(double in_angle, double out_angle) {
- // WK193015: Prevent bugs due to angles being non-continuous.
- if (Math.abs(in_angle - out_angle) > 180)
- in_angle += 360;
- return (in_angle + out_angle) / 2;
- }
-
- private static double rad2deg(double rad) {
- double RNSVG_radToDeg = 180 / Math.PI;
- return rad * RNSVG_radToDeg;
- }
-
- private static double SlopeAngleRadians(Point p) {
- return Math.atan2(p.y, p.x);
- }
-
- private static double CurrentAngle(RNSVGMarkerType type) {
- // For details of this calculation, see:
- // http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
- double in_angle = rad2deg(SlopeAngleRadians(in_slope_));
- double out_angle = rad2deg(SlopeAngleRadians(out_slope_));
- switch (type) {
- case kStartMarker:
- if (auto_start_reverse_)
- out_angle += 180;
- return out_angle;
- case kMidMarker:
- return BisectingAngle(in_angle, out_angle);
- case kEndMarker:
- return in_angle;
- }
- return 0;
- }
-
- private static Point subtract(Point p1, Point p2) {
- return new Point(p2.x - p1.x, p2.y - p1.y);
- }
-
- private static boolean isZero(Point p) {
- return p.x == 0 && p.y == 0;
- }
-
- private static void ComputeQuadTangents(SegmentData data,
- Point start,
- Point control,
- Point end) {
- data.start_tangent = subtract(control, start);
- data.end_tangent = subtract(end, control);
- if (isZero(data.start_tangent))
- data.start_tangent = data.end_tangent;
- else if (isZero(data.end_tangent))
- data.end_tangent = data.start_tangent;
- }
-
- private static SegmentData ExtractPathElementFeatures(PathElement element) {
- SegmentData data = new SegmentData();
- Point[] points = element.points;
- switch (element.type) {
- case kCGPathElementAddCurveToPoint:
- data.position = points[2];
- data.start_tangent = subtract(points[0], origin_);
- data.end_tangent = subtract(points[2], points[1]);
- if (isZero(data.start_tangent))
- ComputeQuadTangents(data, points[0], points[1], points[2]);
- else if (isZero(data.end_tangent))
- ComputeQuadTangents(data, origin_, points[0], points[1]);
- break;
- case kCGPathElementAddQuadCurveToPoint:
- data.position = points[1];
- ComputeQuadTangents(data, origin_, points[0], points[1]);
- break;
- case kCGPathElementMoveToPoint:
- case kCGPathElementAddLineToPoint:
- data.position = points[0];
- data.start_tangent = subtract(data.position, origin_);
- data.end_tangent = subtract(data.position, origin_);
- break;
- case kCGPathElementCloseSubpath:
- data.position = subpath_start_;
- data.start_tangent = subtract(data.position, origin_);
- data.end_tangent = subtract(data.position, origin_);
- break;
- }
- return data;
- }
-
- private static void UpdateFromPathElement(PathElement element) {
- SegmentData segment_data = ExtractPathElementFeatures(element);
- // First update the outgoing slope for the previous element.
- out_slope_ = segment_data.start_tangent;
- // Record the marker for the previous element.
- if (element_index_ > 0) {
- RNSVGMarkerType marker_type =
- element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker;
- double angle = CurrentAngle(marker_type);
- positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle));
- }
- // Update the incoming slope for this marker position.
- in_slope_ = segment_data.end_tangent;
- // Update marker position.
- origin_ = segment_data.position;
- // If this is a 'move to' segment, save the point for use with 'close'.
- if (element.type == ElementType.kCGPathElementMoveToPoint)
- subpath_start_ = element.points[0];
- else if (element.type == ElementType.kCGPathElementCloseSubpath)
- subpath_start_ = new Point(0, 0);
- ++element_index_;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RNSVGRenderableManager.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RNSVGRenderableManager.java
deleted file mode 100644
index 776c1c6ac939e..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RNSVGRenderableManager.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.PathMeasure;
-import android.graphics.RectF;
-import android.graphics.Region;
-
-import com.facebook.react.bridge.Arguments;
-import com.facebook.react.bridge.Promise;
-import com.facebook.react.bridge.ReactApplicationContext;
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
-import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.WritableMap;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-import javax.annotation.Nonnull;
-
-import static com.facebook.react.common.StandardCharsets.UTF_8;
-
-class RNSVGRenderableManager extends ReactContextBaseJavaModule {
- RNSVGRenderableManager(ReactApplicationContext reactContext) {
- super(reactContext);
- }
-
- @Nonnull
- @Override
- public String getName() {
- return "RNSVGRenderableManager";
- }
-
- @SuppressWarnings("unused")
- @ReactMethod(isBlockingSynchronousMethod = true)
- public boolean isPointInFill(int tag, ReadableMap options) {
- RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
- if (svg == null) {
- return false;
- }
-
- float scale = svg.mScale;
- float x = (float) options.getDouble("x") * scale;
- float y = (float) options.getDouble("y") * scale;
-
- int i = svg.hitTest(new float[]{x, y});
- return i != -1;
- }
-
- @SuppressWarnings("unused")
- @ReactMethod(isBlockingSynchronousMethod = true)
- public boolean isPointInStroke(int tag, ReadableMap options) {
- RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
- if (svg == null) {
- return false;
- }
-
- try {
- svg.getPath(null, null);
- } catch (NullPointerException e) {
- svg.invalidate();
- return false;
- }
-
- svg.initBounds();
-
- float scale = svg.mScale;
- int x = (int) (options.getDouble("x") * scale);
- int y = (int) (options.getDouble("y") * scale);
-
- Region strokeRegion = svg.mStrokeRegion;
- return strokeRegion != null && strokeRegion.contains(x, y);
- }
-
- @SuppressWarnings("unused")
- @ReactMethod(isBlockingSynchronousMethod = true)
- public float getTotalLength(int tag) {
- RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
- if (svg == null) {
- return 0;
- }
-
- Path path;
-
- try {
- path = svg.getPath(null, null);
- } catch (NullPointerException e) {
- svg.invalidate();
- return -1;
- }
-
- PathMeasure pm = new PathMeasure(path, false);
- return pm.getLength() / svg.mScale;
- }
-
- @SuppressWarnings("unused")
- @ReactMethod(isBlockingSynchronousMethod = true)
- public WritableMap getPointAtLength(int tag, ReadableMap options) {
- RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
- if (svg == null) {
- return Arguments.createMap();
- }
-
- Path path;
-
- try {
- path = svg.getPath(null, null);
- } catch (NullPointerException e) {
- svg.invalidate();
- return Arguments.createMap();
- }
-
- PathMeasure pm = new PathMeasure(path, false);
- float length = (float) options.getDouble("length");
- float scale = svg.mScale;
-
- float[] pos = new float[2];
- float[] tan = new float[2];
- float distance = Math.max(0, Math.min(length, pm.getLength()));
- pm.getPosTan(distance, pos, tan);
-
- double angle = Math.atan2(tan[1], tan[0]);
- WritableMap result = Arguments.createMap();
- result.putDouble("x", pos[0] / scale);
- result.putDouble("y", pos[1] / scale);
- result.putDouble("angle", angle);
- return result;
- }
-
- @SuppressWarnings("unused")
- @ReactMethod(isBlockingSynchronousMethod = true)
- public WritableMap getBBox(int tag, ReadableMap options) {
- RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
- if (svg == null) {
- return Arguments.createMap();
- }
-
- boolean fill = options.getBoolean("fill");
- boolean stroke = options.getBoolean("stroke");
- boolean markers = options.getBoolean("markers");
- boolean clipped = options.getBoolean("clipped");
-
- try {
- svg.getPath(null, null);
- } catch (NullPointerException e) {
- svg.invalidate();
- return Arguments.createMap();
- }
-
- float scale = svg.mScale;
- svg.initBounds();
-
- RectF bounds = new RectF();
- RectF fillBounds = svg.mFillBounds;
- RectF strokeBounds = svg.mStrokeBounds;
- RectF markerBounds = svg.mMarkerBounds;
- RectF clipBounds = svg.mClipBounds;
-
- if (fill && fillBounds != null) {
- bounds.union(fillBounds);
- }
- if (stroke && strokeBounds != null) {
- bounds.union(strokeBounds);
- }
- if (markers && markerBounds != null) {
- bounds.union(markerBounds);
- }
- if (clipped && clipBounds != null) {
- bounds.intersect(clipBounds);
- }
-
- WritableMap result = Arguments.createMap();
- result.putDouble("x", bounds.left / scale);
- result.putDouble("y", bounds.top / scale);
- result.putDouble("width", bounds.width() / scale);
- result.putDouble("height", bounds.height() / scale);
- return result;
- }
-
- @SuppressWarnings("unused")
- @ReactMethod(isBlockingSynchronousMethod = true)
- public WritableMap getCTM(int tag) {
- RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
- if (svg == null) {
- return Arguments.createMap();
- }
-
- float scale = svg.mScale;
- Matrix ctm = new Matrix(svg.mCTM);
- Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix;
- ctm.preConcat(invViewBoxMatrix);
-
- float[] values = new float[9];
- ctm.getValues(values);
-
- WritableMap result = Arguments.createMap();
- result.putDouble("a", values[Matrix.MSCALE_X]);
- result.putDouble("b", values[Matrix.MSKEW_Y]);
- result.putDouble("c", values[Matrix.MSKEW_X]);
- result.putDouble("d", values[Matrix.MSCALE_Y]);
- result.putDouble("e", values[Matrix.MTRANS_X] / scale);
- result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
- return result;
- }
-
- @SuppressWarnings("unused")
- @ReactMethod(isBlockingSynchronousMethod = true)
- public WritableMap getScreenCTM(int tag) {
- RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
- if (svg == null) {
- return Arguments.createMap();
- }
-
- float[] values = new float[9];
- svg.mCTM.getValues(values);
- float scale = svg.mScale;
-
- WritableMap result = Arguments.createMap();
- result.putDouble("a", values[Matrix.MSCALE_X]);
- result.putDouble("b", values[Matrix.MSKEW_Y]);
- result.putDouble("c", values[Matrix.MSKEW_X]);
- result.putDouble("d", values[Matrix.MSCALE_Y]);
- result.putDouble("e", values[Matrix.MTRANS_X] / scale);
- result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
- return result;
- }
-
- @ReactMethod
- public void getRawResource(String name, Promise promise) {
- try {
- ReactApplicationContext context = getReactApplicationContext();
- Resources resources = context.getResources();
- String packageName = context.getPackageName();
- int id = resources.getIdentifier(name, "raw", packageName);
- InputStream stream = resources.openRawResource(id);
- try {
- InputStreamReader reader = new InputStreamReader(stream, UTF_8);
- char[] buffer = new char[DEFAULT_BUFFER_SIZE];
- StringBuilder builder = new StringBuilder();
- int n;
- while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) {
- builder.append(buffer, 0, n);
- }
- String result = builder.toString();
- promise.resolve(result);
- } finally {
- try {
- stream.close();
- } catch (IOException ioe) {
- // ignore
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- promise.reject(e);
- }
- }
-
- private static final int EOF = -1;
- private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RadialGradientView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RadialGradientView.java
deleted file mode 100644
index 1e111cb2e65fb..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RadialGradientView.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Matrix;
-
-import com.facebook.common.logging.FLog;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-import javax.annotation.Nullable;
-
-@SuppressLint("ViewConstructor")
-class RadialGradientView extends DefinitionView {
- private SVGLength mFx;
- private SVGLength mFy;
- private SVGLength mRx;
- private SVGLength mRy;
- private SVGLength mCx;
- private SVGLength mCy;
- private ReadableArray mGradient;
- private Brush.BrushUnits mGradientUnits;
-
- private static final float[] sRawMatrix = new float[]{
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
- };
- private Matrix mMatrix = null;
-
- public RadialGradientView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "fx")
- public void setFx(Dynamic fx) {
- mFx = SVGLength.from(fx);
- invalidate();
- }
-
- @ReactProp(name = "fy")
- public void setFy(Dynamic fy) {
- mFy = SVGLength.from(fy);
- invalidate();
- }
-
- @ReactProp(name = "rx")
- public void setRx(Dynamic rx) {
- mRx = SVGLength.from(rx);
- invalidate();
- }
-
- @ReactProp(name = "ry")
- public void setRy(Dynamic ry) {
- mRy = SVGLength.from(ry);
- invalidate();
- }
-
- @ReactProp(name = "cx")
- public void setCx(Dynamic cx) {
- mCx = SVGLength.from(cx);
- invalidate();
- }
-
- @ReactProp(name = "cy")
- public void setCy(Dynamic cy) {
- mCy = SVGLength.from(cy);
- invalidate();
- }
-
- @ReactProp(name = "gradient")
- public void setGradient(ReadableArray gradient) {
- mGradient = gradient;
- invalidate();
- }
-
- @ReactProp(name = "gradientUnits")
- public void setGradientUnits(int gradientUnits) {
- switch (gradientUnits) {
- case 0:
- mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
- break;
- case 1:
- mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
- break;
- }
- invalidate();
- }
-
- @ReactProp(name = "gradientTransform")
- public void setGradientTransform(@Nullable ReadableArray matrixArray) {
- if (matrixArray != null) {
- int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
- if (matrixSize == 6) {
- if (mMatrix == null) {
- mMatrix = new Matrix();
- }
- mMatrix.setValues(sRawMatrix);
- } else if (matrixSize != -1) {
- FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
- }
- } else {
- mMatrix = null;
- }
-
- invalidate();
- }
-
- @Override
- void saveDefinition() {
- if (mName != null) {
- SVGLength[] points = new SVGLength[]{mFx,mFy,mRx,mRy,mCx,mCy};
- Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits);
- brush.setGradientColors(mGradient);
- if (mMatrix != null) {
- brush.setGradientTransform(mMatrix);
- }
-
- SvgView svg = getSvgView();
- if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
- brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
- }
-
- svg.defineBrush(brush, mName);
- }
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RectView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RectView.java
deleted file mode 100644
index 74eeb3c3e6ea6..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RectView.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.os.Build;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-@SuppressLint("ViewConstructor")
-class RectView extends RenderableView {
- private SVGLength mX;
- private SVGLength mY;
- private SVGLength mW;
- private SVGLength mH;
- private SVGLength mRx;
- private SVGLength mRy;
-
- public RectView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "x")
- public void setX(Dynamic x) {
- mX = SVGLength.from(x);
- invalidate();
- }
-
- @ReactProp(name = "y")
- public void setY(Dynamic y) {
- mY = SVGLength.from(y);
- invalidate();
- }
-
- @ReactProp(name = "width")
- public void setWidth(Dynamic width) {
- mW = SVGLength.from(width);
- invalidate();
- }
-
- @ReactProp(name = "height")
- public void setHeight(Dynamic height) {
- mH = SVGLength.from(height);
- invalidate();
- }
-
- @ReactProp(name = "rx")
- public void setRx(Dynamic rx) {
- mRx = SVGLength.from(rx);
- invalidate();
- }
-
- @ReactProp(name = "ry")
- public void setRy(Dynamic ry) {
- mRy = SVGLength.from(ry);
- invalidate();
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- Path path = new Path();
- double x = relativeOnWidth(mX);
- double y = relativeOnHeight(mY);
- double w = relativeOnWidth(mW);
- double h = relativeOnHeight(mH);
-
- if (mRx != null || mRy != null) {
- double rx = 0d;
- double ry = 0d;
- if (mRx == null) {
- ry = relativeOnHeight(mRy);
- rx = ry;
- } else if (mRy == null) {
- rx = relativeOnWidth(mRx);
- ry = rx;
- } else {
- rx = relativeOnWidth(mRx);
- ry = relativeOnHeight(mRy);
- }
-
- if (rx > w / 2) {
- rx = w / 2;
- }
-
- if (ry > h / 2) {
- ry = h / 2;
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- path.addRoundRect((float) x, (float) y, (float) (x + w), (float) (y + h), (float) rx, (float) ry, Path.Direction.CW);
- } else {
- path.addRoundRect(new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)), (float) rx, (float) ry, Path.Direction.CW);
- }
- } else {
- path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW);
- path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using integers
- }
- return path;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RenderableView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RenderableView.java
deleted file mode 100644
index c46b7bd8347b4..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RenderableView.java
+++ /dev/null
@@ -1,662 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.DashPathEffect;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
-import com.facebook.react.bridge.JavaOnlyArray;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.bridge.ReadableType;
-import com.facebook.react.bridge.ColorPropConverter;
-import com.facebook.react.uimanager.PointerEvents;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.annotation.Nullable;
-
-@SuppressWarnings({"WeakerAccess", "RedundantSuppression"})
-abstract public class RenderableView extends VirtualView {
-
- RenderableView(ReactContext reactContext) {
- super(reactContext);
- setPivotX(0);
- setPivotY(0);
- }
-
- static RenderableView contextElement;
- // strokeLinecap
- private static final int CAP_BUTT = 0;
- static final int CAP_ROUND = 1;
- private static final int CAP_SQUARE = 2;
-
- // strokeLinejoin
- private static final int JOIN_BEVEL = 2;
- private static final int JOIN_MITER = 0;
- static final int JOIN_ROUND = 1;
-
- // fillRule
- private static final int FILL_RULE_EVENODD = 0;
- static final int FILL_RULE_NONZERO = 1;
-
- // vectorEffect
- private static final int VECTOR_EFFECT_DEFAULT = 0;
- private static final int VECTOR_EFFECT_NON_SCALING_STROKE = 1;
- //static final int VECTOR_EFFECT_INHERIT = 2;
- //static final int VECTOR_EFFECT_URI = 3;
-
- /*
- Used in mergeProperties, keep public
- */
-
- public int vectorEffect = VECTOR_EFFECT_DEFAULT;
- public @Nullable ReadableArray stroke;
- public @Nullable SVGLength[] strokeDasharray;
-
- public SVGLength strokeWidth = new SVGLength(1);
- public float strokeOpacity = 1;
- public float strokeMiterlimit = 4;
- public float strokeDashoffset = 0;
-
- public Paint.Cap strokeLinecap = Paint.Cap.BUTT;
- public Paint.Join strokeLinejoin = Paint.Join.MITER;
-
- public @Nullable ReadableArray fill;
- public float fillOpacity = 1;
- public Path.FillType fillRule = Path.FillType.WINDING;
-
- /*
- End merged properties
- */
- private @Nullable ArrayList mLastMergedList;
- private @Nullable ArrayList mOriginProperties;
- private @Nullable ArrayList mPropList;
- private @Nullable ArrayList mAttributeList;
-
- private static final Pattern regex = Pattern.compile("[0-9.-]+");
-
- @Override
- public void setId(int id) {
- super.setId(id);
- RenderableViewManager.setRenderableView(id, this);
- }
-
- @ReactProp(name = "vectorEffect")
- public void setVectorEffect(int vectorEffect) {
- this.vectorEffect = vectorEffect;
- invalidate();
- }
-
- @ReactProp(name = "fill")
- public void setFill(@Nullable Dynamic fill) {
- if (fill == null || fill.isNull()) {
- this.fill = null;
- invalidate();
- return;
- }
- ReadableType type = fill.getType();
- if (type.equals(ReadableType.Number)) {
- this.fill = JavaOnlyArray.of(0, fill.asInt());
- } else if (type.equals(ReadableType.Array)) {
- this.fill = fill.asArray();
- } else {
- JavaOnlyArray arr = new JavaOnlyArray();
- arr.pushInt(0);
- Matcher m = regex.matcher(fill.asString());
- int i = 0;
- while (m.find()) {
- double parsed = Double.parseDouble(m.group());
- arr.pushDouble(i++ < 3 ? parsed / 255 : parsed);
- }
- this.fill = arr;
- }
- invalidate();
- }
-
- @ReactProp(name = "fillOpacity", defaultFloat = 1f)
- public void setFillOpacity(float fillOpacity) {
- this.fillOpacity = fillOpacity;
- invalidate();
- }
-
- @ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO)
- public void setFillRule(int fillRule) {
- switch (fillRule) {
- case FILL_RULE_EVENODD:
- this.fillRule = Path.FillType.EVEN_ODD;
- break;
- case FILL_RULE_NONZERO:
- break;
- default:
- throw new JSApplicationIllegalArgumentException(
- "fillRule " + fillRule + " unrecognized");
- }
-
- invalidate();
- }
-
- @ReactProp(name = "stroke")
- public void setStroke(@Nullable Dynamic strokeColors) {
- if (strokeColors == null || strokeColors.isNull()) {
- stroke = null;
- invalidate();
- return;
- }
- ReadableType type = strokeColors.getType();
- if (type.equals(ReadableType.Number)) {
- stroke = JavaOnlyArray.of(0, strokeColors.asInt());
- } else if (type.equals(ReadableType.Array)) {
- stroke = strokeColors.asArray();
- } else {
- JavaOnlyArray arr = new JavaOnlyArray();
- arr.pushInt(0);
- Matcher m = regex.matcher(strokeColors.asString());
- int i = 0;
- while (m.find()) {
- double parsed = Double.parseDouble(m.group());
- arr.pushDouble(i++ < 3 ? parsed / 255 : parsed);
- }
- stroke = arr;
- }
- invalidate();
- }
-
- @ReactProp(name = "strokeOpacity", defaultFloat = 1f)
- public void setStrokeOpacity(float strokeOpacity) {
- this.strokeOpacity = strokeOpacity;
- invalidate();
- }
-
- @ReactProp(name = "strokeDasharray")
- public void setStrokeDasharray(@Nullable ReadableArray strokeDasharray) {
- if (strokeDasharray != null) {
- int fromSize = strokeDasharray.size();
- this.strokeDasharray = new SVGLength[fromSize];
- for (int i = 0; i < fromSize; i++) {
- this.strokeDasharray[i] = SVGLength.from(strokeDasharray.getDynamic(i));
- }
- } else {
- this.strokeDasharray = null;
- }
- invalidate();
- }
-
- @ReactProp(name = "strokeDashoffset")
- public void setStrokeDashoffset(float strokeDashoffset) {
- this.strokeDashoffset = strokeDashoffset * mScale;
- invalidate();
- }
-
- @ReactProp(name = "strokeWidth")
- public void setStrokeWidth(Dynamic strokeWidth) {
- this.strokeWidth = SVGLength.from(strokeWidth);
- invalidate();
- }
-
- @ReactProp(name = "strokeMiterlimit", defaultFloat = 4f)
- public void setStrokeMiterlimit(float strokeMiterlimit) {
- this.strokeMiterlimit = strokeMiterlimit;
- invalidate();
- }
-
- @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND)
- public void setStrokeLinecap(int strokeLinecap) {
- switch (strokeLinecap) {
- case CAP_BUTT:
- this.strokeLinecap = Paint.Cap.BUTT;
- break;
- case CAP_SQUARE:
- this.strokeLinecap = Paint.Cap.SQUARE;
- break;
- case CAP_ROUND:
- this.strokeLinecap = Paint.Cap.ROUND;
- break;
- default:
- throw new JSApplicationIllegalArgumentException(
- "strokeLinecap " + strokeLinecap + " unrecognized");
- }
- invalidate();
- }
-
- @ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND)
- public void setStrokeLinejoin(int strokeLinejoin) {
- switch (strokeLinejoin) {
- case JOIN_MITER:
- this.strokeLinejoin = Paint.Join.MITER;
- break;
- case JOIN_BEVEL:
- this.strokeLinejoin = Paint.Join.BEVEL;
- break;
- case JOIN_ROUND:
- this.strokeLinejoin = Paint.Join.ROUND;
- break;
- default:
- throw new JSApplicationIllegalArgumentException(
- "strokeLinejoin " + strokeLinejoin + " unrecognized");
- }
- invalidate();
- }
-
- @ReactProp(name = "propList")
- public void setPropList(@Nullable ReadableArray propList) {
- if (propList != null) {
- mPropList = mAttributeList = new ArrayList<>();
- for (int i = 0; i < propList.size(); i++) {
- mPropList.add(propList.getString(i));
- }
- }
-
- invalidate();
- }
-
- private static double saturate(double v) {
- return v <= 0 ? 0 : (v >= 1 ? 1 : v);
- }
-
- void render(Canvas canvas, Paint paint, float opacity) {
- MaskView mask = null;
- if (mMask != null) {
- SvgView root = getSvgView();
- mask = (MaskView) root.getDefinedMask(mMask);
- }
- if (mask != null) {
- Rect clipBounds = canvas.getClipBounds();
- int height = clipBounds.height();
- int width = clipBounds.width();
-
- Bitmap maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Bitmap original = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- Canvas originalCanvas = new Canvas(original);
- Canvas maskCanvas = new Canvas(maskBitmap);
- Canvas resultCanvas = new Canvas(result);
-
- // Clip to mask bounds and render the mask
- float maskX = (float) relativeOnWidth(mask.mX);
- float maskY = (float) relativeOnHeight(mask.mY);
- float maskWidth = (float) relativeOnWidth(mask.mW);
- float maskHeight = (float) relativeOnHeight(mask.mH);
- maskCanvas.clipRect(maskX, maskY, maskWidth, maskHeight);
-
- Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mask.draw(maskCanvas, maskPaint, 1);
-
- // Apply luminanceToAlpha filter primitive https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
- int nPixels = width * height;
- int[] pixels = new int[nPixels];
- maskBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
-
- for (int i = 0; i < nPixels; i++) {
- int color = pixels[i];
-
- int r = (color >> 16) & 0xFF;
- int g = (color >> 8) & 0xFF;
- int b = color & 0xFF;
- int a = color >>> 24;
-
- double luminance = saturate(((0.299 * r) + (0.587 * g) + (0.144 * b)) / 255);
- int alpha = (int) (a * luminance);
- int pixel = (alpha << 24);
- pixels[i] = pixel;
- }
-
- maskBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
-
- // Render content of current SVG Renderable to image
- draw(originalCanvas, paint, opacity);
-
- // Blend current element and mask
- maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
- resultCanvas.drawBitmap(original, 0, 0, null);
- resultCanvas.drawBitmap(maskBitmap, 0, 0, maskPaint);
-
- // Render composited result into current render context
- canvas.drawBitmap(result, 0, 0, paint);
- } else {
- draw(canvas, paint, opacity);
- }
- }
-
- @Override
- void draw(Canvas canvas, Paint paint, float opacity) {
- opacity *= mOpacity;
-
- boolean computePaths = mPath == null;
- if (computePaths) {
- mPath = getPath(canvas, paint);
- mPath.setFillType(fillRule);
- }
- boolean nonScalingStroke = vectorEffect == VECTOR_EFFECT_NON_SCALING_STROKE;
- Path path = mPath;
- if (nonScalingStroke) {
- Path scaled = new Path();
- //noinspection deprecation
- mPath.transform(mCTM, scaled);
- canvas.setMatrix(null);
- path = scaled;
- }
-
- if (computePaths || path != mPath) {
- mBox = new RectF();
- path.computeBounds(mBox, true);
- }
-
- RectF clientRect = new RectF(mBox);
- mCTM.mapRect(clientRect);
- this.setClientRect(clientRect);
-
- clip(canvas, paint);
-
- if (setupFillPaint(paint, opacity * fillOpacity)) {
- if (computePaths) {
- mFillPath = new Path();
- paint.getFillPath(path, mFillPath);
- }
- canvas.drawPath(path, paint);
- }
- if (setupStrokePaint(paint, opacity * strokeOpacity)) {
- if (computePaths) {
- mStrokePath = new Path();
- paint.getFillPath(path, mStrokePath);
- }
- canvas.drawPath(path, paint);
- }
- renderMarkers(canvas, paint, opacity);
- }
-
- void renderMarkers(Canvas canvas, Paint paint, float opacity) {
- MarkerView markerStart = (MarkerView)getSvgView().getDefinedMarker(mMarkerStart);
- MarkerView markerMid = (MarkerView)getSvgView().getDefinedMarker(mMarkerMid);
- MarkerView markerEnd = (MarkerView)getSvgView().getDefinedMarker(mMarkerEnd);
- if (elements != null && (markerStart != null || markerMid != null || markerEnd != null)) {
- contextElement = this;
- ArrayList positions = RNSVGMarkerPosition.fromPath(elements);
- float width = (float)(this.strokeWidth != null ? relativeOnOther(this.strokeWidth) : 1);
- mMarkerPath = new Path();
- for (RNSVGMarkerPosition position : positions) {
- RNSVGMarkerType type = position.type;
- MarkerView marker = null;
- switch (type) {
- case kStartMarker:
- marker = markerStart;
- break;
-
- case kMidMarker:
- marker = markerMid;
- break;
-
- case kEndMarker:
- marker = markerEnd;
- break;
- }
- if (marker == null) {
- continue;
- }
- marker.renderMarker(canvas, paint, opacity, position, width);
- Matrix transform = marker.markerTransform;
- mMarkerPath.addPath(marker.getPath(canvas, paint), transform);
- }
- contextElement = null;
- }
- }
-
- /**
- * Sets up paint according to the props set on a view. Returns {@code true}
- * if the fill should be drawn, {@code false} if not.
- */
- boolean setupFillPaint(Paint paint, float opacity) {
- if (fill != null && fill.size() > 0) {
- paint.reset();
- paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
- paint.setStyle(Paint.Style.FILL);
- setupPaint(paint, opacity, fill);
- return true;
- }
- return false;
- }
-
- /**
- * Sets up paint according to the props set on a view. Returns {@code true}
- * if the stroke should be drawn, {@code false} if not.
- */
- boolean setupStrokePaint(Paint paint, float opacity) {
- paint.reset();
- double strokeWidth = relativeOnOther(this.strokeWidth);
- if (strokeWidth == 0 || stroke == null || stroke.size() == 0) {
- return false;
- }
-
- paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeCap(strokeLinecap);
- paint.setStrokeJoin(strokeLinejoin);
- paint.setStrokeMiter(strokeMiterlimit * mScale);
- paint.setStrokeWidth((float) strokeWidth);
- setupPaint(paint, opacity, stroke);
-
- if (strokeDasharray != null) {
- int length = strokeDasharray.length;
- float[] intervals = new float[length];
- for (int i = 0; i < length; i++) {
- intervals[i] = (float)relativeOnOther(strokeDasharray[i]);
- }
- paint.setPathEffect(new DashPathEffect(intervals, strokeDashoffset));
- }
-
- return true;
- }
-
- private void setupPaint(Paint paint, float opacity, ReadableArray colors) {
- int colorType = colors.getInt(0);
- switch (colorType) {
- case 0:
- if (colors.size() == 2) {
- int color;
- if (colors.getType(1) == ReadableType.Map) {
- color = ColorPropConverter.getColor(colors.getMap(1), getContext());
- } else {
- color = colors.getInt(1);
- }
- int alpha = color >>> 24;
- int combined = Math.round((float)alpha * opacity);
- paint.setColor(combined << 24 | (color & 0x00ffffff));
- } else {
- // solid color
- paint.setARGB(
- (int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255),
- (int) (colors.getDouble(1) * 255),
- (int) (colors.getDouble(2) * 255),
- (int) (colors.getDouble(3) * 255));
- }
- break;
- case 1: {
- Brush brush = getSvgView().getDefinedBrush(colors.getString(1));
- if (brush != null) {
- brush.setupPaint(paint, mBox, mScale, opacity);
- }
- break;
- }
- case 2: {
- int brush = getSvgView().mTintColor;
- paint.setColor(brush);
- break;
- }
- case 3: {
- if (contextElement != null && contextElement.fill != null) {
- setupPaint(paint, opacity, contextElement.fill);
- }
- break;
- }
- case 4: {
- if (contextElement != null && contextElement.stroke != null) {
- setupPaint(paint, opacity, contextElement.stroke);
- }
- break;
- }
- }
-
- }
-
- abstract Path getPath(Canvas canvas, Paint paint);
-
- @Override
- int hitTest(final float[] src) {
- if (mPath == null || !mInvertible || !mTransformInvertible) {
- return -1;
- }
-
- if (mPointerEvents == PointerEvents.NONE) {
- return -1;
- }
-
- float[] dst = new float[2];
- mInvMatrix.mapPoints(dst, src);
- mInvTransform.mapPoints(dst);
- int x = Math.round(dst[0]);
- int y = Math.round(dst[1]);
-
- initBounds();
-
- if (
- (mRegion == null || !mRegion.contains(x, y)) &&
- (mStrokeRegion == null || !mStrokeRegion.contains(x, y) &&
- (mMarkerRegion == null || !mMarkerRegion.contains(x, y)))
- ) {
- return -1;
- }
-
- Path clipPath = getClipPath();
- if (clipPath != null) {
- if (!mClipRegion.contains(x, y)) {
- return -1;
- }
- }
-
- return getId();
- }
-
- void initBounds() {
- if (mRegion == null && mFillPath != null) {
- mFillBounds = new RectF();
- mFillPath.computeBounds(mFillBounds, true);
- mRegion = getRegion(mFillPath, mFillBounds);
- }
- if (mRegion == null && mPath != null) {
- mFillBounds = new RectF();
- mPath.computeBounds(mFillBounds, true);
- mRegion = getRegion(mPath, mFillBounds);
- }
- if (mStrokeRegion == null && mStrokePath != null) {
- mStrokeBounds = new RectF();
- mStrokePath.computeBounds(mStrokeBounds, true);
- mStrokeRegion = getRegion(mStrokePath, mStrokeBounds);
- }
- if (mMarkerRegion == null && mMarkerPath != null) {
- mMarkerBounds = new RectF();
- mMarkerPath.computeBounds(mMarkerBounds, true);
- mMarkerRegion = getRegion(mMarkerPath, mMarkerBounds);
- }
- Path clipPath = getClipPath();
- if (clipPath != null) {
- if (mClipRegionPath != clipPath) {
- mClipRegionPath = clipPath;
- mClipBounds = new RectF();
- clipPath.computeBounds(mClipBounds, true);
- mClipRegion = getRegion(clipPath, mClipBounds);
- }
- }
- }
-
- Region getRegion(Path path, RectF rectF) {
- Region region = new Region();
- region.setPath(path,
- new Region(
- (int) Math.floor(rectF.left),
- (int) Math.floor(rectF.top),
- (int) Math.ceil(rectF.right),
- (int) Math.ceil(rectF.bottom)
- )
- );
-
- return region;
- }
-
- private ArrayList getAttributeList() {
- return mAttributeList;
- }
-
- void mergeProperties(RenderableView target) {
- ArrayList targetAttributeList = target.getAttributeList();
-
- if (targetAttributeList == null ||
- targetAttributeList.size() == 0) {
- return;
- }
-
- mOriginProperties = new ArrayList<>();
- mAttributeList = mPropList == null ? new ArrayList() : new ArrayList<>(mPropList);
-
- for (int i = 0, size = targetAttributeList.size(); i < size; i++) {
- try {
- String fieldName = targetAttributeList.get(i);
- Field field = getClass().getField(fieldName);
- Object value = field.get(target);
- mOriginProperties.add(field.get(this));
-
- if (!hasOwnProperty(fieldName)) {
- mAttributeList.add(fieldName);
- field.set(this, value);
- }
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
- }
-
- mLastMergedList = targetAttributeList;
- }
-
- void resetProperties() {
- if (mLastMergedList != null && mOriginProperties != null) {
- try {
- for (int i = mLastMergedList.size() - 1; i >= 0; i--) {
- Field field = getClass().getField(mLastMergedList.get(i));
- field.set(this, mOriginProperties.get(i));
- }
- } catch (Exception e) {
- throw new IllegalStateException(e);
- }
-
- mLastMergedList = null;
- mOriginProperties = null;
- mAttributeList = mPropList;
- }
- }
-
- private boolean hasOwnProperty(String propName) {
- return mAttributeList != null && mAttributeList.contains(propName);
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RenderableViewManager.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RenderableViewManager.java
deleted file mode 100644
index 4934e968afef7..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/RenderableViewManager.java
+++ /dev/null
@@ -1,1379 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.graphics.Matrix;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.JavaOnlyMap;
-import com.facebook.react.bridge.ReadableArray;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.ReadableType;
-import com.facebook.react.uimanager.DisplayMetricsHolder;
-import com.facebook.react.uimanager.LayoutShadowNode;
-import com.facebook.react.uimanager.MatrixMathHelper;
-import com.facebook.react.uimanager.PixelUtil;
-import com.facebook.react.uimanager.PointerEvents;
-import com.facebook.react.uimanager.ThemedReactContext;
-import com.facebook.react.uimanager.TransformHelper;
-import com.facebook.react.uimanager.ViewGroupManager;
-import com.facebook.react.uimanager.ViewProps;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.uimanager.annotations.ReactPropGroup;
-
-import java.util.Locale;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static com.facebook.react.uimanager.MatrixMathHelper.determinant;
-import static com.facebook.react.uimanager.MatrixMathHelper.inverse;
-import static com.facebook.react.uimanager.MatrixMathHelper.multiplyVectorByMatrix;
-import static com.facebook.react.uimanager.MatrixMathHelper.roundTo3Places;
-import static com.facebook.react.uimanager.MatrixMathHelper.transpose;
-import static com.facebook.react.uimanager.MatrixMathHelper.v3Combine;
-import static com.facebook.react.uimanager.MatrixMathHelper.v3Cross;
-import static com.facebook.react.uimanager.MatrixMathHelper.v3Dot;
-import static com.facebook.react.uimanager.MatrixMathHelper.v3Length;
-import static com.facebook.react.uimanager.MatrixMathHelper.v3Normalize;
-import static com.facebook.react.uimanager.ViewProps.ALIGN_CONTENT;
-import static com.facebook.react.uimanager.ViewProps.ALIGN_ITEMS;
-import static com.facebook.react.uimanager.ViewProps.ALIGN_SELF;
-import static com.facebook.react.uimanager.ViewProps.BORDER_BOTTOM_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.BORDER_END_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.BORDER_LEFT_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.BORDER_RIGHT_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.BORDER_START_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.BORDER_TOP_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.BORDER_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.BOTTOM;
-import static com.facebook.react.uimanager.ViewProps.COLLAPSABLE;
-import static com.facebook.react.uimanager.ViewProps.DISPLAY;
-import static com.facebook.react.uimanager.ViewProps.END;
-import static com.facebook.react.uimanager.ViewProps.FLEX;
-import static com.facebook.react.uimanager.ViewProps.FLEX_BASIS;
-import static com.facebook.react.uimanager.ViewProps.FLEX_DIRECTION;
-import static com.facebook.react.uimanager.ViewProps.FLEX_GROW;
-import static com.facebook.react.uimanager.ViewProps.FLEX_SHRINK;
-import static com.facebook.react.uimanager.ViewProps.FLEX_WRAP;
-import static com.facebook.react.uimanager.ViewProps.HEIGHT;
-import static com.facebook.react.uimanager.ViewProps.JUSTIFY_CONTENT;
-import static com.facebook.react.uimanager.ViewProps.LEFT;
-import static com.facebook.react.uimanager.ViewProps.MARGIN;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_BOTTOM;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_END;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_HORIZONTAL;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_LEFT;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_RIGHT;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_START;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_TOP;
-import static com.facebook.react.uimanager.ViewProps.MARGIN_VERTICAL;
-import static com.facebook.react.uimanager.ViewProps.MAX_HEIGHT;
-import static com.facebook.react.uimanager.ViewProps.MAX_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.MIN_HEIGHT;
-import static com.facebook.react.uimanager.ViewProps.MIN_WIDTH;
-import static com.facebook.react.uimanager.ViewProps.OVERFLOW;
-import static com.facebook.react.uimanager.ViewProps.PADDING;
-import static com.facebook.react.uimanager.ViewProps.PADDING_BOTTOM;
-import static com.facebook.react.uimanager.ViewProps.PADDING_END;
-import static com.facebook.react.uimanager.ViewProps.PADDING_HORIZONTAL;
-import static com.facebook.react.uimanager.ViewProps.PADDING_LEFT;
-import static com.facebook.react.uimanager.ViewProps.PADDING_RIGHT;
-import static com.facebook.react.uimanager.ViewProps.PADDING_START;
-import static com.facebook.react.uimanager.ViewProps.PADDING_TOP;
-import static com.facebook.react.uimanager.ViewProps.PADDING_VERTICAL;
-import static com.facebook.react.uimanager.ViewProps.POSITION;
-import static com.facebook.react.uimanager.ViewProps.RIGHT;
-import static com.facebook.react.uimanager.ViewProps.START;
-import static com.facebook.react.uimanager.ViewProps.TOP;
-import static com.facebook.react.uimanager.ViewProps.WIDTH;
-import static versioned.host.exp.exponent.modules.api.components.svg.RenderableView.CAP_ROUND;
-import static versioned.host.exp.exponent.modules.api.components.svg.RenderableView.FILL_RULE_NONZERO;
-import static versioned.host.exp.exponent.modules.api.components.svg.RenderableView.JOIN_ROUND;
-
-/**
- * ViewManager for all RNSVG views
- */
-class RenderableViewManager extends ViewGroupManager {
-
- private enum SVGClass {
- RNSVGGroup,
- RNSVGPath,
- RNSVGText,
- RNSVGTSpan,
- RNSVGTextPath,
- RNSVGImage,
- RNSVGCircle,
- RNSVGEllipse,
- RNSVGLine,
- RNSVGRect,
- RNSVGClipPath,
- RNSVGDefs,
- RNSVGUse,
- RNSVGSymbol,
- RNSVGLinearGradient,
- RNSVGRadialGradient,
- RNSVGPattern,
- RNSVGMask,
- RNSVGMarker,
- RNSVGForeignObject,
- }
-
- class RenderableShadowNode extends LayoutShadowNode {
-
- @SuppressWarnings({"unused", "EmptyMethod"})
- @ReactPropGroup(
- names = {
- ALIGN_SELF,
- ALIGN_ITEMS,
- COLLAPSABLE,
- FLEX,
- FLEX_BASIS,
- FLEX_DIRECTION,
- FLEX_GROW,
- FLEX_SHRINK,
- FLEX_WRAP,
- JUSTIFY_CONTENT,
- OVERFLOW,
- ALIGN_CONTENT,
- DISPLAY,
-
- /* position */
- POSITION,
- RIGHT,
- TOP,
- BOTTOM,
- LEFT,
- START,
- END,
-
- /* dimensions */
- WIDTH,
- HEIGHT,
- MIN_WIDTH,
- MAX_WIDTH,
- MIN_HEIGHT,
- MAX_HEIGHT,
-
- /* margins */
- MARGIN,
- MARGIN_VERTICAL,
- MARGIN_HORIZONTAL,
- MARGIN_LEFT,
- MARGIN_RIGHT,
- MARGIN_TOP,
- MARGIN_BOTTOM,
- MARGIN_START,
- MARGIN_END,
-
- /* paddings */
- PADDING,
- PADDING_VERTICAL,
- PADDING_HORIZONTAL,
- PADDING_LEFT,
- PADDING_RIGHT,
- PADDING_TOP,
- PADDING_BOTTOM,
- PADDING_START,
- PADDING_END,
-
- BORDER_WIDTH,
- BORDER_START_WIDTH,
- BORDER_END_WIDTH,
- BORDER_TOP_WIDTH,
- BORDER_BOTTOM_WIDTH,
- BORDER_LEFT_WIDTH,
- BORDER_RIGHT_WIDTH,
- }
- )
- public void ignoreLayoutProps(int index, Dynamic value) {}
- }
-
- @Override
- public LayoutShadowNode createShadowNodeInstance() {
- return new RenderableShadowNode();
- }
-
- @Override
- public Class getShadowNodeClass() {
- return RenderableShadowNode.class;
- }
-
-
- private final SVGClass svgClass;
- private final String mClassName;
-
- static class MatrixDecompositionContext extends MatrixMathHelper.MatrixDecompositionContext {
- final double[] perspective = new double[4];
- final double[] scale = new double[3];
- final double[] skew = new double[3];
- final double[] translation = new double[3];
- final double[] rotationDegrees = new double[3];
- }
-
- private static final MatrixDecompositionContext sMatrixDecompositionContext =
- new MatrixDecompositionContext();
- private static final double[] sTransformDecompositionArray = new double[16];
-
- private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2;
- private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = 5;
-
- private static final double EPSILON = .00001d;
-
- private static boolean isZero(double d) {
- return !Double.isNaN(d) && Math.abs(d) < EPSILON;
- }
-
- private static void decomposeMatrix() {
-
- // output values
- final double[] perspective = sMatrixDecompositionContext.perspective;
- final double[] scale = sMatrixDecompositionContext.scale;
- final double[] skew = sMatrixDecompositionContext.skew;
- final double[] translation = sMatrixDecompositionContext.translation;
- final double[] rotationDegrees = sMatrixDecompositionContext.rotationDegrees;
-
- // create normalized, 2d array matrix
- // and normalized 1d array perspectiveMatrix with redefined 4th column
- if (isZero(sTransformDecompositionArray[15])) {
- return;
- }
- double[][] matrix = new double[4][4];
- double[] perspectiveMatrix = new double[16];
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- double value = sTransformDecompositionArray[(i * 4) + j] / sTransformDecompositionArray[15];
- matrix[i][j] = value;
- perspectiveMatrix[(i * 4) + j] = j == 3 ? 0 : value;
- }
- }
- perspectiveMatrix[15] = 1;
-
- // test for singularity of upper 3x3 part of the perspective matrix
- if (isZero(determinant(perspectiveMatrix))) {
- return;
- }
-
- // isolate perspective
- if (!isZero(matrix[0][3]) || !isZero(matrix[1][3]) || !isZero(matrix[2][3])) {
- // rightHandSide is the right hand side of the equation.
- // rightHandSide is a vector, or point in 3d space relative to the origin.
- double[] rightHandSide = { matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3] };
-
- // Solve the equation by inverting perspectiveMatrix and multiplying
- // rightHandSide by the inverse.
- double[] inversePerspectiveMatrix = inverse(
- perspectiveMatrix
- );
- double[] transposedInversePerspectiveMatrix = transpose(
- inversePerspectiveMatrix
- );
- multiplyVectorByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspective);
- } else {
- // no perspective
- perspective[0] = perspective[1] = perspective[2] = 0d;
- perspective[3] = 1d;
- }
-
- // translation is simple
- System.arraycopy(matrix[3], 0, translation, 0, 3);
-
- // Now get scale and shear.
- // 'row' is a 3 element array of 3 component vectors
- double[][] row = new double[3][3];
- for (int i = 0; i < 3; i++) {
- row[i][0] = matrix[i][0];
- row[i][1] = matrix[i][1];
- row[i][2] = matrix[i][2];
- }
-
- // Compute X scale factor and normalize first row.
- scale[0] = v3Length(row[0]);
- row[0] = v3Normalize(row[0], scale[0]);
-
- // Compute XY shear factor and make 2nd row orthogonal to 1st.
- skew[0] = v3Dot(row[0], row[1]);
- row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
-
- // Compute XY shear factor and make 2nd row orthogonal to 1st.
- skew[0] = v3Dot(row[0], row[1]);
- row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
-
- // Now, compute Y scale and normalize 2nd row.
- scale[1] = v3Length(row[1]);
- row[1] = v3Normalize(row[1], scale[1]);
- skew[0] /= scale[1];
-
- // Compute XZ and YZ shears, orthogonalize 3rd row
- skew[1] = v3Dot(row[0], row[2]);
- row[2] = v3Combine(row[2], row[0], 1.0, -skew[1]);
- skew[2] = v3Dot(row[1], row[2]);
- row[2] = v3Combine(row[2], row[1], 1.0, -skew[2]);
-
- // Next, get Z scale and normalize 3rd row.
- scale[2] = v3Length(row[2]);
- row[2] = v3Normalize(row[2], scale[2]);
- skew[1] /= scale[2];
- skew[2] /= scale[2];
-
- // At this point, the matrix (in rows) is orthonormal.
- // Check for a coordinate system flip. If the determinant
- // is -1, then negate the matrix and the scaling factors.
- double[] pdum3 = v3Cross(row[1], row[2]);
- if (v3Dot(row[0], pdum3) < 0) {
- for (int i = 0; i < 3; i++) {
- scale[i] *= -1;
- row[i][0] *= -1;
- row[i][1] *= -1;
- row[i][2] *= -1;
- }
- }
-
- // Now, get the rotations out
- // Based on: http://nghiaho.com/?page_id=846
- double conv = 180 / Math.PI;
- rotationDegrees[0] = roundTo3Places(-Math.atan2(row[2][1], row[2][2]) * conv);
- rotationDegrees[1] = roundTo3Places(-Math.atan2(-row[2][0], Math.sqrt(row[2][1] * row[2][1] + row[2][2] * row[2][2])) * conv);
- rotationDegrees[2] = roundTo3Places(-Math.atan2(row[1][0], row[0][0]) * conv);
- }
-
- private static void setTransformProperty(View view, ReadableArray transforms) {
- TransformHelper.processTransform(transforms, sTransformDecompositionArray);
- decomposeMatrix();
- view.setTranslationX(
- PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[0]));
- view.setTranslationY(
- PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[1]));
- view.setRotation((float) sMatrixDecompositionContext.rotationDegrees[2]);
- view.setRotationX((float) sMatrixDecompositionContext.rotationDegrees[0]);
- view.setRotationY((float) sMatrixDecompositionContext.rotationDegrees[1]);
- view.setScaleX((float) sMatrixDecompositionContext.scale[0]);
- view.setScaleY((float) sMatrixDecompositionContext.scale[1]);
-
- double[] perspectiveArray = sMatrixDecompositionContext.perspective;
-
- if (perspectiveArray.length > PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) {
- float invertedCameraDistance = (float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX];
- if (invertedCameraDistance == 0) {
- // Default camera distance, before scale multiplier (1280)
- invertedCameraDistance = 0.00078125f;
- }
- float cameraDistance = -1 / invertedCameraDistance;
- float scale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
-
- // The following converts the matrix's perspective to a camera distance
- // such that the camera perspective looks the same on Android and iOS.
- // The native Android implementation removed the screen density from the
- // calculation, so squaring and a normalization value of
- // sqrt(5) produces an exact replica with iOS.
- // For more information, see https://github.com/facebook/react-native/pull/18302
- float normalizedCameraDistance = scale * scale * cameraDistance * CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER;
- view.setCameraDistance(normalizedCameraDistance);
-
- }
- }
-
- private static void resetTransformProperty(View view) {
- view.setTranslationX(0);
- view.setTranslationY(0);
- view.setRotation(0);
- view.setRotationX(0);
- view.setRotationY(0);
- view.setScaleX(1);
- view.setScaleY(1);
- view.setCameraDistance(0);
- }
-
- static class GroupViewManager extends RenderableViewManager {
- GroupViewManager() {
- super(SVGClass.RNSVGGroup);
- }
-
- GroupViewManager(SVGClass svgClass) {
- super(svgClass);
- }
-
- @ReactProp(name = "font")
- public void setFont(GroupView node, @Nullable ReadableMap font) {
- node.setFont(font);
- }
-
- @ReactProp(name = "fontSize")
- public void setFontSize(GroupView node, Dynamic fontSize) {
- JavaOnlyMap map = new JavaOnlyMap();
- switch (fontSize.getType()) {
- case Number:
- map.putDouble("fontSize", fontSize.asDouble());
- break;
- case String:
- map.putString("fontSize", fontSize.asString());
- break;
- default:
- return;
- }
- node.setFont(map);
- }
-
- @ReactProp(name = "fontWeight")
- public void setFontWeight(GroupView node, Dynamic fontWeight) {
- JavaOnlyMap map = new JavaOnlyMap();
- switch (fontWeight.getType()) {
- case Number:
- map.putDouble("fontWeight", fontWeight.asDouble());
- break;
- case String:
- map.putString("fontWeight", fontWeight.asString());
- break;
- default:
- return;
- }
- node.setFont(map);
- }
- }
-
-
- static class PathViewManager extends RenderableViewManager {
- PathViewManager() {
- super(SVGClass.RNSVGPath);
- }
-
- @ReactProp(name = "d")
- public void setD(PathView node, String d) {
- node.setD(d);
- }
- }
-
- static class TextViewManager extends GroupViewManager {
- TextViewManager() {
- super(SVGClass.RNSVGText);
- }
-
- TextViewManager(SVGClass svgClass) {
- super(svgClass);
- }
-
- @ReactProp(name = "inlineSize")
- public void setInlineSize(TextView node, Dynamic inlineSize) {
- node.setInlineSize(inlineSize);
- }
-
- @ReactProp(name = "textLength")
- public void setTextLength(TextView node, Dynamic length) {
- node.setTextLength(length);
- }
-
- @ReactProp(name = "lengthAdjust")
- public void setLengthAdjust(TextView node, @Nullable String adjustment) {
- node.setLengthAdjust(adjustment);
- }
-
- @ReactProp(name = "alignmentBaseline")
- public void setMethod(TextView node, @Nullable String alignment) {
- node.setMethod(alignment);
- }
-
- @ReactProp(name = "baselineShift")
- public void setBaselineShift(TextView node, Dynamic baselineShift) {
- node.setBaselineShift(baselineShift);
- }
-
- @ReactProp(name = "verticalAlign")
- public void setVerticalAlign(TextView node, @Nullable String verticalAlign) {
- node.setVerticalAlign(verticalAlign);
- }
-
- @ReactProp(name = "rotate")
- public void setRotate(TextView node, Dynamic rotate) {
- node.setRotate(rotate);
- }
-
- @ReactProp(name = "dx")
- public void setDeltaX(TextView node, Dynamic deltaX) {
- node.setDeltaX(deltaX);
- }
-
- @ReactProp(name = "dy")
- public void setDeltaY(TextView node, Dynamic deltaY) {
- node.setDeltaY(deltaY);
- }
-
- @ReactProp(name = "x")
- public void setX(TextView node, Dynamic positionX) {
- node.setPositionX(positionX);
- }
-
- @ReactProp(name = "y")
- public void setY(TextView node, Dynamic positionY) {
- node.setPositionY(positionY);
- }
-
- @ReactProp(name = "font")
- public void setFont(TextView node, @Nullable ReadableMap font) {
- node.setFont(font);
- }
- }
-
- static class TSpanViewManager extends TextViewManager {
- TSpanViewManager() {
- super(SVGClass.RNSVGTSpan);
- }
-
- @ReactProp(name = "content")
- public void setContent(TSpanView node, @Nullable String content) {
- node.setContent(content);
- }
- }
-
- static class TextPathViewManager extends TextViewManager {
- TextPathViewManager() {
- super(SVGClass.RNSVGTextPath);
- }
-
- @ReactProp(name = "href")
- public void setHref(TextPathView node, String href) {
- node.setHref(href);
- }
-
- @ReactProp(name = "startOffset")
- public void setStartOffset(TextPathView node, Dynamic startOffset) {
- node.setStartOffset(startOffset);
- }
-
- @ReactProp(name = "method")
- public void setMethod(TextPathView node, @Nullable String method) {
- node.setMethod(method);
- }
-
- @ReactProp(name = "spacing")
- public void setSpacing(TextPathView node, @Nullable String spacing) {
- node.setSpacing(spacing);
- }
-
- @ReactProp(name = "side")
- public void setSide(TextPathView node, @Nullable String side) {
- node.setSide(side);
- }
-
- @ReactProp(name = "midLine")
- public void setSharp(TextPathView node, @Nullable String midLine) {
- node.setSharp(midLine);
- }
- }
-
- static class ImageViewManager extends RenderableViewManager {
- ImageViewManager(){
- super(SVGClass.RNSVGImage);
- }
-
- @ReactProp(name = "x")
- public void setX(ImageView node, Dynamic x) {
- node.setX(x);
- }
-
- @ReactProp(name = "y")
- public void setY(ImageView node, Dynamic y) {
- node.setY(y);
- }
-
- @ReactProp(name = "width")
- public void setWidth(ImageView node, Dynamic width) {
- node.setWidth(width);
- }
-
- @ReactProp(name = "height")
- public void setHeight(ImageView node, Dynamic height) {
- node.setHeight(height);
- }
-
- @ReactProp(name = "src")
- public void setSrc(ImageView node, @Nullable ReadableMap src) {
- node.setSrc(src);
- }
-
-
- @ReactProp(name = "align")
- public void setAlign(ImageView node, String align) {
- node.setAlign(align);
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(ImageView node, int meetOrSlice) {
- node.setMeetOrSlice(meetOrSlice);
- }
- }
-
- static class CircleViewManager extends RenderableViewManager {
- CircleViewManager() {
- super(SVGClass.RNSVGCircle);
- }
-
- @ReactProp(name = "cx")
- public void setCx(CircleView node, Dynamic cx) {
- node.setCx(cx);
- }
-
- @ReactProp(name = "cy")
- public void setCy(CircleView node, Dynamic cy) {
- node.setCy(cy);
- }
-
- @ReactProp(name = "r")
- public void setR(CircleView node, Dynamic r) {
- node.setR(r);
- }
- }
-
- static class EllipseViewManager extends RenderableViewManager {
- EllipseViewManager() {
- super(SVGClass.RNSVGEllipse);
- }
-
- @ReactProp(name = "cx")
- public void setCx(EllipseView node, Dynamic cx) {
- node.setCx(cx);
- }
-
- @ReactProp(name = "cy")
- public void setCy(EllipseView node, Dynamic cy) {
- node.setCy(cy);
- }
-
- @ReactProp(name = "rx")
- public void setRx(EllipseView node, Dynamic rx) {
- node.setRx(rx);
- }
-
- @ReactProp(name = "ry")
- public void setRy(EllipseView node, Dynamic ry) {
- node.setRy(ry);
- }
- }
-
- static class LineViewManager extends RenderableViewManager {
- LineViewManager() {
- super(SVGClass.RNSVGLine);
- }
-
- @ReactProp(name = "x1")
- public void setX1(LineView node, Dynamic x1) {
- node.setX1(x1);
- }
-
- @ReactProp(name = "y1")
- public void setY1(LineView node, Dynamic y1) {
- node.setY1(y1);
- }
-
- @ReactProp(name = "x2")
- public void setX2(LineView node, Dynamic x2) {
- node.setX2(x2);
- }
-
- @ReactProp(name = "y2")
- public void setY2(LineView node, Dynamic y2) {
- node.setY2(y2);
- }
- }
-
- static class RectViewManager extends RenderableViewManager {
- RectViewManager() {
- super(SVGClass.RNSVGRect);
- }
-
- @ReactProp(name = "x")
- public void setX(RectView node, Dynamic x) {
- node.setX(x);
- }
-
- @ReactProp(name = "y")
- public void setY(RectView node, Dynamic y) {
- node.setY(y);
- }
-
- @ReactProp(name = "width")
- public void setWidth(RectView node, Dynamic width) {
- node.setWidth(width);
- }
-
- @ReactProp(name = "height")
- public void setHeight(RectView node, Dynamic height) {
- node.setHeight(height);
- }
-
- @ReactProp(name = "rx")
- public void setRx(RectView node, Dynamic rx) {
- node.setRx(rx);
- }
-
- @ReactProp(name = "ry")
- public void setRy(RectView node, Dynamic ry) {
- node.setRy(ry);
- }
- }
-
- static class ClipPathViewManager extends GroupViewManager {
- ClipPathViewManager() {
- super(SVGClass.RNSVGClipPath);
- }
- }
-
- static class DefsViewManager extends RenderableViewManager {
- DefsViewManager() {
- super(SVGClass.RNSVGDefs);
- }
- }
-
- static class UseViewManager extends RenderableViewManager {
- UseViewManager() {
- super(SVGClass.RNSVGUse);
- }
-
- @ReactProp(name = "href")
- public void setHref(UseView node, String href) {
- node.setHref(href);
- }
-
- @ReactProp(name = "x")
- public void setX(UseView node, Dynamic x) {
- node.setX(x);
- }
-
- @ReactProp(name = "y")
- public void setY(UseView node, Dynamic y) {
- node.setY(y);
- }
-
- @ReactProp(name = "width")
- public void setWidth(UseView node, Dynamic width) {
- node.setWidth(width);
- }
-
- @ReactProp(name = "height")
- public void setHeight(UseView node, Dynamic height) {
- node.setHeight(height);
- }
- }
-
- static class SymbolManager extends GroupViewManager {
- SymbolManager() {
- super(SVGClass.RNSVGSymbol);
- }
-
- @ReactProp(name = "minX")
- public void setMinX(SymbolView node, float minX) {
- node.setMinX(minX);
- }
-
- @ReactProp(name = "minY")
- public void setMinY(SymbolView node, float minY) {
- node.setMinY(minY);
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(SymbolView node, float vbWidth) {
- node.setVbWidth(vbWidth);
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(SymbolView node, float vbHeight) {
- node.setVbHeight(vbHeight);
- }
-
- @ReactProp(name = "align")
- public void setAlign(SymbolView node, String align) {
- node.setAlign(align);
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(SymbolView node, int meetOrSlice) {
- node.setMeetOrSlice(meetOrSlice);
- }
- }
-
- static class PatternManager extends GroupViewManager {
- PatternManager() {
- super(SVGClass.RNSVGPattern);
- }
-
- @ReactProp(name = "x")
- public void setX(PatternView node, Dynamic x) {
- node.setX(x);
- }
-
- @ReactProp(name = "y")
- public void setY(PatternView node, Dynamic y) {
- node.setY(y);
- }
-
- @ReactProp(name = "width")
- public void setWidth(PatternView node, Dynamic width) {
- node.setWidth(width);
- }
-
- @ReactProp(name = "height")
- public void setHeight(PatternView node, Dynamic height) {
- node.setHeight(height);
- }
-
- @ReactProp(name = "patternUnits")
- public void setPatternUnits(PatternView node, int patternUnits) {
- node.setPatternUnits(patternUnits);
- }
-
- @ReactProp(name = "patternContentUnits")
- public void setPatternContentUnits(PatternView node, int patternContentUnits) {
- node.setPatternContentUnits(patternContentUnits);
- }
-
- @ReactProp(name = "patternTransform")
- public void setPatternTransform(PatternView node, @Nullable ReadableArray matrixArray) {
- node.setPatternTransform(matrixArray);
- }
-
- @ReactProp(name = "minX")
- public void setMinX(PatternView node, float minX) {
- node.setMinX(minX);
- }
-
- @ReactProp(name = "minY")
- public void setMinY(PatternView node, float minY) {
- node.setMinY(minY);
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(PatternView node, float vbWidth) {
- node.setVbWidth(vbWidth);
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(PatternView node, float vbHeight) {
- node.setVbHeight(vbHeight);
- }
-
- @ReactProp(name = "align")
- public void setAlign(PatternView node, String align) {
- node.setAlign(align);
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(PatternView node, int meetOrSlice) {
- node.setMeetOrSlice(meetOrSlice);
- }
- }
-
- static class MaskManager extends GroupViewManager {
- MaskManager() {
- super(SVGClass.RNSVGMask);
- }
-
- @ReactProp(name = "x")
- public void setX(MaskView node, Dynamic x) {
- node.setX(x);
- }
-
- @ReactProp(name = "y")
- public void setY(MaskView node, Dynamic y) {
- node.setY(y);
- }
-
- @ReactProp(name = "width")
- public void setWidth(MaskView node, Dynamic width) {
- node.setWidth(width);
- }
-
- @ReactProp(name = "height")
- public void setHeight(MaskView node, Dynamic height) {
- node.setHeight(height);
- }
-
- @ReactProp(name = "maskUnits")
- public void setMaskUnits(MaskView node, int maskUnits) {
- node.setMaskUnits(maskUnits);
- }
-
- @ReactProp(name = "maskContentUnits")
- public void setMaskContentUnits(MaskView node, int maskContentUnits) {
- node.setMaskContentUnits(maskContentUnits);
- }
-
- @ReactProp(name = "maskTransform")
- public void setMaskTransform(MaskView node, @Nullable ReadableArray matrixArray) {
- node.setMaskTransform(matrixArray);
- }
- }
-
- static class ForeignObjectManager extends GroupViewManager {
- ForeignObjectManager() {
- super(SVGClass.RNSVGForeignObject);
- }
-
- @ReactProp(name = "x")
- public void setX(ForeignObjectView node, Dynamic x) {
- node.setX(x);
- }
-
- @ReactProp(name = "y")
- public void setY(ForeignObjectView node, Dynamic y) {
- node.setY(y);
- }
-
- @ReactProp(name = "width")
- public void setWidth(ForeignObjectView node, Dynamic width) {
- node.setWidth(width);
- }
-
- @ReactProp(name = "height")
- public void setHeight(ForeignObjectView node, Dynamic height) {
- node.setHeight(height);
- }
- }
-
- static class MarkerManager extends GroupViewManager {
- MarkerManager() {
- super(SVGClass.RNSVGMarker);
- }
-
- @ReactProp(name = "refX")
- public void setRefX(MarkerView node, Dynamic refX) {
- node.setRefX(refX);
- }
-
- @ReactProp(name = "refY")
- public void setRefY(MarkerView node, Dynamic refY) {
- node.setRefY(refY);
- }
-
- @ReactProp(name = "markerWidth")
- public void setMarkerWidth(MarkerView node, Dynamic markerWidth) {
- node.setMarkerWidth(markerWidth);
- }
-
- @ReactProp(name = "markerHeight")
- public void setMarkerHeight(MarkerView node, Dynamic markerHeight) {
- node.setMarkerHeight(markerHeight);
- }
-
- @ReactProp(name = "markerUnits")
- public void setMarkerUnits(MarkerView node, String markerUnits) {
- node.setMarkerUnits(markerUnits);
- }
-
- @ReactProp(name = "orient")
- public void setOrient(MarkerView node, String orient) {
- node.setOrient(orient);
- }
-
- @ReactProp(name = "minX")
- public void setMinX(MarkerView node, float minX) {
- node.setMinX(minX);
- }
-
- @ReactProp(name = "minY")
- public void setMinY(MarkerView node, float minY) {
- node.setMinY(minY);
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(MarkerView node, float vbWidth) {
- node.setVbWidth(vbWidth);
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(MarkerView node, float vbHeight) {
- node.setVbHeight(vbHeight);
- }
-
- @ReactProp(name = "align")
- public void setAlign(MarkerView node, String align) {
- node.setAlign(align);
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(MarkerView node, int meetOrSlice) {
- node.setMeetOrSlice(meetOrSlice);
- }
- }
-
- static class LinearGradientManager extends RenderableViewManager {
- LinearGradientManager() {
- super(SVGClass.RNSVGLinearGradient);
- }
-
- @ReactProp(name = "x1")
- public void setX1(LinearGradientView node, Dynamic x1) {
- node.setX1(x1);
- }
-
- @ReactProp(name = "y1")
- public void setY1(LinearGradientView node, Dynamic y1) {
- node.setY1(y1);
- }
-
- @ReactProp(name = "x2")
- public void setX2(LinearGradientView node, Dynamic x2) {
- node.setX2(x2);
- }
-
- @ReactProp(name = "y2")
- public void setY2(LinearGradientView node, Dynamic y2) {
- node.setY2(y2);
- }
-
- @ReactProp(name = "gradient")
- public void setGradient(LinearGradientView node, ReadableArray gradient) {
- node.setGradient(gradient);
- }
-
- @ReactProp(name = "gradientUnits")
- public void setGradientUnits(LinearGradientView node, int gradientUnits) {
- node.setGradientUnits(gradientUnits);
- }
-
- @ReactProp(name = "gradientTransform")
- public void setGradientTransform(LinearGradientView node, @Nullable ReadableArray matrixArray) {
- node.setGradientTransform(matrixArray);
- }
- }
-
- static class RadialGradientManager extends RenderableViewManager {
- RadialGradientManager() {
- super(SVGClass.RNSVGRadialGradient);
- }
-
- @ReactProp(name = "fx")
- public void setFx(RadialGradientView node, Dynamic fx) {
- node.setFx(fx);
- }
-
- @ReactProp(name = "fy")
- public void setFy(RadialGradientView node, Dynamic fy) {
- node.setFy(fy);
- }
-
- @ReactProp(name = "rx")
- public void setRx(RadialGradientView node, Dynamic rx) {
- node.setRx(rx);
- }
-
- @ReactProp(name = "ry")
- public void setRy(RadialGradientView node, Dynamic ry) {
- node.setRy(ry);
- }
-
- @ReactProp(name = "cx")
- public void setCx(RadialGradientView node, Dynamic cx) {
- node.setCx(cx);
- }
-
- @ReactProp(name = "cy")
- public void setCy(RadialGradientView node, Dynamic cy) {
- node.setCy(cy);
- }
-
- @ReactProp(name = "gradient")
- public void setGradient(RadialGradientView node, ReadableArray gradient) {
- node.setGradient(gradient);
- }
-
- @ReactProp(name = "gradientUnits")
- public void setGradientUnits(RadialGradientView node, int gradientUnits) {
- node.setGradientUnits(gradientUnits);
- }
-
- @ReactProp(name = "gradientTransform")
- public void setGradientTransform(RadialGradientView node, @Nullable ReadableArray matrixArray) {
- node.setGradientTransform(matrixArray);
- }
- }
-
- private RenderableViewManager(SVGClass svgclass) {
- svgClass = svgclass;
- mClassName = svgclass.toString();
- }
-
- @Nonnull
- @Override
- public String getName() {
- return mClassName;
- }
-
- @ReactProp(name = "mask")
- public void setMask(VirtualView node, String mask) {
- node.setMask(mask);
- }
-
- @ReactProp(name = "markerStart")
- public void setMarkerStart(VirtualView node, String markerStart) {
- node.setMarkerStart(markerStart);
- }
-
- @ReactProp(name = "markerMid")
- public void setMarkerMid(VirtualView node, String markerMid) {
- node.setMarkerMid(markerMid);
- }
-
- @ReactProp(name = "markerEnd")
- public void setMarkerEnd(VirtualView node, String markerEnd) {
- node.setMarkerEnd(markerEnd);
- }
-
- @ReactProp(name = "clipPath")
- public void setClipPath(VirtualView node, String clipPath) {
- node.setClipPath(clipPath);
- }
-
- @ReactProp(name = "clipRule")
- public void setClipRule(VirtualView node, int clipRule) {
- node.setClipRule(clipRule);
- }
-
- @ReactProp(name = "opacity", defaultFloat = 1f)
- public void setOpacity(@Nonnull VirtualView node, float opacity) {
- node.setOpacity(opacity);
- }
-
- @ReactProp(name = "fill")
- public void setFill(RenderableView node, @Nullable Dynamic fill) {
- node.setFill(fill);
- }
-
- @ReactProp(name = "fillOpacity", defaultFloat = 1f)
- public void setFillOpacity(RenderableView node, float fillOpacity) {
- node.setFillOpacity(fillOpacity);
- }
-
- @ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO)
- public void setFillRule(RenderableView node, int fillRule) {
- node.setFillRule(fillRule);
- }
-
-
- @ReactProp(name = "stroke")
- public void setStroke(RenderableView node, @Nullable Dynamic strokeColors) {
- node.setStroke(strokeColors);
- }
-
- @ReactProp(name = "strokeOpacity", defaultFloat = 1f)
- public void setStrokeOpacity(RenderableView node, float strokeOpacity) {
- node.setStrokeOpacity(strokeOpacity);
- }
-
- @ReactProp(name = "strokeDasharray")
- public void setStrokeDasharray(RenderableView node, @Nullable ReadableArray strokeDasharray) {
- node.setStrokeDasharray(strokeDasharray);
- }
-
- @ReactProp(name = "strokeDashoffset")
- public void setStrokeDashoffset(RenderableView node, float strokeDashoffset) {
- node.setStrokeDashoffset(strokeDashoffset);
- }
-
- @ReactProp(name = "strokeWidth")
- public void setStrokeWidth(RenderableView node, Dynamic strokeWidth) {
- node.setStrokeWidth(strokeWidth);
- }
-
- @ReactProp(name = "strokeMiterlimit", defaultFloat = 4f)
- public void setStrokeMiterlimit(RenderableView node, float strokeMiterlimit) {
- node.setStrokeMiterlimit(strokeMiterlimit);
- }
-
- @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND)
- public void setStrokeLinecap(RenderableView node, int strokeLinecap) {
- node.setStrokeLinecap(strokeLinecap);
- }
-
- @ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND)
- public void setStrokeLinejoin(RenderableView node, int strokeLinejoin) {
- node.setStrokeLinejoin(strokeLinejoin);
- }
-
- @ReactProp(name = "vectorEffect")
- public void setVectorEffect(RenderableView node, int vectorEffect) {
- node.setVectorEffect(vectorEffect);
- }
-
- @ReactProp(name = "matrix")
- public void setMatrix(VirtualView node, Dynamic matrixArray) {
- node.setMatrix(matrixArray);
- }
-
- @ReactProp(name = "transform")
- public void setTransform(VirtualView node, Dynamic matrix) {
- if (matrix.getType() != ReadableType.Array) {
- return;
- }
- ReadableArray ma = matrix.asArray();
- if (ma == null) {
- resetTransformProperty(node);
- } else {
- setTransformProperty(node, ma);
- }
- Matrix m = node.getMatrix();
- node.mTransform = m;
- node.mTransformInvertible = m.invert(node.mInvTransform);
- }
-
- @ReactProp(name = "propList")
- public void setPropList(RenderableView node, @Nullable ReadableArray propList) {
- node.setPropList(propList);
- }
-
- @ReactProp(name = "responsible")
- public void setResponsible(VirtualView node, boolean responsible) {
- node.setResponsible(responsible);
- }
-
- @ReactProp(name = ViewProps.POINTER_EVENTS)
- public void setPointerEvents(VirtualView view, @androidx.annotation.Nullable String pointerEventsStr) {
- if (pointerEventsStr == null) {
- view.setPointerEvents(PointerEvents.AUTO);
- } else {
- PointerEvents pointerEvents =
- PointerEvents.valueOf(pointerEventsStr.toUpperCase(Locale.US).replace("-", "_"));
- view.setPointerEvents(pointerEvents);
- }
- }
-
- @ReactProp(name = "onLayout")
- public void setOnLayout(VirtualView node, boolean onLayout) {
- node.setOnLayout(onLayout);
- }
-
- @ReactProp(name = "name")
- public void setName(VirtualView node, String name) {
- node.setName(name);
- }
-
- @ReactProp(name = "display")
- public void setDisplay(VirtualView node, String display) {
- node.setDisplay(display);
- }
-
- private void invalidateSvgView(VirtualView node) {
- SvgView view = node.getSvgView();
- if (view!= null) {
- view.invalidate();
- }
- if (node instanceof TextView) {
- ((TextView)node).getTextContainer().clearChildCache();
- }
- }
-
- @Override
- protected void addEventEmitters(@Nonnull ThemedReactContext reactContext, @Nonnull VirtualView view) {
- super.addEventEmitters(reactContext, view);
- view.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
- @Override
- public void onChildViewAdded(View view, View view1) {
- if (view instanceof VirtualView) {
- invalidateSvgView((VirtualView) view);
- }
- }
-
- @Override
- public void onChildViewRemoved(View view, View view1) {
- if (view instanceof VirtualView) {
- invalidateSvgView((VirtualView) view);
- }
- }
- });
- }
-
- /**
- * Callback that will be triggered after all properties are updated in current update transaction
- * (all @ReactProp handlers for properties updated in current transaction have been called). If
- * you want to override this method you should call super.onAfterUpdateTransaction from it as
- * the parent class of the ViewManager may rely on callback being executed.
- */
- @Override
- protected void onAfterUpdateTransaction(@Nonnull VirtualView node) {
- super.onAfterUpdateTransaction(node);
- invalidateSvgView(node);
- }
-
- @Nonnull
- @Override
- protected VirtualView createViewInstance(@Nonnull ThemedReactContext reactContext) {
- switch (svgClass) {
- case RNSVGGroup:
- return new GroupView(reactContext);
- case RNSVGPath:
- return new PathView(reactContext);
- case RNSVGCircle:
- return new CircleView(reactContext);
- case RNSVGEllipse:
- return new EllipseView(reactContext);
- case RNSVGLine:
- return new LineView(reactContext);
- case RNSVGRect:
- return new RectView(reactContext);
- case RNSVGText:
- return new TextView(reactContext);
- case RNSVGTSpan:
- return new TSpanView(reactContext);
- case RNSVGTextPath:
- return new TextPathView(reactContext);
- case RNSVGImage:
- return new ImageView(reactContext);
- case RNSVGClipPath:
- return new ClipPathView(reactContext);
- case RNSVGDefs:
- return new DefsView(reactContext);
- case RNSVGUse:
- return new UseView(reactContext);
- case RNSVGSymbol:
- return new SymbolView(reactContext);
- case RNSVGLinearGradient:
- return new LinearGradientView(reactContext);
- case RNSVGRadialGradient:
- return new RadialGradientView(reactContext);
- case RNSVGPattern:
- return new PatternView(reactContext);
- case RNSVGMask:
- return new MaskView(reactContext);
- case RNSVGMarker:
- return new MarkerView(reactContext);
- case RNSVGForeignObject:
- return new ForeignObjectView(reactContext);
- default:
- throw new IllegalStateException("Unexpected type " + svgClass.toString());
- }
- }
-
- private static final SparseArray mTagToRenderableView = new SparseArray<>();
- private static final SparseArray mTagToRunnable = new SparseArray<>();
-
- static void setRenderableView(int tag, RenderableView svg) {
- mTagToRenderableView.put(tag, svg);
- Runnable task = mTagToRunnable.get(tag);
- if (task != null) {
- task.run();
- mTagToRunnable.delete(tag);
- }
- }
-
- static void runWhenViewIsAvailable(int tag, Runnable task) {
- mTagToRunnable.put(tag, task);
- }
-
- static @Nullable RenderableView getRenderableViewByTag(int tag) {
- return mTagToRenderableView.get(tag);
- }
-
- @Override
- public void onDropViewInstance(@Nonnull VirtualView view) {
- super.onDropViewInstance(view);
- mTagToRenderableView.remove(view.getId());
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SVGLength.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SVGLength.java
deleted file mode 100644
index 2f584fe5d90ce..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SVGLength.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReadableArray;
-
-import java.util.ArrayList;
-
-class SVGLength {
- // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLength
- public enum UnitType {
- UNKNOWN,
- NUMBER,
- PERCENTAGE,
- EMS,
- EXS,
- PX,
- CM,
- MM,
- IN,
- PT,
- PC,
- }
-
- final double value;
- final UnitType unit;
- private SVGLength() {
- value = 0;
- unit = UnitType.UNKNOWN;
- }
- SVGLength(double number) {
- value = number;
- unit = UnitType.NUMBER;
- }
- private SVGLength(String length) {
- length = length.trim();
- int stringLength = length.length();
- int percentIndex = stringLength - 1;
- if (stringLength == 0 || length.equals("normal")) {
- unit = UnitType.UNKNOWN;
- value = 0;
- } else if (length.codePointAt(percentIndex) == '%') {
- unit = UnitType.PERCENTAGE;
- value = Double.valueOf(length.substring(0, percentIndex));
- } else {
- int twoLetterUnitIndex = stringLength - 2;
- if (twoLetterUnitIndex > 0) {
- String lastTwo = length.substring(twoLetterUnitIndex);
- int end = twoLetterUnitIndex;
- switch (lastTwo) {
- case "px":
- unit = UnitType.NUMBER;
- break;
-
- case "em":
- unit = UnitType.EMS;
- break;
- case "ex":
- unit = UnitType.EXS;
- break;
-
- case "pt":
- unit = UnitType.PT;
- break;
-
- case "pc":
- unit = UnitType.PC;
- break;
-
- case "mm":
- unit = UnitType.MM;
- break;
-
- case "cm":
- unit = UnitType.CM;
- break;
-
- case "in":
- unit = UnitType.IN;
- break;
-
- default:
- unit = UnitType.NUMBER;
- end = stringLength;
- }
- value = Double.valueOf(length.substring(0, end));
- } else {
- unit = UnitType.NUMBER;
- value = Double.valueOf(length);
- }
- }
- }
-
- static SVGLength from(Dynamic dynamic) {
- switch (dynamic.getType()) {
- case Number:
- return new SVGLength(dynamic.asDouble());
- case String:
- return new SVGLength(dynamic.asString());
- default:
- return new SVGLength();
- }
- }
-
- static String toString(Dynamic dynamic) {
- switch (dynamic.getType()) {
- case Number:
- return String.valueOf(dynamic.asDouble());
- case String:
- return dynamic.asString();
- default:
- return null;
- }
- }
-
- static ArrayList arrayFrom(Dynamic dynamic) {
- switch (dynamic.getType()) {
- case Number: {
- ArrayList list = new ArrayList<>(1);
- list.add(new SVGLength(dynamic.asDouble()));
- return list;
- }
- case Array: {
- ReadableArray arr = dynamic.asArray();
- int size = arr.size();
- ArrayList list = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- Dynamic val = arr.getDynamic(i);
- list.add(from(val));
- }
- return list;
- }
- case String: {
- ArrayList list = new ArrayList<>(1);
- list.add(new SVGLength(dynamic.asString()));
- return list;
- }
- default:
- return null;
- }
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgPackage.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgPackage.java
deleted file mode 100644
index f5e67e96f3af7..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgPackage.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import com.facebook.react.ReactPackage;
-import com.facebook.react.bridge.JavaScriptModule;
-import com.facebook.react.bridge.NativeModule;
-import com.facebook.react.bridge.ReactApplicationContext;
-import com.facebook.react.uimanager.ViewManager;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-import static versioned.host.exp.exponent.modules.api.components.svg.RenderableViewManager.*;
-
-public class SvgPackage implements ReactPackage {
-
- @Nonnull
- @Override
- public List createViewManagers(@Nonnull ReactApplicationContext reactContext) {
- return Arrays.asList(
- new GroupViewManager(),
- new PathViewManager(),
- new CircleViewManager(),
- new EllipseViewManager(),
- new LineViewManager(),
- new RectViewManager(),
- new TextViewManager(),
- new TSpanViewManager(),
- new TextPathViewManager(),
- new ImageViewManager(),
- new ClipPathViewManager(),
- new DefsViewManager(),
- new UseViewManager(),
- new SymbolManager(),
- new LinearGradientManager(),
- new RadialGradientManager(),
- new PatternManager(),
- new MaskManager(),
- new ForeignObjectManager(),
- new MarkerManager(),
- new SvgViewManager());
- }
-
- @Nonnull
- @Override
- public List createNativeModules(@Nonnull ReactApplicationContext reactContext) {
- return Arrays.asList(
- new SvgViewModule(reactContext),
- new RNSVGRenderableManager(reactContext)
- );
- }
-
- @SuppressWarnings("unused")
- public List> createJSModules() {
- return Collections.emptyList();
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgView.java
deleted file mode 100644
index c486cfd822c88..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgView.java
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.util.Base64;
-import android.view.View;
-import android.view.ViewParent;
-
-import androidx.annotation.NonNull;
-
-import com.facebook.react.bridge.ColorPropConverter;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.DisplayMetricsHolder;
-import com.facebook.react.uimanager.ReactCompoundView;
-import com.facebook.react.uimanager.ReactCompoundViewGroup;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.views.view.ReactViewGroup;
-
-import java.io.ByteArrayOutputStream;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * Custom {@link View} implementation that draws an RNSVGSvg React view and its children.
- */
-@SuppressLint("ViewConstructor")
-public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactCompoundViewGroup {
-
- @Override
- public boolean interceptsTouchEvent(float touchX, float touchY) {
- return true;
- }
-
- @SuppressWarnings("unused")
- public enum Events {
- EVENT_DATA_URL("onDataURL");
-
- private final String mName;
-
- Events(final String name) {
- mName = name;
- }
-
- @Nonnull
- public String toString() {
- return mName;
- }
- }
-
- private @Nullable Bitmap mBitmap;
-
- public SvgView(ReactContext reactContext) {
- super(reactContext);
- mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
- }
-
- @Override
- public void setId(int id) {
- super.setId(id);
- SvgViewManager.setSvgView(id, this);
- }
-
- @Override
- public void invalidate() {
- super.invalidate();
- ViewParent parent = getParent();
- if (parent instanceof VirtualView) {
- if (!mRendered) {
- return;
- }
- mRendered = false;
- ((VirtualView) parent).getSvgView().invalidate();
- return;
- }
- if (mBitmap != null) {
- mBitmap.recycle();
- }
- mBitmap = null;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (getParent() instanceof VirtualView) {
- return;
- }
- super.onDraw(canvas);
- if (mBitmap == null) {
- mBitmap = drawOutput();
- }
- if (mBitmap != null) {
- canvas.drawBitmap(mBitmap, 0, 0, null);
- if (toDataUrlTask != null) {
- toDataUrlTask.run();
- toDataUrlTask = null;
- }
- }
- }
-
- private Runnable toDataUrlTask = null;
-
- void setToDataUrlTask(Runnable task) {
- toDataUrlTask = task;
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- this.invalidate();
- }
-
- @Override
- public int reactTagForTouch(float touchX, float touchY) {
- return hitTest(touchX, touchY);
- }
-
- private boolean mResponsible = false;
-
- private final Map mDefinedClipPaths = new HashMap<>();
- private final Map mDefinedTemplates = new HashMap<>();
- private final Map mDefinedMarkers = new HashMap<>();
- private final Map mDefinedMasks = new HashMap<>();
- private final Map mDefinedBrushes = new HashMap<>();
- private Canvas mCanvas;
- private final float mScale;
-
- private float mMinX;
- private float mMinY;
- private float mVbWidth;
- private float mVbHeight;
- private SVGLength mbbWidth;
- private SVGLength mbbHeight;
- private String mAlign;
- private int mMeetOrSlice;
- final Matrix mInvViewBoxMatrix = new Matrix();
- private boolean mInvertible = true;
- private boolean mRendered = false;
- int mTintColor = 0;
-
- boolean notRendered() {
- return !mRendered;
- }
-
- private void clearChildCache() {
- if (!mRendered) {
- return;
- }
- mRendered = false;
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof VirtualView) {
- VirtualView n = ((VirtualView)node);
- n.clearChildCache();
- }
- }
- }
-
- @ReactProp(name = "tintColor")
- public void setTintColor(@Nullable Dynamic tintColor) {
- switch (tintColor.getType()) {
- case Map:
- mTintColor = ColorPropConverter.getColor(tintColor.asMap(), getContext());
- break;
- case Number:
- mTintColor = tintColor.asInt();
- break;
- default:
- mTintColor = 0;
- }
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "minX")
- public void setMinX(float minX) {
- mMinX = minX;
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "minY")
- public void setMinY(float minY) {
- mMinY = minY;
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(float vbWidth) {
- mVbWidth = vbWidth;
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(float vbHeight) {
- mVbHeight = vbHeight;
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "bbWidth")
- public void setBbWidth(Dynamic bbWidth) {
- mbbWidth = SVGLength.from(bbWidth);
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "bbHeight")
- public void setBbHeight(Dynamic bbHeight) {
- mbbHeight = SVGLength.from(bbHeight);
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "align")
- public void setAlign(String align) {
- mAlign = align;
- invalidate();
- clearChildCache();
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(int meetOrSlice) {
- mMeetOrSlice = meetOrSlice;
- invalidate();
- clearChildCache();
- }
-
- private Bitmap drawOutput() {
- mRendered = true;
- float width = getWidth();
- float height = getHeight();
- boolean invalid = Float.isNaN(width) || Float.isNaN(height) || width < 1 || height < 1 || (Math.log10(width) + Math.log10(height) > 42);
- if (invalid) {
- return null;
- }
- Bitmap bitmap = Bitmap.createBitmap(
- (int) width,
- (int) height,
- Bitmap.Config.ARGB_8888);
-
- drawChildren(new Canvas(bitmap));
- return bitmap;
- }
-
- Rect getCanvasBounds() {
- return mCanvas.getClipBounds();
- }
-
- synchronized void drawChildren(final Canvas canvas) {
- mRendered = true;
- mCanvas = canvas;
- Matrix mViewBoxMatrix = new Matrix();
- if (mAlign != null) {
- RectF vbRect = getViewBox();
- float width = canvas.getWidth();
- float height = canvas.getHeight();
- boolean nested = getParent() instanceof VirtualView;
- if (nested) {
- width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12);
- height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12);
- }
- RectF eRect = new RectF(0,0, width, height);
- if (nested) {
- canvas.clipRect(eRect);
- }
- mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
- mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix);
- canvas.concat(mViewBoxMatrix);
- }
-
- final Paint paint = new Paint();
-
- paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
-
- paint.setTypeface(Typeface.DEFAULT);
-
-
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof VirtualView) {
- ((VirtualView)node).saveDefinition();
- }
- }
-
- for (int i = 0; i < getChildCount(); i++) {
- View lNode = getChildAt(i);
- if (lNode instanceof VirtualView) {
- VirtualView node = (VirtualView)lNode;
- int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix);
- node.render(canvas, paint, 1f);
- node.restoreCanvas(canvas, count);
-
- if (node.isResponsible() && !mResponsible) {
- mResponsible = true;
- }
- }
- }
- }
-
- private RectF getViewBox() {
- return new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
- }
-
- String toDataURL() {
- Bitmap bitmap = Bitmap.createBitmap(
- getWidth(),
- getHeight(),
- Bitmap.Config.ARGB_8888);
-
- clearChildCache();
- drawChildren(new Canvas(bitmap));
- clearChildCache();
- this.invalidate();
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
- bitmap.recycle();
- byte[] bitmapBytes = stream.toByteArray();
- return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
- }
-
- String toDataURL(int width, int height) {
- Bitmap bitmap = Bitmap.createBitmap(
- width,
- height,
- Bitmap.Config.ARGB_8888);
-
- clearChildCache();
- drawChildren(new Canvas(bitmap));
- clearChildCache();
- this.invalidate();
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
- bitmap.recycle();
- byte[] bitmapBytes = stream.toByteArray();
- return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
- }
-
- void enableTouchEvents() {
- if (!mResponsible) {
- mResponsible = true;
- }
- }
-
- boolean isResponsible() {
- return mResponsible;
- }
-
- private int hitTest(float touchX, float touchY) {
- if (!mResponsible || !mInvertible) {
- return getId();
- }
-
- float[] transformed = { touchX, touchY };
- mInvViewBoxMatrix.mapPoints(transformed);
-
- int count = getChildCount();
- int viewTag = -1;
- for (int i = count - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (child instanceof VirtualView) {
- viewTag = ((VirtualView) child).hitTest(transformed);
- } else if (child instanceof SvgView) {
- viewTag = ((SvgView) child).hitTest(touchX, touchY);
- }
- if (viewTag != -1) {
- break;
- }
- }
-
- return viewTag == -1 ? getId() : viewTag;
- }
-
- void defineClipPath(VirtualView clipPath, String clipPathRef) {
- mDefinedClipPaths.put(clipPathRef, clipPath);
- }
-
- VirtualView getDefinedClipPath(String clipPathRef) {
- return mDefinedClipPaths.get(clipPathRef);
- }
-
- void defineTemplate(VirtualView template, String templateRef) {
- mDefinedTemplates.put(templateRef, template);
- }
-
- VirtualView getDefinedTemplate(String templateRef) {
- return mDefinedTemplates.get(templateRef);
- }
-
- void defineBrush(Brush brush, String brushRef) {
- mDefinedBrushes.put(brushRef, brush);
- }
-
- Brush getDefinedBrush(String brushRef) {
- return mDefinedBrushes.get(brushRef);
- }
-
- void defineMask(VirtualView mask, String maskRef) {
- mDefinedMasks.put(maskRef, mask);
- }
-
- VirtualView getDefinedMask(String maskRef) {
- return mDefinedMasks.get(maskRef);
- }
-
- void defineMarker(VirtualView marker, String markerRef) {
- mDefinedMarkers.put(markerRef, marker);
- }
-
- VirtualView getDefinedMarker(String markerRef) {
- return mDefinedMarkers.get(markerRef);
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgViewManager.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgViewManager.java
deleted file mode 100644
index 368aad587fe11..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgViewManager.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.util.SparseArray;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.uimanager.ThemedReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.views.view.ReactViewGroup;
-import com.facebook.react.views.view.ReactViewManager;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles
- * invalidating the native view on view updates happening in the underlying tree.
- */
-class SvgViewManager extends ReactViewManager {
-
- private static final String REACT_CLASS = "RNSVGSvgView";
-
- private static final SparseArray mTagToSvgView = new SparseArray<>();
- private static final SparseArray mTagToRunnable = new SparseArray<>();
-
- static void setSvgView(int tag, SvgView svg) {
- mTagToSvgView.put(tag, svg);
- Runnable task = mTagToRunnable.get(tag);
- if (task != null) {
- task.run();
- mTagToRunnable.delete(tag);
- }
- }
-
- static void runWhenViewIsAvailable(int tag, Runnable task) {
- mTagToRunnable.put(tag, task);
- }
-
- static @Nullable SvgView getSvgViewByTag(int tag) {
- return mTagToSvgView.get(tag);
- }
-
- @Nonnull
- @Override
- public String getName() {
- return REACT_CLASS;
- }
-
- @Nonnull
- @Override
- public SvgView createViewInstance(ThemedReactContext reactContext) {
- return new SvgView(reactContext);
- }
-
- @Override
- public void updateExtraData(ReactViewGroup root, Object extraData) {
- super.updateExtraData(root, extraData);
- root.invalidate();
- }
-
- @Override
- public void onDropViewInstance(@Nonnull ReactViewGroup view) {
- super.onDropViewInstance(view);
- mTagToSvgView.remove(view.getId());
- }
-
- @Override
- public boolean needsCustomLayoutForChildren() {
- return true;
- }
-
- @ReactProp(name = "tintColor")
- public void setTintColor(SvgView node, @Nullable Dynamic tintColor) {
- node.setTintColor(tintColor);
- }
-
- @ReactProp(name = "color")
- public void setColor(SvgView node, @Nullable Dynamic color) {
- node.setTintColor(color);
- }
-
- @ReactProp(name = "minX")
- public void setMinX(SvgView node, float minX) {
- node.setMinX(minX);
- }
-
- @ReactProp(name = "minY")
- public void setMinY(SvgView node, float minY) {
- node.setMinY(minY);
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(SvgView node, float vbWidth) {
- node.setVbWidth(vbWidth);
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(SvgView node, float vbHeight) {
- node.setVbHeight(vbHeight);
- }
-
- @ReactProp(name = "bbWidth")
- public void setBbWidth(SvgView node, Dynamic bbWidth) {
- node.setBbWidth(bbWidth);
- }
-
- @ReactProp(name = "bbHeight")
- public void setBbHeight(SvgView node, Dynamic bbHeight) {
- node.setBbHeight(bbHeight);
- }
-
- @ReactProp(name = "align")
- public void setAlign(SvgView node, String align) {
- node.setAlign(align);
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(SvgView node, int meetOrSlice) {
- node.setMeetOrSlice(meetOrSlice);
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgViewModule.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgViewModule.java
deleted file mode 100644
index 8acb6883355d5..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SvgViewModule.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import com.facebook.react.bridge.Callback;
-import com.facebook.react.bridge.ReactApplicationContext;
-import com.facebook.react.bridge.ReactContextBaseJavaModule;
-import com.facebook.react.bridge.ReactMethod;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.bridge.UiThreadUtil;
-
-import javax.annotation.Nonnull;
-
-class SvgViewModule extends ReactContextBaseJavaModule {
- SvgViewModule(ReactApplicationContext reactContext) {
- super(reactContext);
- }
-
- @Nonnull
- @Override
- public String getName() {
- return "RNSVGSvgViewManager";
- }
-
- private static void toDataURL(final int tag, final ReadableMap options, final Callback successCallback, final int attempt) {
- UiThreadUtil.runOnUiThread(
- new Runnable() {
- @Override
- public void run() {
- SvgView svg = SvgViewManager.getSvgViewByTag(tag);
-
- if (svg == null) {
- SvgViewManager.runWhenViewIsAvailable(tag, new Runnable() {
- @Override
- public void run() {
- SvgView svg = SvgViewManager.getSvgViewByTag(tag);
- if (svg == null) { // Should never happen
- return;
- }
- svg.setToDataUrlTask(new Runnable() {
- @Override
- public void run() {
- toDataURL(tag, options, successCallback, attempt + 1);
- }
- });
- }
- });
- } else if (svg.notRendered()) {
- svg.setToDataUrlTask(new Runnable() {
- @Override
- public void run() {
- toDataURL(tag, options, successCallback, attempt + 1);
- }
- });
- } else {
- if (options != null) {
- successCallback.invoke(
- svg.toDataURL(
- options.getInt("width"),
- options.getInt("height")
- )
- );
- } else {
- successCallback.invoke(svg.toDataURL());
- }
- }
- }
- }
- );
- }
-
- @SuppressWarnings("unused")
- @ReactMethod
- public void toDataURL(int tag, ReadableMap options, Callback successCallback) {
- toDataURL(tag, options, successCallback, 0);
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SymbolView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SymbolView.java
deleted file mode 100644
index 7ada03c9ff444..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/SymbolView.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-@SuppressLint("ViewConstructor")
-class SymbolView extends GroupView {
-
- private float mMinX;
- private float mMinY;
- private float mVbWidth;
- private float mVbHeight;
- private String mAlign;
- private int mMeetOrSlice;
-
- public SymbolView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "minX")
- public void setMinX(float minX) {
- mMinX = minX;
- invalidate();
- }
-
- @ReactProp(name = "minY")
- public void setMinY(float minY) {
- mMinY = minY;
- invalidate();
- }
-
- @ReactProp(name = "vbWidth")
- public void setVbWidth(float vbWidth) {
- mVbWidth = vbWidth;
- invalidate();
- }
-
- @ReactProp(name = "vbHeight")
- public void setVbHeight(float vbHeight) {
- mVbHeight = vbHeight;
- invalidate();
- }
-
- @ReactProp(name = "align")
- public void setAlign(String align) {
- mAlign = align;
- invalidate();
- }
-
- @ReactProp(name = "meetOrSlice")
- public void setMeetOrSlice(int meetOrSlice) {
- mMeetOrSlice = meetOrSlice;
- invalidate();
- }
-
- @Override
- void draw(Canvas canvas, Paint paint, float opacity) {
- saveDefinition();
- }
-
- void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) {
- if (mAlign != null) {
- RectF vbRect = new RectF(mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
- RectF eRect = new RectF(0, 0, width, height);
- Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
- canvas.concat(viewBoxMatrix);
- super.draw(canvas, paint, opacity);
- }
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TSpanView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TSpanView.java
deleted file mode 100644
index e800ee704ef9e..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TSpanView.java
+++ /dev/null
@@ -1,1251 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-
-import android.annotation.SuppressLint;
-import android.content.res.AssetManager;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PathMeasure;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.text.Layout;
-import android.text.SpannableString;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.view.View;
-import android.view.ViewParent;
-
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableMap;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.views.text.ReactFontManager;
-
-import java.util.ArrayList;
-
-import java.text.Bidi;
-
-import javax.annotation.Nullable;
-
-import static android.graphics.Matrix.MTRANS_X;
-import static android.graphics.Matrix.MTRANS_Y;
-import static android.graphics.PathMeasure.POSITION_MATRIX_FLAG;
-import static android.graphics.PathMeasure.TANGENT_MATRIX_FLAG;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.AlignmentBaseline;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.FontStyle;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.FontVariantLigatures;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.FontWeight;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.TextAnchor;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.TextPathMidLine;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.TextPathSide;
-
-@SuppressLint("ViewConstructor")
-class TSpanView extends TextView {
- private static final double tau = 2 * Math.PI;
- private static final double radToDeg = 360 / tau;
-
- private static final String FONTS = "fonts/";
- private static final String OTF = ".otf";
- private static final String TTF = ".ttf";
-
- private Path mCachedPath;
- @Nullable String mContent;
- private TextPathView textPath;
- private final ArrayList emoji = new ArrayList<>();
- private final ArrayList emojiTransforms = new ArrayList<>();
- private final AssetManager assets;
-
- public TSpanView(ReactContext reactContext) {
- super(reactContext);
- assets = mContext.getResources().getAssets();
- }
-
- @ReactProp(name = "content")
- public void setContent(@Nullable String content) {
- mContent = content;
- invalidate();
- }
-
- @Override
- public void invalidate() {
- mCachedPath = null;
- super.invalidate();
- }
-
- void clearCache() {
- mCachedPath = null;
- super.clearCache();
- }
-
- @Override
- void draw(Canvas canvas, Paint paint, float opacity) {
- if (mContent != null) {
- if (mInlineSize != null && mInlineSize.value != 0) {
- if (setupFillPaint(paint, opacity * fillOpacity)) {
- drawWrappedText(canvas, paint);
- }
- if (setupStrokePaint(paint, opacity * strokeOpacity)) {
- drawWrappedText(canvas, paint);
- }
- } else {
- int numEmoji = emoji.size();
- if (numEmoji > 0) {
- GlyphContext gc = getTextRootGlyphContext();
- FontData font = gc.getFont();
- applyTextPropertiesToPaint(paint, font);
- for (int i = 0; i < numEmoji; i++) {
- String current = emoji.get(i);
- Matrix mid = emojiTransforms.get(i);
- canvas.save();
- canvas.concat(mid);
- canvas.drawText(current, 0, 0, paint);
- canvas.restore();
- }
- }
- drawPath(canvas, paint, opacity);
- }
- } else {
- clip(canvas, paint);
- drawGroup(canvas, paint, opacity);
- }
- }
-
- private void drawWrappedText(Canvas canvas, Paint paint) {
- GlyphContext gc = getTextRootGlyphContext();
- pushGlyphContext();
- FontData font = gc.getFont();
- TextPaint tp = new TextPaint(paint);
- applyTextPropertiesToPaint(tp, font);
- applySpacingAndFeatures(tp, font);
- double fontSize = gc.getFontSize();
-
- Layout.Alignment align;
- switch (font.textAnchor) {
- default:
- case start:
- align = Layout.Alignment.ALIGN_NORMAL;
- break;
-
- case middle:
- align = Layout.Alignment.ALIGN_CENTER;
- break;
-
- case end:
- align = Layout.Alignment.ALIGN_OPPOSITE;
- break;
- }
-
- boolean includeFontPadding = true;
- SpannableString text = new SpannableString(mContent);
- final double width = PropHelper.fromRelative(mInlineSize, canvas.getWidth(), 0, mScale, fontSize);
- StaticLayout layout = getStaticLayout(tp, align, includeFontPadding, text, (int) width);
-
- int lineAscent = layout.getLineAscent(0);
-
- float dx = (float) gc.nextX(0);
- float dy = (float) (gc.nextY() + lineAscent);
- popGlyphContext();
-
- canvas.save();
- canvas.translate(dx, dy);
- layout.draw(canvas);
- canvas.restore();
- }
-
- @SuppressWarnings("deprecation")
- private StaticLayout getStaticLayout(TextPaint tp, Layout.Alignment align, boolean includeFontPadding, SpannableString text, int width) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- return new StaticLayout(
- text,
- tp,
- width,
- align,
- 1.f,
- 0.f,
- includeFontPadding);
- } else {
- return StaticLayout.Builder.obtain(text, 0, text.length(), tp, width)
- .setAlignment(align)
- .setLineSpacing(0.f, 1.f)
- .setIncludePad(includeFontPadding)
- .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
- .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
- .build();
- }
- }
-
-
- /**
- * Implements visual to logical order converter.
- *
- * @author Nesterovsky bros
- *
- * @param text an input text in visual order to convert.
- * @return a String value in logical order.
- */
- public static String visualToLogical(String text)
- {
- if ((text == null) || (text.length() == 0))
- {
- return text;
- }
-
- Bidi bidi = new Bidi(text, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
-
- if (bidi.isLeftToRight())
- {
- return text;
- }
-
- int count = bidi.getRunCount();
- byte[] levels = new byte[count];
- Integer[] runs = new Integer[count];
-
- for (int i = 0; i < count; i++)
- {
- levels[i] = (byte)bidi.getRunLevel(i);
- runs[i] = i;
- }
-
- Bidi.reorderVisually(levels, 0, runs, 0, count);
-
- StringBuilder result = new StringBuilder();
-
- for (int i = 0; i < count; i++)
- {
- int index = runs[i];
- int start = bidi.getRunStart(index);
- int end = bidi.getRunLimit(index);
- int level = levels[index];
-
- if ((level & 1) != 0)
- {
- for (; --end >= start;)
- {
- result.append(text.charAt(end));
- }
- }
- else
- {
- result.append(text, start, end);
- }
- }
-
- return result.toString();
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- if (mCachedPath != null) {
- return mCachedPath;
- }
-
- if (mContent == null) {
- mCachedPath = getGroupPath(canvas, paint);
- return mCachedPath;
- }
-
- setupTextPath();
-
- pushGlyphContext();
- mCachedPath = getLinePath(visualToLogical(mContent), paint, canvas);
- popGlyphContext();
-
- return mCachedPath;
- }
-
- double getSubtreeTextChunksTotalAdvance(Paint paint) {
- if (!Double.isNaN(cachedAdvance)) {
- return cachedAdvance;
- }
- double advance = 0;
-
- if (mContent == null) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof TextView) {
- TextView text = (TextView)child;
- advance += text.getSubtreeTextChunksTotalAdvance(paint);
- }
- }
- cachedAdvance = advance;
- return advance;
- }
-
- String line = mContent;
- final int length = line.length();
-
- if (length == 0) {
- cachedAdvance = 0;
- return advance;
- }
-
- GlyphContext gc = getTextRootGlyphContext();
- FontData font = gc.getFont();
- applyTextPropertiesToPaint(paint, font);
-
- applySpacingAndFeatures(paint, font);
-
- cachedAdvance = paint.measureText(line);
- return cachedAdvance;
- }
-
- final static String requiredFontFeatures = "'rlig', 'liga', 'clig', 'calt', 'locl', 'ccmp', 'mark', 'mkmk',";
- final static String disableDiscretionaryLigatures = "'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, ";
- final static String defaultFeatures = requiredFontFeatures + "'kern', ";
- final static String additionalLigatures = "'hlig', 'cala', ";
- final static String fontWeightTag = "'wght' ";
-
- private void applySpacingAndFeatures(Paint paint, FontData font) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- double letterSpacing = font.letterSpacing;
- paint.setLetterSpacing((float) (letterSpacing / (font.fontSize * mScale)));
-
- final boolean allowOptionalLigatures = letterSpacing == 0 &&
- font.fontVariantLigatures == FontVariantLigatures.normal;
-
- if (allowOptionalLigatures) {
- paint.setFontFeatureSettings(defaultFeatures + additionalLigatures + font.fontFeatureSettings);
- } else {
- paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings);
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- paint.setFontVariationSettings(fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings);
- }
- }
- }
-
- @SuppressWarnings("ConstantConditions")
- private Path getLinePath(String line, Paint paint, Canvas canvas) {
- final int length = line.length();
- final Path path = new Path();
-
- emoji.clear();
- emojiTransforms.clear();
-
- if (length == 0) {
- return path;
- }
-
- double pathLength = 0;
- PathMeasure pm = null;
- boolean isClosed = false;
- final boolean hasTextPath = textPath != null;
- if (hasTextPath) {
- pm = new PathMeasure(textPath.getTextPath(canvas, paint), false);
- pathLength = pm.getLength();
- isClosed = pm.isClosed();
- if (pathLength == 0) {
- return path;
- }
- }
-
- GlyphContext gc = getTextRootGlyphContext();
- FontData font = gc.getFont();
- applyTextPropertiesToPaint(paint, font);
- GlyphPathBag bag = new GlyphPathBag(paint);
- boolean[] ligature = new boolean[length];
- final char[] chars = line.toCharArray();
-
- /*
- *
- * Three properties affect the space between characters and words:
- *
- * ‘kerning’ indicates whether the user agent should adjust inter-glyph spacing
- * based on kerning tables that are included in the relevant font
- * (i.e., enable auto-kerning) or instead disable auto-kerning
- * and instead set inter-character spacing to a specific length (typically, zero).
- *
- * ‘letter-spacing’ indicates an amount of space that is to be added between text
- * characters supplemental to any spacing due to the ‘kerning’ property.
- *
- * ‘word-spacing’ indicates the spacing behavior between words.
- *
- * Letter-spacing is applied after bidi reordering and is in addition to any word-spacing.
- * Depending on the justification rules in effect, user agents may further increase
- * or decrease the space between typographic character units in order to justify text.
- *
- * */
- double kerning = font.kerning;
- double wordSpacing = font.wordSpacing;
- double letterSpacing = font.letterSpacing;
- final boolean autoKerning = !font.manualKerning;
-
- /*
- 11.1.2. Fonts and glyphs
-
- A font consists of a collection of glyphs together with other information (collectively,
- the font tables) necessary to use those glyphs to present characters on some visual medium.
-
- The combination of the collection of glyphs and the font tables is called the font data.
-
- A font may supply substitution and positioning tables that can be used by a formatter
- (text shaper) to re-order, combine and position a sequence of glyphs to form one or more
- composite glyphs.
-
- The combining may be as simple as a ligature, or as complex as an indic syllable which
- combines, usually with some re-ordering, multiple consonants and vowel glyphs.
-
- The tables may be language dependent, allowing the use of language appropriate letter forms.
-
- When a glyph, simple or composite, represents an indivisible unit for typesetting purposes,
- it is know as a typographic character.
-
- Ligatures are an important feature of advance text layout.
-
- Some ligatures are discretionary while others (e.g. in Arabic) are required.
-
- The following explicit rules apply to ligature formation:
-
- Ligature formation should not be enabled when characters are in different DOM text nodes;
- thus, characters separated by markup should not use ligatures.
-
- Ligature formation should not be enabled when characters are in different text chunks.
-
- Discretionary ligatures should not be used when the spacing between two characters is not
- the same as the default space (e.g. when letter-spacing has a non-default value,
- or text-align has a value of justify and text-justify has a value of distribute).
- (See CSS Text Module Level 3, ([css-text-3]).
-
- SVG attributes such as ‘dx’, ‘textLength’, and ‘spacing’ (in ‘textPath’) that may reposition
- typographic characters do not break discretionary ligatures.
-
- If discretionary ligatures are not desired
- they can be turned off by using the font-variant-ligatures property.
-
- /*
- When the effective letter-spacing between two characters is not zero
- (due to either justification or non-zero computed ‘letter-spacing’),
- user agents should not apply optional ligatures.
- https://www.w3.org/TR/css-text-3/#letter-spacing-property
- */
- final boolean allowOptionalLigatures = letterSpacing == 0 &&
- font.fontVariantLigatures == FontVariantLigatures.normal;
-
- /*
- For OpenType fonts, discretionary ligatures include those enabled by
- the liga, clig, dlig, hlig, and cala features;
- required ligatures are found in the rlig feature.
- https://svgwg.org/svg2-draft/text.html#FontsGlyphs
-
- http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
-
- https://www.microsoft.com/typography/otspec/featurelist.htm
- https://www.microsoft.com/typography/otspec/featuretags.htm
- https://www.microsoft.com/typography/otspec/features_pt.htm
- https://www.microsoft.com/typography/otfntdev/arabicot/features.aspx
- http://unifraktur.sourceforge.net/testcases/enable_opentype_features/
- https://en.wikipedia.org/wiki/List_of_typographic_features
- http://ilovetypography.com/OpenType/opentype-features.html
- https://www.typotheque.com/articles/opentype_features_in_css
- https://practice.typekit.com/lesson/caring-about-opentype-features/
- http://stateofwebtype.com/
-
- 6.12. Low-level font feature settings control: the font-feature-settings property
-
- Name: font-feature-settings
- Value: normal | #
- Initial: normal
- Applies to: all elements
- Inherited: yes
- Percentages: N/A
- Media: visual
- Computed value: as specified
- Animatable: no
-
- https://drafts.csswg.org/css-fonts-3/#default-features
-
- 7.1. Default features
-
- For OpenType fonts, user agents must enable the default features defined in the OpenType
- documentation for a given script and writing mode.
-
- Required ligatures, common ligatures and contextual forms must be enabled by default
- (OpenType features: rlig, liga, clig, calt),
- along with localized forms (OpenType feature: locl),
- and features required for proper display of composed characters and marks
- (OpenType features: ccmp, mark, mkmk).
-
- These features must always be enabled, even when the value of the ‘font-variant’ and
- ‘font-feature-settings’ properties is ‘normal’.
-
- Individual features are only disabled when explicitly overridden by the author,
- as when ‘font-variant-ligatures’ is set to ‘no-common-ligatures’.
-
- TODO For handling complex scripts such as Arabic, Mongolian or Devanagari additional features
- are required.
-
- TODO For upright text within vertical text runs,
- vertical alternates (OpenType feature: vert) must be enabled.
- */
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- // String arabic = "'isol', 'fina', 'medi', 'init', 'rclt', 'mset', 'curs', ";
- if (allowOptionalLigatures) {
- paint.setFontFeatureSettings(defaultFeatures + additionalLigatures + font.fontFeatureSettings);
- } else {
- paint.setFontFeatureSettings(defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings);
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- paint.setFontVariationSettings(fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings);
- }
- }
- // OpenType.js font data
- ReadableMap fontData = font.fontData;
-
- float[] advances = new float[length];
- paint.getTextWidths(line, advances);
-
- /*
- This would give both advances and textMeasure in one call / looping over the text
- double textMeasure = paint.getTextRunAdvances(line, 0, length, 0, length, true, advances, 0);
- */
- /*
- Determine the startpoint-on-the-path for the first glyph using attribute ‘startOffset’
- and property text-anchor.
-
- For text-anchor:start, startpoint-on-the-path is the point
- on the path which represents the point on the path which is ‘startOffset’ distance
- along the path from the start of the path, calculated using the user agent's distance
- along the path algorithm.
-
- For text-anchor:middle, startpoint-on-the-path is the point
- on the path which represents the point on the path which is [ ‘startOffset’ minus half
- of the total advance values for all of the glyphs in the ‘textPath’ element ] distance
- along the path from the start of the path, calculated using the user agent's distance
- along the path algorithm.
-
- For text-anchor:end, startpoint-on-the-path is the point on
- the path which represents the point on the path which is [ ‘startOffset’ minus the
- total advance values for all of the glyphs in the ‘textPath’ element ].
-
- Before rendering the first glyph, the horizontal component of the startpoint-on-the-path
- is adjusted to take into account various horizontal alignment text properties and
- attributes, such as a ‘dx’ attribute value on a ‘tspan’ element.
- */
- final TextAnchor textAnchor = font.textAnchor;
- TextView anchorRoot = getTextAnchorRoot();
- final double textMeasure = anchorRoot.getSubtreeTextChunksTotalAdvance(paint);
- double offset = getTextAnchorOffset(textAnchor, textMeasure);
-
- int side = 1;
- double startOfRendering = 0;
- double endOfRendering = pathLength;
- double fontSize = gc.getFontSize();
- boolean sharpMidLine = false;
- if (hasTextPath) {
- sharpMidLine = textPath.getMidLine() == TextPathMidLine.sharp;
- /*
- Name
- side
- Value
- left | right
- initial value
- left
- Animatable
- yes
-
- Determines the side of the path the text is placed on
- (relative to the path direction).
-
- Specifying a value of right effectively reverses the path.
-
- Added in SVG 2 to allow text either inside or outside closed subpaths
- and basic shapes (e.g. rectangles, circles, and ellipses).
-
- Adding 'side' was resolved at the Sydney (2015) meeting.
- */
- side = textPath.getSide() == TextPathSide.right ? -1 : 1;
- /*
- Name
- startOffset
- Value
- | |
- initial value
- 0
- Animatable
- yes
-
- An offset from the start of the path for the initial current text position,
- calculated using the user agent's distance along the path algorithm,
- after converting the path to the ‘textPath’ element's coordinate system.
-
- If a other than a percentage is given, then the ‘startOffset’
- represents a distance along the path measured in the current user coordinate
- system for the ‘textPath’ element.
-
- If a percentage is given, then the ‘startOffset’ represents a percentage
- distance along the entire path. Thus, startOffset="0%" indicates the start
- point of the path and startOffset="100%" indicates the end point of the path.
-
- Negative values and values larger than the path length (e.g. 150%) are allowed.
-
- Any typographic characters with mid-points that are not on the path are not rendered
-
- For paths consisting of a single closed subpath (including an equivalent path for a
- basic shape), typographic characters are rendered along one complete circuit of the
- path. The text is aligned as determined by the text-anchor property to a position
- along the path set by the ‘startOffset’ attribute.
-
- For the start (end) value, the text is rendered from the start (end) of the line
- until the initial position along the path is reached again.
-
- For the middle, the text is rendered from the middle point in both directions until
- a point on the path equal distance in both directions from the initial position on
- the path is reached.
- */
- final double absoluteStartOffset = getAbsoluteStartOffset(textPath.getStartOffset(), pathLength, fontSize);
- offset += absoluteStartOffset;
- if (isClosed) {
- final double halfPathDistance = pathLength / 2;
- startOfRendering = absoluteStartOffset + (textAnchor == TextAnchor.middle ? -halfPathDistance : 0);
- endOfRendering = startOfRendering + pathLength;
- }
- /*
- TextPathSpacing spacing = textPath.getSpacing();
- if (spacing == TextPathSpacing.auto) {
- // Hmm, what to do here?
- // https://svgwg.org/svg2-draft/text.html#TextPathElementSpacingAttribute
- }
- */
- }
-
- /*
- Name
- method
- Value
- align | stretch
- initial value
- align
- Animatable
- yes
- Indicates the method by which text should be rendered along the path.
-
- A value of align indicates that the typographic character should be rendered using
- simple 2×3 matrix transformations such that there is no stretching/warping of the
- typographic characters. Typically, supplemental rotation, scaling and translation
- transformations are done for each typographic characters to be rendered.
-
- As a result, with align, in fonts where the typographic characters are designed to be
- connected (e.g., cursive fonts), the connections may not align properly when text is
- rendered along a path.
-
- A value of stretch indicates that the typographic character outlines will be converted
- into paths, and then all end points and control points will be adjusted to be along the
- perpendicular vectors from the path, thereby stretching and possibly warping the glyphs.
-
- With this approach, connected typographic characters, such as in cursive scripts,
- will maintain their connections. (Non-vertical straight path segments should be
- converted to Bézier curves in such a way that horizontal straight paths have an
- (approximately) constant offset from the path along which the typographic characters
- are rendered.)
-
- TODO implement stretch
- */
-
- /*
- Name Value Initial value Animatable
- textLength | | See below yes
-
- The author's computation of the total sum of all of the advance values that correspond
- to character data within this element, including the advance value on the glyph
- (horizontal or vertical), the effect of properties letter-spacing and word-spacing and
- adjustments due to attributes ‘dx’ and ‘dy’ on this ‘text’ or ‘tspan’ element or any
- descendants. This value is used to calibrate the user agent's own calculations with
- that of the author.
-
- The purpose of this attribute is to allow the author to achieve exact alignment,
- in visual rendering order after any bidirectional reordering, for the first and
- last rendered glyphs that correspond to this element; thus, for the last rendered
- character (in visual rendering order after any bidirectional reordering),
- any supplemental inter-character spacing beyond normal glyph advances are ignored
- (in most cases) when the user agent determines the appropriate amount to expand/compress
- the text string to fit within a length of ‘textLength’.
-
- If attribute ‘textLength’ is specified on a given element and also specified on an
- ancestor, the adjustments on all character data within this element are controlled by
- the value of ‘textLength’ on this element exclusively, with the possible side-effect
- that the adjustment ratio for the contents of this element might be different than the
- adjustment ratio used for other content that shares the same ancestor. The user agent
- must assume that the total advance values for the other content within that ancestor is
- the difference between the advance value on that ancestor and the advance value for
- this element.
-
- This attribute is not intended for use to obtain effects such as shrinking or
- expanding text.
-
- A negative value is an error (see Error processing).
-
- The ‘textLength’ attribute is only applied when the wrapping area is not defined by the
- TODO shape-inside or the inline-size properties. It is also not applied for any ‘text’ or
- TODO ‘tspan’ element that has forced line breaks (due to a white-space value of pre or
- pre-line).
-
- If the attribute is not specified anywhere within a ‘text’ element, the effect is as if
- the author's computation exactly matched the value calculated by the user agent;
- thus, no advance adjustments are made.
- */
- double scaleSpacingAndGlyphs = 1;
- if (mTextLength != null) {
- final double author = PropHelper.fromRelative(mTextLength, canvas.getWidth(), 0, mScale, fontSize);
- if (author < 0) {
- throw new IllegalArgumentException("Negative textLength value");
- }
- switch (mLengthAdjust) {
- default:
- case spacing:
- letterSpacing += (author - textMeasure) / (length - 1);
- break;
- case spacingAndGlyphs:
- scaleSpacingAndGlyphs = author / textMeasure;
- break;
- }
- }
- final double scaledDirection = scaleSpacingAndGlyphs * side;
-
- /*
- https://developer.mozilla.org/en/docs/Web/CSS/vertical-align
- https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
- https://www.microsoft.com/typography/otspec/base.htm
- http://apike.ca/prog_svg_text_style.html
- https://www.w3schools.com/tags/canvas_textbaseline.asp
- http://vanseodesign.com/web-design/svg-text-baseline-alignment/
- https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align
- https://tympanus.net/codrops/css_reference/vertical-align/
-
- https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty
- 11.10.2.6. The ‘alignment-baseline’ property
-
- This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3]
- https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
-
- The vertical-align property shorthand should be preferred in new content.
-
- SVG 2 introduces some changes to the definition of this property.
- In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed.
- For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and
- 'text-after-edge' should be mapped to 'text-bottom'.
-
- Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property.
- */
- final Paint.FontMetrics fm = paint.getFontMetrics();
- final double descenderDepth = fm.descent;
- final double bottom = descenderDepth + fm.leading;
- final double ascenderHeight = -fm.ascent + fm.leading;
- final double top = -fm.top;
- final double totalHeight = top + bottom;
- double baselineShift = 0;
- String baselineShiftString = getBaselineShift();
- AlignmentBaseline baseline = getAlignmentBaseline();
- if (baseline != null) {
- // TODO alignment-baseline, test / verify behavior
- // TODO get per glyph baselines from font baseline table, for high-precision alignment
- switch (baseline) {
- // https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
- default:
- case baseline:
- // Use the dominant baseline choice of the parent.
- // Match the box’s corresponding baseline to that of its parent.
- baselineShift = 0;
- break;
-
- case textBottom:
- case afterEdge:
- case textAfterEdge:
- // Match the bottom of the box to the bottom of the parent’s content area.
- // text-after-edge = text-bottom
- // text-after-edge = descender depth
- baselineShift = -descenderDepth;
- break;
-
- case alphabetic:
- // Match the box’s alphabetic baseline to that of its parent.
- // alphabetic = 0
- baselineShift = 0;
- break;
-
- case ideographic:
- // Match the box’s ideographic character face under-side baseline to that of its parent.
- // ideographic = descender depth
- baselineShift = -descenderDepth;
- break;
-
- case middle:
- // Align the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent.
- // middle = x height / 2
- Rect bounds = new Rect();
- // this will just retrieve the bounding rect for 'x'
- paint.getTextBounds("x", 0, 1, bounds);
- int xHeight = bounds.height();
- baselineShift = xHeight / 2.0;
- break;
-
- case central:
- // Match the box’s central baseline to the central baseline of its parent.
- // central = (ascender height - descender depth) / 2
- baselineShift = (ascenderHeight - descenderDepth) / 2;
- break;
-
- case mathematical:
- // Match the box’s mathematical baseline to that of its parent.
- // Hanging and mathematical baselines
- // There are no obvious formulas to calculate the position of these baselines.
- // At the time of writing FOP puts the hanging baseline at 80% of the ascender
- // height and the mathematical baseline at 50%.
- baselineShift = 0.5 * ascenderHeight;
- break;
-
- case hanging:
- baselineShift = 0.8 * ascenderHeight;
- break;
-
- case textTop:
- case beforeEdge:
- case textBeforeEdge:
- // Match the top of the box to the top of the parent’s content area.
- // text-before-edge = text-top
- // text-before-edge = ascender height
- baselineShift = ascenderHeight;
- break;
-
- case bottom:
- // Align the top of the aligned subtree with the top of the line box.
- baselineShift = bottom;
- break;
-
- case center:
- // Align the center of the aligned subtree with the center of the line box.
- baselineShift = totalHeight / 2;
- break;
-
- case top:
- // Align the bottom of the aligned subtree with the bottom of the line box.
- baselineShift = top;
- break;
- }
- }
- /*
- 2.2.2. Alignment Shift: baseline-shift longhand
-
- This property specifies by how much the box is shifted up from its alignment point.
- It does not apply when alignment-baseline is top or bottom.
-
- Authors should use the vertical-align shorthand instead of this property.
-
- Values have the following meanings:
-
-
- Raise (positive value) or lower (negative value) by the specified length.
-
- Raise (positive value) or lower (negative value) by the specified percentage of the line-height.
- TODO sub
- Lower by the offset appropriate for subscripts of the parent’s box.
- (The UA should use the parent’s font data to find this offset whenever possible.)
- TODO super
- Raise by the offset appropriate for superscripts of the parent’s box.
- (The UA should use the parent’s font data to find this offset whenever possible.)
-
- User agents may additionally support the keyword baseline as computing to 0
- if is necessary for them to support legacy SVG content.
- Issue: We would prefer to remove this,
- and are looking for feedback from SVG user agents as to whether it’s necessary.
-
- https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift
- */
- if (baselineShiftString != null && !baselineShiftString.isEmpty()) {
- switch (baseline) {
- case top:
- case bottom:
- break;
-
- default:
- switch (baselineShiftString) {
- case "sub":
- // TODO
- if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) {
- int unitsPerEm = fontData.getInt("unitsPerEm");
- ReadableMap tables = fontData.getMap("tables");
- if (tables.hasKey("os2")) {
- ReadableMap os2 = tables.getMap("os2");
- if (os2.hasKey("ySubscriptYOffset")) {
- double subOffset = os2.getDouble("ySubscriptYOffset");
- baselineShift += mScale * fontSize * subOffset / unitsPerEm;
- }
- }
- }
- break;
-
- case "super":
- // TODO
- if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) {
- int unitsPerEm = fontData.getInt("unitsPerEm");
- ReadableMap tables = fontData.getMap("tables");
- if (tables.hasKey("os2")) {
- ReadableMap os2 = tables.getMap("os2");
- if (os2.hasKey("ySuperscriptYOffset")) {
- double superOffset = os2.getDouble("ySuperscriptYOffset");
- baselineShift -= mScale * fontSize * superOffset / unitsPerEm;
- }
- }
- }
- break;
-
- case "baseline":
- break;
-
- default:
- baselineShift -= PropHelper.fromRelative(baselineShiftString, mScale * fontSize, mScale, fontSize);
- }
- break;
- }
- }
-
- final Matrix start = new Matrix();
- final Matrix mid = new Matrix();
- final Matrix end = new Matrix();
-
- final float[] startPointMatrixData = new float[9];
- final float[] endPointMatrixData = new float[9];
-
- for (int index = 0; index < length; index++) {
- char currentChar = chars[index];
- String current = String.valueOf(currentChar);
- boolean alreadyRenderedGraphemeCluster = ligature[index];
-
- /*
- Determine the glyph's charwidth (i.e., the amount which the current text position
- advances horizontally when the glyph is drawn using horizontal text layout).
- */
- boolean hasLigature = false;
- if (alreadyRenderedGraphemeCluster) {
- current = "";
- } else {
- int nextIndex = index;
- while (++nextIndex < length) {
- float nextWidth = advances[nextIndex];
- if (nextWidth > 0) {
- break;
- }
- String nextLigature = current + chars[nextIndex];
- ligature[nextIndex] = true;
- current = nextLigature;
- hasLigature = true;
- }
- }
- double charWidth = paint.measureText(current) * scaleSpacingAndGlyphs;
-
- /*
- For each subsequent glyph, set a new startpoint-on-the-path as the previous
- endpoint-on-the-path, but with appropriate adjustments taking into account
- horizontal kerning tables in the font and current values of various attributes
- and properties, including spacing properties (e.g. letter-spacing and word-spacing)
- and ‘tspan’ elements with values provided for attributes ‘dx’ and ‘dy’. All
- adjustments are calculated as distance adjustments along the path, calculated
- using the user agent's distance along the path algorithm.
- */
- if (autoKerning) {
- double kerned = advances[index] * scaleSpacingAndGlyphs;
- kerning = kerned - charWidth;
- }
-
- boolean isWordSeparator = currentChar == ' ';
- double wordSpace = isWordSeparator ? wordSpacing : 0;
- double spacing = wordSpace + letterSpacing;
- double advance = charWidth + spacing;
-
- double x = gc.nextX(alreadyRenderedGraphemeCluster ? 0 : kerning + advance);
- double y = gc.nextY();
- double dx = gc.nextDeltaX();
- double dy = gc.nextDeltaY();
- double r = gc.nextRotation();
-
- if (alreadyRenderedGraphemeCluster || isWordSeparator) {
- // Skip rendering other grapheme clusters of ligatures (already rendered),
- // But, make sure to increment index positions by making gc.next() calls.
- continue;
- }
-
- advance *= side;
- charWidth *= side;
- double cursor = offset + (x + dx) * side;
- double startPoint = cursor - advance;
-
- if (hasTextPath) {
- /*
- Determine the point on the curve which is charwidth distance along the path from
- the startpoint-on-the-path for this glyph, calculated using the user agent's
- distance along the path algorithm. This point is the endpoint-on-the-path for
- the glyph.
- */
- double endPoint = startPoint + charWidth;
-
- /*
- Determine the midpoint-on-the-path, which is the point on the path which is
- "halfway" (user agents can choose either a distance calculation or a parametric
- calculation) between the startpoint-on-the-path and the endpoint-on-the-path.
- */
- double halfWay = charWidth / 2;
- double midPoint = startPoint + halfWay;
-
- // Glyphs whose midpoint-on-the-path are off the path are not rendered.
- if (midPoint > endOfRendering) {
- continue;
- } else if (midPoint < startOfRendering) {
- continue;
- }
-
- /*
- Determine the glyph-midline, which is the vertical line in the glyph's
- coordinate system that goes through the glyph's x-axis midpoint.
-
- Position the glyph such that the glyph-midline passes through
- the midpoint-on-the-path and is perpendicular to the line
- through the startpoint-on-the-path and the endpoint-on-the-path.
-
- TODO suggest adding a compatibility mid-line rendering attribute to textPath,
- for a chrome/firefox/opera/safari compatible sharp text path rendering,
- which doesn't bend text smoothly along a right angle curve, (like Edge does)
- but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
- https://github.com/w3c/svgwg/issues/337
- */
- final int posAndTanFlags = POSITION_MATRIX_FLAG | TANGENT_MATRIX_FLAG;
- if (sharpMidLine) {
- pm.getMatrix((float) midPoint, mid, posAndTanFlags);
- } else {
- /*
- In the calculation above, if either the startpoint-on-the-path
- or the endpoint-on-the-path is off the end of the path,
- then extend the path beyond its end points with a straight line
- that is parallel to the tangent at the path at its end point
- so that the midpoint-on-the-path can still be calculated.
-
- TODO suggest change in wording of svg spec:
- so that the midpoint-on-the-path can still be calculated.
- to
- so that the angle of the glyph-midline to the x-axis can still be calculated.
- or
- so that the line through the startpoint-on-the-path and the
- endpoint-on-the-path can still be calculated.
- https://github.com/w3c/svgwg/issues/337#issuecomment-318056199
- */
- if (startPoint < 0) {
- pm.getMatrix(0, start, posAndTanFlags);
- start.preTranslate((float) startPoint, 0);
- } else {
- pm.getMatrix((float) startPoint, start, POSITION_MATRIX_FLAG);
- }
-
- pm.getMatrix((float) midPoint, mid, POSITION_MATRIX_FLAG);
-
- if (endPoint > pathLength) {
- pm.getMatrix((float) pathLength, end, posAndTanFlags);
- end.preTranslate((float) (endPoint - pathLength), 0);
- } else {
- pm.getMatrix((float) endPoint, end, POSITION_MATRIX_FLAG);
- }
-
- start.getValues(startPointMatrixData);
- end.getValues(endPointMatrixData);
-
- double startX = startPointMatrixData[MTRANS_X];
- double startY = startPointMatrixData[MTRANS_Y];
- double endX = endPointMatrixData[MTRANS_X];
- double endY = endPointMatrixData[MTRANS_Y];
-
- // line through the startpoint-on-the-path and the endpoint-on-the-path
- double lineX = endX - startX;
- double lineY = endY - startY;
-
- double glyphMidlineAngle = Math.atan2(lineY, lineX);
-
- mid.preRotate((float) (glyphMidlineAngle * radToDeg * side));
- }
-
- /*
- Align the glyph vertically relative to the midpoint-on-the-path based on property
- alignment-baseline and any specified values for attribute ‘dy’ on a ‘tspan’ element.
- */
- mid.preTranslate((float) -halfWay, (float) (dy + baselineShift));
- mid.preScale((float) scaledDirection, (float) side);
- mid.postTranslate(0, (float) y);
- } else {
- mid.setTranslate((float) startPoint, (float) (y + dy + baselineShift));
- }
-
- mid.preRotate((float) r);
-
-
- Path glyph;
- if (hasLigature) {
- glyph = new Path();
- paint.getTextPath(current, 0, current.length(), 0, 0, glyph);
- } else {
- glyph = bag.getOrCreateAndCache(currentChar, current);
- }
- RectF bounds = new RectF();
- glyph.computeBounds(bounds, true);
- float width = bounds.width();
- if (width == 0) { // Render unicode emoji
- canvas.save();
- canvas.concat(mid);
- emoji.add(current);
- emojiTransforms.add(new Matrix(mid));
- canvas.drawText(current, 0, 0, paint);
- canvas.restore();
- } else {
- glyph.transform(mid);
- path.addPath(glyph);
- }
- }
-
- return path;
- }
-
- private double getAbsoluteStartOffset(SVGLength startOffset, double distance, double fontSize) {
- return PropHelper.fromRelative(startOffset, distance, 0, mScale, fontSize);
- }
-
- private double getTextAnchorOffset(TextAnchor textAnchor, double textMeasure) {
- switch (textAnchor) {
- default:
- case start:
- return 0;
-
- case middle:
- return -textMeasure / 2;
-
- case end:
- return -textMeasure;
- }
- }
-
- private void applyTextPropertiesToPaint(Paint paint, FontData font) {
- boolean isBold = font.fontWeight == FontWeight.Bold || font.absoluteFontWeight >= 550;
- boolean isItalic = font.fontStyle == FontStyle.italic;
-
- int style;
- if (isBold && isItalic) {
- style = Typeface.BOLD_ITALIC;
- } else if (isBold) {
- style = Typeface.BOLD;
- } else if (isItalic) {
- style = Typeface.ITALIC;
- } else {
- style = Typeface.NORMAL;
- }
-
- Typeface typeface = null;
- int weight = font.absoluteFontWeight;
- final String fontFamily = font.fontFamily;
- if (fontFamily != null && fontFamily.length() > 0) {
- String otfpath = FONTS + fontFamily + OTF;
- String ttfpath = FONTS + fontFamily + TTF;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- Typeface.Builder builder = new Typeface.Builder(assets, otfpath);
- builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings);
- builder.setWeight(weight);
- builder.setItalic(isItalic);
- typeface = builder.build();
- if (typeface == null) {
- builder = new Typeface.Builder(assets, ttfpath);
- builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings);
- builder.setWeight(weight);
- builder.setItalic(isItalic);
- typeface = builder.build();
- }
- } else {
- try {
- typeface = Typeface.createFromAsset(assets, otfpath);
- typeface = Typeface.create(typeface, style);
- } catch (Exception ignored) {
- try {
- typeface = Typeface.createFromAsset(assets, ttfpath);
- typeface = Typeface.create(typeface, style);
- } catch (Exception ignored2) {
- }
- }
- }
- }
-
- if (typeface == null) {
- try {
- typeface = ReactFontManager.getInstance().getTypeface(fontFamily, style, assets);
- } catch (Exception ignored) {
- }
- }
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- typeface = Typeface.create(typeface, weight, isItalic);
- }
-
- paint.setLinearText(true);
- paint.setSubpixelText(true);
- paint.setTypeface(typeface);
- paint.setTextSize((float) (font.fontSize * mScale));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- paint.setLetterSpacing(0);
- }
- }
-
- private void setupTextPath() {
- ViewParent parent = getParent();
-
- while (parent != null) {
- if (parent.getClass() == TextPathView.class) {
- textPath = (TextPathView) parent;
- break;
- } else if (!(parent instanceof TextView)) {
- break;
- }
-
- parent = parent.getParent();
- }
- }
-
- @Override
- int hitTest(final float[] src) {
- if (mContent == null) {
- return super.hitTest(src);
- }
- if (mPath == null || !mInvertible || !mTransformInvertible) {
- return -1;
- }
-
- float[] dst = new float[2];
- mInvMatrix.mapPoints(dst, src);
- mInvTransform.mapPoints(dst);
- int x = Math.round(dst[0]);
- int y = Math.round(dst[1]);
-
- initBounds();
-
- if (
- (mRegion == null || !mRegion.contains(x, y)) &&
- (mStrokeRegion == null || !mStrokeRegion.contains(x, y))
- ) {
- return -1;
- }
-
- Path clipPath = getClipPath();
- if (clipPath != null) {
- if (!mClipRegion.contains(x, y)) {
- return -1;
- }
- }
-
- return getId();
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextLayoutAlgorithm.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextLayoutAlgorithm.java
deleted file mode 100644
index 8dadfa354fd35..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextLayoutAlgorithm.java
+++ /dev/null
@@ -1,1303 +0,0 @@
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-// TODO implement https://www.w3.org/TR/SVG2/text.html#TextLayoutAlgorithm
-
-import android.graphics.Path;
-import android.graphics.PathMeasure;
-import android.graphics.PointF;
-import android.view.View;
-
-import java.util.ArrayList;
-
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.Direction;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.TextAnchor;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.TextPathSide;
-
-@SuppressWarnings("ALL")
-class TextLayoutAlgorithm {
- class CharacterInformation {
- int index;
- double x = 0;
- double y = 0;
- double advance;
- char character;
- double rotate = 0;
- TextView element;
- boolean hidden = false;
- boolean middle = false;
- boolean resolved = false;
- boolean xSpecified = false;
- boolean ySpecified = false;
- boolean addressable = true;
- boolean anchoredChunk = false;
- boolean rotateSpecified = false;
- boolean firstCharacterInResolvedDescendant = false;
-
- CharacterInformation(int index, char c) {
- this.index = index;
- this.character = c;
- }
- }
-
- class LayoutInput {
- TextView text;
- boolean horizontal;
- }
-
- private void getSubTreeTypographicCharacterPositions(
- ArrayList inTextPath,
- ArrayList subtree,
- StringBuilder line,
- View node,
- TextPathView textPath
- ) {
- if (node instanceof TSpanView) {
- final TSpanView tSpanView = (TSpanView) node;
- String content = tSpanView.mContent;
- if (content == null) {
- for (int i = 0; i < tSpanView.getChildCount(); i++) {
- getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, tSpanView.getChildAt(i), textPath);
- }
- } else {
- for (int i = 0; i < content.length(); i++) {
- subtree.add(tSpanView);
- inTextPath.add(textPath);
- }
- line.append(content);
- }
- } else {
- textPath = node instanceof TextPathView ? (TextPathView) node : textPath;
- for (int i = 0; i < textPath.getChildCount(); i++) {
- getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, textPath.getChildAt(i), textPath);
- }
- }
- }
-
- CharacterInformation[] layoutText(LayoutInput layoutInput) {
-/*
- Setup
-
- Let root be the result of generating
- typographic character positions for the
- ‘text’ element and its subtree, laid out as if it
- were an absolutely positioned element.
-
- This will be a single line of text unless the
- white-space property causes line breaks.
-*/
- TextView text = layoutInput.text;
- StringBuilder line = new StringBuilder();
- ArrayList subtree = new ArrayList<>();
- ArrayList inTextPath = new ArrayList<>();
- getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, text, null);
- final char[] root = line.toString().toCharArray();
-/*
- Let count be the number of DOM characters
- within the ‘text’ element's subtree.
-*/
- int count = root.length;
-/*
-
- Let result be an array of length count
- whose entries contain the per-character information described
- above. Each entry is initialized as follows:
-
- its global index number equal to its position in the array,
- its "x" coordinate set to "unspecified",
- its "y" coordinate set to "unspecified",
- its "rotate" coordinate set to "unspecified",
- its "hidden" flag is false,
- its "addressable" flag is true,
- its "middle" flag is false,
- its "anchored chunk" flag is false.
-*/
- final CharacterInformation[] result = new CharacterInformation[count];
- for (int i = 0; i < count; i++) {
- result[i] = new CharacterInformation(i, root[i]);
- }
-/*
- If result is empty, then return result.
-*/
- if (count == 0) {
- return result;
- }
-/*
-
- Let CSS_positions be an array of length
- count whose entries will be filled with the
- x and y positions of the corresponding
- typographic character in root. The array
- entries are initialized to (0, 0).
-*/
- PointF[] CSS_positions = new PointF[count];
- for (int i = 0; i < count; i++) {
- CSS_positions[i] = new PointF(0, 0);
- }
-/*
- Let "horizontal" be a flag, true if the writing mode of ‘text’
- is horizontal, false otherwise.
-*/
- final boolean horizontal = true;
-/*
- Set flags and assign initial positions
-
- For each array element with index i in
- result:
-*/
- for (int i = 0; i < count; i++) {
-/*
- TODO Set addressable to false if the character at index i was:
-
- part of the text content of a non-rendered element
-
- discarded during layout due to being a
- collapsed
- white space character, a soft hyphen character, or a
- bidi control character; or
-
-
- discarded during layout due to being a
- collapsed
- segment break; or
-
-
- trimmed
- from the start or end of a line.
-
- Since there is collapsible white space not addressable by glyph
- positioning attributes in the following ‘text’ element
- (with a standard font), the "B" glyph will be placed at x=300.
-
-
- A
- B
-
-
- This is because the white space before the "A", and all but one white space
- character between the "A" and "B", is collapsed away or trimmed.
-
-*/
- result[i].addressable = true;
-/*
-
- Set middle to true if the character at index i
- TODO is the second or later character that corresponds to a typographic character.
-*/
- result[i].middle = false;
-/*
-
- TODO If the character at index i corresponds to a typographic character at the beginning of a line, then set the "anchored
- chunk" flag of result[i] to true.
-
- This ensures chunks shifted by text-anchor do not
- span multiple lines.
-*/
- result[i].anchoredChunk = i == 0;
-/*
-
- If addressable is true and middle is false then
- set CSS_positions[i] to the position of the
- TODO corresponding typographic character as determined by the CSS
- renderer. Otherwise, if i > 0, then set
- CSS_positions[i] =
- CSS_positions[i − 1]
-
-*/
- if (result[i].addressable && !result[i].middle) {
- CSS_positions[i].set(0, 0);
- } else if (i > 0) {
- CSS_positions[i].set(CSS_positions[i - 1]);
- }
- }
-/*
-
- Resolve character positioning
-
- Position adjustments (e.g values in a ‘x’ attribute)
- specified by a node apply to all characters in that node including
- characters in the node's descendants. Adjustments specified in
- descendant nodes, however, override adjustments from ancestor
- nodes. This section resolves which adjustments are to be applied to
- which characters. It also directly sets the rotate coordinate
- of result.
-
- Set up:
-
- Let resolve_x, resolve_y,
- resolve_dx, and resolve_dy be arrays of
- length count whose entries are all initialized
- to "unspecified".
-*/
- String[] resolve_x = new String[count];
- String[] resolve_y = new String[count];
- String[] resolve_dx = new String[count];
- String[] resolve_dy = new String[count];
-/*
-
- Set "in_text_path" flag false.
-
- This flag will allow ‘y’ (‘x’)
- attribute values to be ignored for horizontal (vertical)
- text inside ‘textPath’ elements.
-*/
- boolean in_text_path = false;
-/*
- Call the following procedure with the ‘text’ element node.
-
- Procedure: resolve character
- positioning:
-
- A recursive procedure that takes as input a node and
- whose steps are as follows:
-*/
- class CharacterPositioningResolver {
- private int global = 0;
- private boolean horizontal = true;
- private boolean in_text_path = false;
- private CharacterInformation[] result;
- private String[] resolve_x;
- private String[] resolve_y;
- private String[] resolve_dx;
- private String[] resolve_dy;
-
- private CharacterPositioningResolver(
- CharacterInformation[] result,
- String[] resolve_x,
- String[] resolve_y,
- String[] resolve_dx,
- String[] resolve_dy
- ) {
- this.result = result;
- this.resolve_x = resolve_x;
- this.resolve_y = resolve_y;
- this.resolve_dx = resolve_dx;
- this.resolve_dy = resolve_dy;
- }
-
- private void resolveCharacterPositioning(TextView node) {
-/*
- If node is a ‘text’ or ‘tspan’ node:
-*/
- if (node.getClass() == TextView.class || node.getClass() == TSpanView.class) {
-/*
- Let index equal the "global index number" of the
- first character in the node.
-*/
- int index = global;
-/*
- Let x, y, dx, dy
- and rotate be the lists of values from the
- TODO corresponding attributes on node, or empty
- lists if the corresponding attribute was not specified
- or was invalid.
-*/
- // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
- String[] x = new String[]{};
-
- // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
- String[] y = new String[]{};
-
- // Current SVGLengthList
- // https://www.w3.org/TR/SVG/types.html#DataTypeLengths
-
- // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
- String[] dx = new String[]{};
-
- // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
- String[] dy = new String[]{};
-
- // Current SVGLengthList
- // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
-
- // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
- double[] rotate = new double[]{};
-/*
-
- If "in_text_path" flag is false:
- Let new_chunk_count
- = max(length of x, length of y).
-*/
- int new_chunk_count;
- if (!in_text_path) {
- new_chunk_count = Math.max(x.length, y.length);
-/*
-
- Else:
-*/
- } else {
-/*
- If the "horizontal" flag is true:
-
- Let new_chunk_count = length of x.
-*/
- if (horizontal) {
- new_chunk_count = x.length;
-/*
-
- Else:
-
- Let new_chunk_count = length of y.
-*/
- } else {
- new_chunk_count = y.length;
- }
- }
-/*
-
- Let length be the number of DOM characters in the
- subtree rooted at node.
-*/
- String content = ((TSpanView) node).mContent;
- int length = content == null ? 0 : content.length();
-/*
- Let i = 0 and j = 0.
-
- i is an index of addressable characters in the node;
- j is an index of all characters in the node.
-*/
- int i = 0;
- int j = 0;
-/*
- While j < length, do:
-*/
- while (j < length) {
-/*
- This loop applies the ‘x’, ‘y’,
- ‘dx’, ‘dy’ and ‘rotate’
- attributes to the content inside node.
- If the "addressable" flag of result[index +
- j] is true, then:
-*/
- if (result[index + j].addressable) {
-/*
- If i < TODO new_check_count, then (typo)
- set the "anchored chunk" flag of
- result[index + j] to
- true. Else set the flag to false.
-
- Setting the flag to false ensures that ‘x’
- and ‘y’ attributes set in a ‘text’
- element don't create anchored chunk in a ‘textPath’
- element when they should not.
-*/
- result[index + j].anchoredChunk = i < new_chunk_count;
-/*
-
- If i < length of x,
- then set resolve_x[index
- + j] to x[i].
-*/
- if (i < x.length) {
- resolve_x[index + j] = x[i];
- }
-/*
-
- If "in_text_path" flag is true and the "horizontal"
- flag is false, unset
- resolve_x[index].
-
- The ‘x’ attribute is ignored for
- vertical text on a path.
-*/
- if (in_text_path && !horizontal) {
- resolve_x[index] = "";
- }
-/*
-
- If i < length of y,
- then set resolve_y[index
- + j] to y[i].
-*/
- if (i < y.length) {
- resolve_y[index + j] = y[i];
- }
-/*
- If "in_text_path" flag is true and the "horizontal"
- flag is true, unset
- resolve_y[index].
-
- The ‘y’ attribute is ignored for
- horizontal text on a path.
-*/
- if (in_text_path && horizontal) {
- resolve_y[index] = "";
- }
-/*
- If i < length of dx,
- then set resolve_dx[index
- + j] to TODO dy[i]. (typo)
-*/
- if (i < dx.length) {
- resolve_dx[index + j] = dx[i];
- }
-/*
- If i < length of dy,
- then set resolve_dy[index
- + j] to dy[i].
-*/
- if (i < dy.length) {
- resolve_dy[index + j] = dy[i];
- }
-/*
- If i < length of rotate,
- then set the angle value of result[index
- + j] to rotate[i].
- Otherwise, if rotate is not empty, then
- set result[index + j]
- to result[index + j − 1].
-*/
- if (i < rotate.length) {
- result[index + j].rotate = rotate[i];
- } else if (rotate.length != 0) {
- result[index + j].rotate = result[index + j - 1].rotate;
- }
-/*
- Set i = i + 1.
- Set j = j + 1.
-*/
- }
- i++;
- j++;
- }
-/*
- If node is a ‘textPath’ node:
-
- Let index equal the global index number of the
- first character in the node (including descendant nodes).
-*/
- } else if (node.getClass() == TextPathView.class) {
- int index = global;
-/*
- Set the "anchored chunk" flag of result[index]
- to true.
-
- A ‘textPath’ element always creates an anchored chunk.
-*/
- result[index].anchoredChunk = true;
-/*
- Set in_text_path flag true.
-*/
- in_text_path = true;
-/*
- For each child node child of node:
- Resolve glyph
- positioning of child.
-*/
- for (int child = 0; child < node.getChildCount(); child++) {
- resolveCharacterPositioning((TextView) node.getChildAt(child));
- }
-/*
- If node is a ‘textPath’ node:
-
- Set "in_text_path" flag false.
-
-*/
- if (node instanceof TextPathView) {
- in_text_path = false;
- }
- }
- }
- }
-
- CharacterPositioningResolver resolver = new CharacterPositioningResolver(
- result,
- resolve_x,
- resolve_y,
- resolve_dx,
- resolve_dy
- );
-/*
- Adjust positions: dx, dy
-
- The ‘dx’ and ‘dy’ adjustments are applied
- before adjustments due to the ‘textLength’ attribute while
- the ‘x’, ‘y’ and ‘rotate’
- adjustments are applied after.
-
- Let shift be the cumulative x and
- y shifts due to ‘x’ and ‘y’
- attributes, initialized to (0,0).
-*/
- PointF shift = new PointF(0, 0);
-/*
- For each array element with index i in result:
-*/
- for (int i = 0; i < count; i++) {
-/*
- If resolve_x[i] is unspecified, set it to 0.
- If resolve_y[i] is unspecified, set it to 0.
-*/
- if (resolve_x[i].equals("")) {
- resolve_x[i] = "0";
- }
- if (resolve_y[i].equals("")) {
- resolve_y[i] = "0";
- }
-/*
- Let shift.x = shift.x + resolve_x[i]
- and shift.y = shift.y + resolve_y[i].
-*/
- shift.x = shift.x + Float.parseFloat(resolve_x[i]);
- shift.y = shift.y + Float.parseFloat(resolve_y[i]);
-/*
- Let result[i].x = CSS_positions[i].x + shift.x
- and result[i].y = CSS_positions[i].y + shift.y.
-*/
- result[i].x = CSS_positions[i].x + shift.x;
- result[i].y = CSS_positions[i].y + shift.y;
- }
-/*
- TODO Apply ‘textLength’ attribute
-
- Set up:
-
- Define resolved descendant node as a
- descendant of node with a valid ‘textLength’
- attribute that is not itself a descendant node of a
- descendant node that has a valid ‘textLength’
- attribute.
-
- Call the following procedure with the ‘text’ element
- node.
-
- Procedure: resolve text length:
-
- A recursive procedure that takes as input
- a node and whose steps are as follows:
- For each child node child of node:
-
- Resolve text length of child.
-
- Child nodes are adjusted before parent nodes.
-*/
- class TextLengthResolver {
- int global;
-
- private void resolveTextLength(TextView node) {
- /*
-
- If node is a ‘text’ or ‘tspan’ node
- and if the node has a valid ‘textLength’ attribute value:
-*/
- final Class extends TextView> nodeClass = node.getClass();
- final boolean validTextLength = node.mTextLength != null;
- if (
- (nodeClass == TSpanView.class)
- && validTextLength
- ) {
- /*
- Let a = +∞ and b = −∞.
-*/
- double a = Double.POSITIVE_INFINITY;
- double b = Double.NEGATIVE_INFINITY;
-/*
-
-
- Let i and j be the global
- index of the first character and last characters
- in node, respectively.
-*/
- String content = ((TSpanView) node).mContent;
- int i = global;
- int j = i + (content == null ? 0 : content.length());
-/*
- For each index k in the range
- [i, j] where the "addressable" flag
- of result[k] is true:
-
- This loop finds the left-(top-) most and
- right-(bottom-) most extents of the typographic characters within the node and checks for
- forced line breaks.
-*/
- for (int k = i; k <= j; k++) {
- if (!result[i].addressable) {
- continue;
- }
-/*
- If the character at k is a linefeed
- or carriage return, return. No adjustments due to
- ‘textLength’ are made to a node with
- a forced line break.
-*/
- switch (result[i].character) {
- case '\n':
- case '\r':
- return;
- }
-/*
- Let pos = the x coordinate of the position
- in result[k], if the "horizontal"
- flag is true, and the y coordinate otherwise.
-*/
- double pos = horizontal ? result[k].x : result[k].y;
-/*
- Let advance = the advance of
- the typographic character corresponding to
- character k. [NOTE: This advance will be
- negative for RTL horizontal text.]
-*/
- double advance = result[k].advance;
-/*
- Set a =
- min(a, pos, pos
- + advance).
-
-
- Set b =
- max(b, pos, pos
- + advance).
-*/
- a = Math.min(a, Math.min(pos, pos + advance));
- b = Math.max(b, Math.max(pos, pos + advance));
- }
-/*
-
- If a ≠+∞ then:
-
-*/
- if (a != Double.POSITIVE_INFINITY) {
-/*
-
- Find the distance delta = ‘textLength’
- computed value − (b − a).
-*/
- double delta = node.mTextLength.value - (b - a);
-/*
-
- User agents are required to shift the last
- typographic character in the node by
- delta, in the positive x direction
- if the "horizontal" flag is true and if
- direction is
- lrt, in the
- negative x direction if the "horizontal" flag
- is true and direction is
- rtl, or in the
- positive y direction otherwise. User agents
- are free to adjust intermediate
- typographic characters for optimal
- typography. The next steps indicate one way to
- adjust typographic characters when
- the value of ‘lengthAdjust’ is
- spacing.
-
- Find n, the total number of
- typographic characters in this node
- TODO including any descendant nodes that are not resolved
- descendant nodes or within a resolved descendant
- node.
-*/
- int n = 0;
- int resolvedDescendantNodes = 0;
- for (int c = 0; c < node.getChildCount(); c++) {
- if (((TextPathView) node.getChildAt(c)).mTextLength == null) {
- String ccontent = ((TSpanView) node).mContent;
- n += ccontent == null ? 0 : ccontent.length();
- } else {
- result[n].firstCharacterInResolvedDescendant = true;
- resolvedDescendantNodes++;
- }
- }
-/*
- Let n = n + number of
- resolved descendant nodes − 1.
-*/
- n += resolvedDescendantNodes - 1;
-/*
- Each resolved descendant node is treated as if it
- were a single
- typographic character in this
- context.
-
- Find the per-character adjustment δ
- = delta/n.
-
- Let shift = 0.
-*/
- double perCharacterAdjustment = delta / n;
- double shift = 0;
-/*
- For each index k in the range [i,j]:
-*/
- for (int k = i; k <= j; k++) {
-/*
- Add shift to the x coordinate of the
- position in result[k], if the "horizontal"
- flag is true, and to the y coordinate
- otherwise.
-*/
- if (horizontal) {
- result[k].x += shift;
- } else {
- result[k].y += shift;
- }
-/*
- If the "middle" flag for result[k]
- is not true and k is not a character in
- a resolved descendant node other than the first
- character then shift = shift
- + δ.
- */
- if (!result[k].middle && (!result[k].resolved || result[k].firstCharacterInResolvedDescendant)) {
- shift += perCharacterAdjustment;
- }
- }
- }
- }
- }
- }
- TextLengthResolver lengthResolver = new TextLengthResolver();
- lengthResolver.resolveTextLength(text);
-/*
-
- Adjust positions: x, y
-
- This loop applies ‘x’ and ‘y’ values,
- and ensures that text-anchor chunks do not start in
- the middle of a typographic character.
-
- Let shift be the current adjustment due to
- the ‘x’ and ‘y’ attributes,
- initialized to (0,0).
-
- Set index = 1.
-*/
- shift.set(0, 0);
- int index = 1;
-/*
- While index < count:
-*/
- while (index < count) {
-/*
- TODO If resolved_x[index] is set, then let (typo)
- shift.x =
- resolved_x[index] −
- result.x[index].
-*/
- if (resolve_x[index] != null) {
- shift.x = (float) (Double.parseDouble(resolve_x[index]) - result[index].x);
- }
-/*
- TODO If resolved_y[index] is set, then let (typo)
- shift.y =
- resolved_y[index] −
- result.y[index].
-*/
- if (resolve_y[index] != null) {
- shift.y = (float) (Double.parseDouble(resolve_y[index]) - result[index].y);
- }
-/*
- Let result.x[index] =
- result.x[index] + shift.x
- and result.y[index] =
- result.y[index] + shift.y.
-*/
- result[index].x += shift.x;
- result[index].y += shift.y;
-/*
- If the "middle" and "anchored chunk" flags
- of result[index] are both true, then:
-*/
- if (result[index].middle && result[index].anchoredChunk) {
-/*
- Set the "anchored chunk" flag
- of result[index] to false.
-*/
- result[index].anchoredChunk = false;
- }
-/*
-
- If index + 1 < count, then set
- the "anchored chunk" flag
- of result[index + 1] to true.
-*/
- if (index + 1 < count) {
- result[index + 1].anchoredChunk = true;
- }
-/*
- Set index to index + 1.
-*/
- index++;
- }
-/*
-
- Apply anchoring
-
- TODO For each slice result[i..j]
- (inclusive of both i and j), where:
-
- the "anchored chunk" flag of result[i]
- is true,
-
- the "anchored chunk" flags
- of result[k] where i
- < k ≤ j are false, and
-
- j = count − 1 or the "anchored
- chunk" flag of result[j + 1] is
- true;
- do:
-
- This loops over each anchored chunk.
-
- Let a = +∞ and b = −∞.
-
- For each index k in the range
- [i, j] where the "addressable" flag
- of result[k] is true:
-
- This loop finds the left-(top-) most and
- right-(bottom-) most extents of the typographic character within the anchored chunk.
-*/
- int i = 0;
- double a = Double.POSITIVE_INFINITY;
- double b = Double.NEGATIVE_INFINITY;
- double prevA = Double.POSITIVE_INFINITY;
- double prevB = Double.NEGATIVE_INFINITY;
- for (int k = 0; k < count; k++) {
- if (!result[k].addressable) {
- continue;
- }
- if (result[k].anchoredChunk) {
- prevA = a;
- prevB = b;
- a = Double.POSITIVE_INFINITY;
- b = Double.NEGATIVE_INFINITY;
- }
-/*
- Let pos = the x coordinate of the position
- in result[k], if the "horizontal" flag
- is true, and the y coordinate otherwise.
-
- Let advance = the advance of
- the typographic character corresponding to
- character k. [NOTE: This advance will be
- negative for RTL horizontal text.]
-
- Set a =
- min(a, pos, pos
- + advance).
-
- Set b =
- max(b, pos, pos
- + advance).
-*/
- double pos = horizontal ? result[k].x : result[k].y;
- double advance = result[k].advance;
- a = Math.min(a, Math.min(pos, pos + advance));
- b = Math.max(b, Math.max(pos, pos + advance));
-/*
- If a ≠+∞, then:
-
- Here we perform the text anchoring.
-
- Let shift be the x coordinate of
- result[i], if the "horizontal" flag
- is true, and the y coordinate otherwise.
-
- TODO Adjust shift based on the value of text-anchor
- TODO and direction of the element the character at
- index i is in:
-
- (start, ltr) or (end, rtl)
- Set shift = shift − a.
- (start, rtl) or (end, ltr)
- Set shift = shift − b.
- (middle, ltr) or (middle, rtl)
- Set shift = shift − (a + b) / 2.
-*/
- if ((k > 0 && result[k].anchoredChunk && prevA != Double.POSITIVE_INFINITY) || k == count - 1) {
- TextAnchor anchor = TextAnchor.start;
- Direction direction = Direction.ltr;
-
- if (k == count - 1) {
- prevA = a;
- prevB = b;
- }
-
- double anchorShift = horizontal ? result[i].x : result[i].y;
- switch (anchor) {
- case start:
- if (direction == Direction.ltr) {
- anchorShift = anchorShift - prevA;
- } else {
- anchorShift = anchorShift - prevB;
- }
- break;
-
- case middle:
- if (direction == Direction.ltr) {
- anchorShift = anchorShift - (prevA + prevB) / 2;
- } else {
- anchorShift = anchorShift - (prevA + prevB) / 2;
- }
- break;
-
- case end:
- if (direction == Direction.ltr) {
- anchorShift = anchorShift - prevB;
- } else {
- anchorShift = anchorShift - prevA;
- }
- break;
- }
-/*
- For each index k in the range [i, j]:
-
- Add shift to the x coordinate of the position
- in result[k], if the "horizontal"
- flag is true, and to the y coordinate otherwise.
-*/
- int j = k == count - 1 ? k : k - 1;
- for (int r = i; r <= j; r++) {
- if (horizontal) {
- result[r].x += anchorShift;
- } else {
- result[r].y += anchorShift;
- }
- }
-
- i = k;
- }
- }
-/*
-
- Position on path
-
- Set index = 0.
-
- Set the "in path" flag to false.
-
- Set the "after path" flag to false.
-
- Let path_end be an offset for characters that follow
- a ‘textPath’ element. Set path_end to (0,0).
-
- While index < count:
-*/
- index = 0;
- boolean inPath = false;
- boolean afterPath = false;
- PointF path_end = new PointF(0, 0);
- Path textPath = null;
- PathMeasure pm = new PathMeasure();
- while (index < count) {
-/*
- If the character at index i is within a
- ‘textPath’ element and corresponds to a typographic character, then:
-
- Set "in path" flag to true.
-*/
- final TextPathView textPathView = inTextPath.get(index);
- if (textPathView != null && result[index].addressable) {
- textPath = textPathView.getTextPath(null, null);
- inPath = true;
-/*
-
- If the "middle" flag of
- result[index] is false, then:
-*/
- if (!result[index].middle) {
-/*
- Here we apply ‘textPath’ positioning.
-
- Let path be the equivalent path of
- the basic shape element referenced by
- the ‘textPath’ element, or an empty path if
- the reference is invalid.
-
- If the ‘side’ attribute of
- the ‘textPath’ element is
- 'right', then
- TODO reverse path.
-*/
- Path path = textPath;
- if (textPathView.getSide() == TextPathSide.right) {
-
- }
-/*
- Let length be the length
- of path.
-*/
- pm.setPath(path, false);
- double length = pm.getLength();
-/*
- Let offset be the value of the
- ‘textPath’ element's
- ‘startOffset’ attribute, adjusted
- due to any ‘pathLength’ attribute on the
- referenced element (if the referenced element is
- a ‘path’ element).
-*/
- double offset = textPathView.getStartOffset().value;
-/*
- Let advance = the advance of
- the typographic character corresponding
- to character TODO k. (typo) [NOTE: This advance will
- be negative for RTL horizontal text.]
-*/
- double advance = result[index].advance;
-/*
- Let (x, y)
- and angle be the position and angle
- in result[index].
-*/
- double x = result[index].x;
- double y = result[index].y;
- double angle = result[index].rotate;
-/*
-
- Let mid be a coordinate value depending
- on the value of the "horizontal" flag:
-
- true
- mid is x + advance / 2
- + offset
- false
- mid is y + advance / 2
- + offset
-*/
- double mid = (horizontal ? x : y) + advance / 2 + offset;
-/*
-
- The user agent is free to make any additional adjustments to
- mid necessary to ensure high quality typesetting
- TODO due to a ‘spacing’ value of
- 'auto' or a
- ‘method’ value of
- 'stretch'.
-
- If path is not a closed subpath and
- mid < 0 or mid > length,
- set the "hidden" flag of result[index] to true.
-*/
- if (!pm.isClosed() && (mid < 0 || mid > length)) {
- result[index].hidden = true;
- }
-/*
- If path is a closed subpath depending on
- the values of text-anchor and direction of
- the element the character at index is in:
-*/
- if (pm.isClosed()) {
-/*
- This implements the special wrapping criteria for single
- closed subpaths.
-
- (start, ltr) or (end, rtl)
-
- If mid−offset < 0
- or mid−offset > length,
- set the "hidden" flag of result[index] to true.
-
- (middle, ltr) or (middle, rtl)
-
- If
- If mid−offset < −length/2
- or mid−offset > length/2,
- set the "hidden" flag of result[index] to true.
-
- (start, rtl) or (end, ltr)
-
- If mid−offset < −length
- or mid−offset > 0,
- set the "hidden" flag of result[index] to true.
-*/
- TextAnchor anchor = TextAnchor.start;
- Direction direction = Direction.ltr;
-
- double anchorShift = horizontal ? result[i].x : result[i].y;
- switch (anchor) {
- case start:
- if (direction == Direction.ltr) {
- if (mid < 0 || mid > length) {
- result[index].hidden = true;
- }
- } else {
- if (mid < -length || mid > 0) {
- result[index].hidden = true;
- }
- }
- break;
-
- case middle:
- if (mid < -length / 2 || mid > length / 2) {
- result[index].hidden = true;
- }
- break;
-
- case end:
- if (direction == Direction.ltr) {
- if (mid < -length || mid > 0) {
- result[index].hidden = true;
- }
- } else {
- if (mid < 0 || mid > length) {
- result[index].hidden = true;
- }
- }
- break;
- }
- }
-/*
- Set mid = mid mod length.
-*/
- mid %= length;
-/*
- If the hidden flag is false:
-*/
- if (!result[index].hidden) {
-/*
- Let point be the position and
- t be the unit vector tangent to
- the point mid distance
- along path.
-*/
- float[] point = new float[2];
- float[] t = new float[2];
- pm.getPosTan((float) mid, point, t);
- final double tau = 2 * Math.PI;
- final double radToDeg = 360 / tau;
- final double r = Math.atan2(t[1], t[0]) * radToDeg;
-/*
- If the "horizontal" flag is
-*/
- if (horizontal) {
-/*
- true
-
- Let n be the normal unit vector
- pointing in the direction t + 90°.
-*/
- double normAngle = r + 90;
- double[] n = new double[]{Math.cos(normAngle), Math.sin(normAngle)};
-/*
- Let o be the horizontal distance from the
- TODO vertical center line of the glyph to the alignment point.
-*/
- double o = 0;
-/*
- Then set the position in
- result[index] to
- point -
- o×t +
- y×n.
-
- Let r be the angle from
- the positive x-axis to the tangent.
-
- Set the angle value
- in result[index]
- to angle + r.
-*/
- result[index].rotate += r;
- } else {
-/*
- false
-
- Let n be the normal unit vector
- pointing in the direction t - 90°.
-*/
- double normAngle = r - 90;
- double[] n = new double[]{Math.cos(normAngle), Math.sin(normAngle)};
-/*
- Let o be the vertical distance from the
- TODO horizontal center line of the glyph to the alignment point.
-*/
- double o = 0;
-/*
-
- Then set the position in
- result[index] to
- point -
- o×t +
- x×n.
-
- Let r be the angle from
- the positive y-axis to the tangent.
-
- Set the angle value
- in result[index]
- to angle + r.
-*/
- result[index].rotate += r;
- }
- }
-/*
-
- Otherwise, the "middle" flag
- of result[index] is true:
-
- Set the position and angle values
- of result[index] to those
- in result[index − 1].
-*/
- } else {
- result[index].x = result[index - 1].x;
- result[index].y = result[index - 1].y;
- result[index].rotate = result[index - 1].rotate;
- }
-
- }
-/*
- If the character at index i is not within a
- ‘textPath’ element and corresponds to a typographic character, then:
-
- This sets the starting point for rendering any characters that
- occur after a ‘textPath’ element to the end of the path.
-*/
- if (textPathView == null && result[index].addressable) {
-/*
- If the "in path" flag is true:
-
- Set the "in path" flag to false.
-
- Set the "after path" flag to true.
-
- Set path_end equal to the end point of the path
- referenced by ‘textPath’ − the position of
- result[index].
-*/
- if (inPath) {
- inPath = false;
- afterPath = true;
- pm.setPath(textPath, false);
- float[] pos = new float[2];
- pm.getPosTan(pm.getLength(), pos, null);
- path_end.set(pos[0], pos[1]);
- }
-/*
-
- If the "after path" is true.
-
- If anchored chunk of
- result[index] is true, set the
- "after path" flag to false.
-
- Else,
- let result.x[index] =
- result.x[index] + path_end.x
- and result.y[index] =
- result.y[index] + path_end.y.
-*/
- if (afterPath) {
- if (result[index].anchoredChunk) {
- afterPath = false;
- } else {
- result[index].x += path_end.x;
- result[index].y += path_end.y;
- }
- }
- }
-/*
-
- Set index = index + 1.
-*/
- index++;
- }
-/*
- Return result
-*/
- return result;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextPathView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextPathView.java
deleted file mode 100644
index 192ea17045ec4..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextPathView.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-import javax.annotation.Nullable;
-
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.*;
-
-@SuppressLint("ViewConstructor")
-class TextPathView extends TextView {
-
- private String mHref;
- private TextPathSide mSide;
- private TextPathMidLine mMidLine;
- private @Nullable SVGLength mStartOffset;
- private TextPathMethod mMethod = TextPathMethod.align;
- private TextPathSpacing mSpacing = TextPathSpacing.exact;
-
- public TextPathView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "href")
- public void setHref(String href) {
- mHref = href;
- invalidate();
- }
-
- @ReactProp(name = "startOffset")
- public void setStartOffset(Dynamic startOffset) {
- mStartOffset = SVGLength.from(startOffset);
- invalidate();
- }
-
- @ReactProp(name = "method")
- public void setMethod(@Nullable String method) {
- mMethod = TextPathMethod.valueOf(method);
- invalidate();
- }
-
- @ReactProp(name = "spacing")
- public void setSpacing(@Nullable String spacing) {
- mSpacing = TextPathSpacing.valueOf(spacing);
- invalidate();
- }
-
- @ReactProp(name = "side")
- public void setSide(@Nullable String side) {
- mSide = TextPathSide.valueOf(side);
- invalidate();
- }
-
- @ReactProp(name = "midLine")
- public void setSharp(@Nullable String midLine) {
- mMidLine = TextPathMidLine.valueOf(midLine);
- invalidate();
- }
-
- @SuppressWarnings("unused")
- TextPathMethod getMethod() {
- return mMethod;
- }
-
- @SuppressWarnings("unused")
- TextPathSpacing getSpacing() {
- return mSpacing;
- }
-
- TextPathSide getSide() {
- return mSide;
- }
-
- TextPathMidLine getMidLine() {
- return mMidLine;
- }
-
- SVGLength getStartOffset() {
- return mStartOffset;
- }
-
- @Override
- void draw(Canvas canvas, Paint paint, float opacity) {
- drawGroup(canvas, paint, opacity);
- }
-
- Path getTextPath(Canvas canvas, Paint paint) {
- SvgView svg = getSvgView();
- VirtualView template = svg.getDefinedTemplate(mHref);
-
- if (!(template instanceof RenderableView)) {
- // warning about this.
- return null;
- }
-
- RenderableView view = (RenderableView)template;
- return view.getPath(canvas, paint);
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- return getGroupPath(canvas, paint);
- }
-
- @Override
- void pushGlyphContext() {
- // do nothing
- }
-
- @Override
- void popGlyphContext() {
- // do nothing
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextProperties.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextProperties.java
deleted file mode 100644
index 27c3639fb2dda..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextProperties.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-class TextProperties {
-
- /*
- https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
- 2.2.1. Alignment Point: alignment-baseline longhand
-
- Name: alignment-baseline
- Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
- Initial: baseline
- Applies to: inline-level boxes, flex items, grid items, table cells
- Inherited: no
- Percentages: N/A
- Media: visual
- Computed value: as specified
- Canonical order: per grammar
- Animation type: discrete
- */
- enum AlignmentBaseline {
- baseline("baseline"),
- textBottom("text-bottom"),
- alphabetic("alphabetic"),
- ideographic("ideographic"),
- middle("middle"),
- central("central"),
- mathematical("mathematical"),
- textTop("text-top"),
- bottom("bottom"),
- center("center"),
- top("top"),
- /*
- SVG implementations may support the following aliases in order to support legacy content:
-
- text-before-edge = text-top
- text-after-edge = text-bottom
- */
- textBeforeEdge("text-before-edge"),
- textAfterEdge("text-after-edge"),
- // SVG 1.1
- beforeEdge("before-edge"),
- afterEdge("after-edge"),
- hanging("hanging"),
- ;
-
- private final String alignment;
-
- AlignmentBaseline(String alignment) {
- this.alignment = alignment;
- }
-
- static AlignmentBaseline getEnum(String strVal) {
- if (!alignmentToEnum.containsKey(strVal)) {
- throw new IllegalArgumentException("Unknown String Value: " + strVal);
- }
- return alignmentToEnum.get(strVal);
- }
-
- private static final Map alignmentToEnum = new HashMap<>();
-
- static {
- for (final AlignmentBaseline en : AlignmentBaseline.values()) {
- alignmentToEnum.put(en.alignment, en);
- }
- }
-
- @Nonnull
- @Override
- public String toString() {
- return alignment;
- }
- }
-
- // TODO implement rtl
- @SuppressWarnings("unused")
- enum Direction {
- ltr,
- rtl
- }
-
- enum FontVariantLigatures {
- normal,
- @SuppressWarnings("unused")none
- }
-
- enum FontStyle {
- normal,
- italic,
- @SuppressWarnings("unused")oblique
- }
-
- enum FontWeight {
- // Absolute
- Normal ("normal"),
- Bold ("bold"),
- w100 ("100"),
- w200 ("200"),
- w300 ("300"),
- w400 ("400"),
- w500 ("500"),
- w600 ("600"),
- w700 ("700"),
- w800 ("800"),
- w900 ("900"),
- // Relative
- Bolder ("bolder"),
- Lighter ("lighter");
-
- private final String weight;
- FontWeight(String weight) {
- this.weight = weight;
- }
-
- static boolean hasEnum(String strVal) {
- return weightToEnum.containsKey(strVal);
- }
-
- static FontWeight get(String strVal) {
- return weightToEnum.get(strVal);
- }
-
- private static final Map weightToEnum = new HashMap<>();
- static {
- for (final FontWeight en : FontWeight.values()) {
- weightToEnum.put(en.weight, en);
- }
- }
-
- @Nonnull
- @Override
- public String toString() {
- return weight;
- }
- }
-
- enum TextAnchor
- {
- start,
- middle,
- end
- }
-
- enum TextDecoration
- {
- None("none"),
- Underline("underline"),
- Overline("overline"),
- LineThrough("line-through"),
- Blink("blink");
-
- private final String decoration;
- TextDecoration(String decoration) {
- this.decoration = decoration;
- }
-
- static TextDecoration getEnum(String strVal) {
- if(!decorationToEnum.containsKey(strVal)) {
- throw new IllegalArgumentException("Unknown String Value: " + strVal);
- }
- return decorationToEnum.get(strVal);
- }
-
- private static final Map decorationToEnum = new HashMap<>();
- static {
- for (final TextDecoration en : TextDecoration.values()) {
- decorationToEnum.put(en.decoration, en);
- }
- }
-
- @Nonnull
- @Override
- public String toString() {
- return decoration;
- }
- }
-
- enum TextLengthAdjust
- {
- spacing,
- spacingAndGlyphs
- }
-
- enum TextPathMethod {
- align,
- @SuppressWarnings("unused")stretch
- }
-
- /*
- TODO suggest adding a compatibility mid-line rendering attribute to textPath,
- for a chrome/firefox/opera/safari compatible sharp text path rendering,
- which doesn't bend text smoothly along a right angle curve, (like Edge does)
- but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
- */
- enum TextPathMidLine {
- sharp,
- @SuppressWarnings("unused")smooth
- }
-
- enum TextPathSide {
- @SuppressWarnings("unused")left,
- right
- }
-
- enum TextPathSpacing {
- @SuppressWarnings("unused")auto,
- exact
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextView.java
deleted file mode 100644
index d46509255dddd..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/TextView.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Region;
-import android.view.View;
-import android.view.ViewParent;
-
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-import java.util.ArrayList;
-
-import javax.annotation.Nullable;
-
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.AlignmentBaseline;
-import static versioned.host.exp.exponent.modules.api.components.svg.TextProperties.TextLengthAdjust;
-
-@SuppressLint("ViewConstructor")
-class TextView extends GroupView {
- SVGLength mInlineSize = null;
- SVGLength mTextLength = null;
- private String mBaselineShift = null;
- TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing;
- private AlignmentBaseline mAlignmentBaseline;
- @Nullable private ArrayList mPositionX;
- @Nullable private ArrayList mPositionY;
- @Nullable private ArrayList mRotate;
- @Nullable private ArrayList mDeltaX;
- @Nullable private ArrayList mDeltaY;
- double cachedAdvance = Double.NaN;
-
- public TextView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @Override
- public void invalidate() {
- if (mPath == null) {
- return;
- }
- super.invalidate();
- getTextContainer().clearChildCache();
- }
-
- void clearCache() {
- cachedAdvance = Double.NaN;
- super.clearCache();
- }
-
- @ReactProp(name = "inlineSize")
- public void setInlineSize(Dynamic inlineSize) {
- mInlineSize = SVGLength.from(inlineSize);
- invalidate();
- }
-
- @ReactProp(name = "textLength")
- public void setTextLength(Dynamic length) {
- mTextLength = SVGLength.from(length);
- invalidate();
- }
-
- @ReactProp(name = "lengthAdjust")
- public void setLengthAdjust(@Nullable String adjustment) {
- mLengthAdjust = TextLengthAdjust.valueOf(adjustment);
- invalidate();
- }
-
- @ReactProp(name = "alignmentBaseline")
- public void setMethod(@Nullable String alignment) {
- mAlignmentBaseline = AlignmentBaseline.getEnum(alignment);
- invalidate();
- }
-
- @ReactProp(name = "baselineShift")
- public void setBaselineShift(Dynamic baselineShift) {
- mBaselineShift = SVGLength.toString(baselineShift);
- invalidate();
- }
-
- @ReactProp(name = "verticalAlign")
- public void setVerticalAlign(@Nullable String verticalAlign) {
- if (verticalAlign != null) {
- verticalAlign = verticalAlign.trim();
- int i = verticalAlign.lastIndexOf(' ');
- try {
- mAlignmentBaseline = AlignmentBaseline.getEnum(verticalAlign.substring(i));
- } catch (IllegalArgumentException e) {
- mAlignmentBaseline = AlignmentBaseline.baseline;
- }
- try {
- mBaselineShift = verticalAlign.substring(0, i);
- } catch (IndexOutOfBoundsException e) {
- mBaselineShift = null;
- }
- } else {
- mAlignmentBaseline = AlignmentBaseline.baseline;
- mBaselineShift = null;
- }
- invalidate();
- }
-
- @ReactProp(name = "rotate")
- public void setRotate(Dynamic rotate) {
- mRotate = SVGLength.arrayFrom(rotate);
- invalidate();
- }
-
- @ReactProp(name = "dx")
- public void setDeltaX(Dynamic deltaX) {
- mDeltaX = SVGLength.arrayFrom(deltaX);
- invalidate();
- }
-
- @ReactProp(name = "dy")
- public void setDeltaY(Dynamic deltaY) {
- mDeltaY = SVGLength.arrayFrom(deltaY);
- invalidate();
- }
-
- @ReactProp(name = "x")
- public void setPositionX(Dynamic positionX) {
- mPositionX = SVGLength.arrayFrom(positionX);
- invalidate();
- }
-
- @ReactProp(name = "y")
- public void setPositionY(Dynamic positionY) {
- mPositionY = SVGLength.arrayFrom(positionY);
- invalidate();
- }
-
- @Override
- void draw(Canvas canvas, Paint paint, float opacity) {
- setupGlyphContext(canvas);
- clip(canvas, paint);
- getGroupPath(canvas, paint);
- pushGlyphContext();
- drawGroup(canvas, paint, opacity);
- popGlyphContext();
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- if (mPath != null) {
- return mPath;
- }
- setupGlyphContext(canvas);
- return getGroupPath(canvas, paint);
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint, Region.Op op) {
- return getPath(canvas, paint);
- }
-
- AlignmentBaseline getAlignmentBaseline() {
- if (mAlignmentBaseline == null) {
- ViewParent parent = this.getParent();
- while (parent != null) {
- if (parent instanceof TextView) {
- TextView node = (TextView)parent;
- final AlignmentBaseline baseline = node.mAlignmentBaseline;
- if (baseline != null) {
- mAlignmentBaseline = baseline;
- return baseline;
- }
- }
- parent = parent.getParent();
- }
- }
- if (mAlignmentBaseline == null) {
- mAlignmentBaseline = AlignmentBaseline.baseline;
- }
- return mAlignmentBaseline;
- }
-
- String getBaselineShift() {
- if (mBaselineShift == null) {
- ViewParent parent = this.getParent();
- while (parent != null) {
- if (parent instanceof TextView) {
- TextView node = (TextView)parent;
- final String baselineShift = node.mBaselineShift;
- if (baselineShift != null) {
- mBaselineShift = baselineShift;
- return baselineShift;
- }
- }
- parent = parent.getParent();
- }
- }
- return mBaselineShift;
- }
-
- Path getGroupPath(Canvas canvas, Paint paint) {
- if (mPath != null) {
- return mPath;
- }
- pushGlyphContext();
- mPath = super.getPath(canvas, paint);
- popGlyphContext();
-
- return mPath;
- }
-
- @Override
- void pushGlyphContext() {
- boolean isTextNode = !(this instanceof TextPathView) && !(this instanceof TSpanView);
- getTextRootGlyphContext().pushContext(isTextNode, this, mFont, mPositionX, mPositionY, mDeltaX, mDeltaY, mRotate);
- }
-
- TextView getTextAnchorRoot() {
- GlyphContext gc = getTextRootGlyphContext();
- ArrayList font = gc.mFontContext;
- TextView node = this;
- ViewParent parent = this.getParent();
- for (int i = font.size() - 1; i >= 0; i--) {
- if (!(parent instanceof TextView) || font.get(i).textAnchor == TextProperties.TextAnchor.start || node.mPositionX != null) {
- return node;
- }
- node = (TextView) parent;
- parent = node.getParent();
- }
- return node;
- }
-
- double getSubtreeTextChunksTotalAdvance(Paint paint) {
- if (!Double.isNaN(cachedAdvance)) {
- return cachedAdvance;
- }
- double advance = 0;
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof TextView) {
- TextView text = (TextView) child;
- advance += text.getSubtreeTextChunksTotalAdvance(paint);
- }
- }
- cachedAdvance = advance;
- return advance;
- }
-
- TextView getTextContainer() {
- TextView node = this;
- ViewParent parent = this.getParent();
- while (parent instanceof TextView) {
- node = (TextView) parent;
- parent = node.getParent();
- }
- return node;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/UseView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/UseView.java
deleted file mode 100644
index dd05cf2643c91..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/UseView.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-
-import com.facebook.common.logging.FLog;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.uimanager.annotations.ReactProp;
-
-@SuppressLint("ViewConstructor")
-class UseView extends RenderableView {
- private String mHref;
- private SVGLength mX;
- private SVGLength mY;
- private SVGLength mW;
- private SVGLength mH;
-
- public UseView(ReactContext reactContext) {
- super(reactContext);
- }
-
- @ReactProp(name = "href")
- public void setHref(String href) {
- mHref = href;
- invalidate();
- }
-
- @ReactProp(name = "x")
- public void setX(Dynamic x) {
- mX = SVGLength.from(x);
- invalidate();
- }
-
- @ReactProp(name = "y")
- public void setY(Dynamic y) {
- mY = SVGLength.from(y);
- invalidate();
- }
-
- @ReactProp(name = "width")
- public void setWidth(Dynamic width) {
- mW = SVGLength.from(width);
- invalidate();
- }
-
- @ReactProp(name = "height")
- public void setHeight(Dynamic height) {
- mH = SVGLength.from(height);
- invalidate();
- }
-
- @Override
- void draw(Canvas canvas, Paint paint, float opacity) {
- VirtualView template = getSvgView().getDefinedTemplate(mHref);
-
- if (template == null) {
- FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
- "template named: " + mHref + " is not defined.");
- return;
- }
-
- template.clearCache();
- canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
- if (template instanceof RenderableView) {
- ((RenderableView)template).mergeProperties(this);
- }
-
- int count = template.saveAndSetupCanvas(canvas, mCTM);
- clip(canvas, paint);
-
- if (template instanceof SymbolView) {
- SymbolView symbol = (SymbolView)template;
- symbol.drawSymbol(canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
- } else {
- template.draw(canvas, paint, opacity * mOpacity);
- }
-
- this.setClientRect(template.getClientRect());
-
- template.restoreCanvas(canvas, count);
- if (template instanceof RenderableView) {
- ((RenderableView)template).resetProperties();
- }
- }
-
- @Override
- int hitTest(float[] src) {
- if (!mInvertible || !mTransformInvertible) {
- return -1;
- }
-
- float[] dst = new float[2];
- mInvMatrix.mapPoints(dst, src);
- mInvTransform.mapPoints(dst);
-
- VirtualView template = getSvgView().getDefinedTemplate(mHref);
- if (template == null) {
- FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
- "template named: " + mHref + " is not defined.");
- return -1;
- }
-
- int hitChild = template.hitTest(dst);
- if (hitChild != -1) {
- return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId();
- }
-
- return -1;
- }
-
- @Override
- Path getPath(Canvas canvas, Paint paint) {
- VirtualView template = getSvgView().getDefinedTemplate(mHref);
- if (template == null) {
- FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
- "template named: " + mHref + " is not defined.");
- return null;
- }
- Path path = template.getPath(canvas, paint);
- Path use = new Path();
- Matrix m = new Matrix();
- m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
- path.transform(m, use);
- return use;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/ViewBox.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/ViewBox.java
deleted file mode 100644
index 50f76b7b2d512..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/ViewBox.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2015-present, Horcrux.
- * All rights reserved.
- *
- * This source code is licensed under the MIT-style license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.graphics.Matrix;
-import android.graphics.RectF;
-
-class ViewBox {
-
- private static final int MOS_MEET = 0;
- private static final int MOS_SLICE = 1;
- private static final int MOS_NONE = 2;
-
- static Matrix getTransform(RectF vbRect, RectF eRect, String align, int meetOrSlice) {
- // based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
-
- // Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively.
- double vbX = vbRect.left;
- double vbY = vbRect.top;
- double vbWidth = vbRect.width();
- double vbHeight = vbRect.height();
-
- // Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
- double eX = eRect.left;
- double eY = eRect.top;
- double eWidth = eRect.width();
- double eHeight = eRect.height();
-
-
- // Initialize scale-x to e-width/vb-width.
- double scaleX = eWidth / vbWidth;
-
- // Initialize scale-y to e-height/vb-height.
- double scaleY = eHeight / vbHeight;
-
- // Initialize translate-x to e-x - (vb-x * scale-x).
- // Initialize translate-y to e-y - (vb-y * scale-y).
- double translateX = eX - (vbX * scaleX);
- double translateY = eY - (vbY * scaleY);
-
- // If align is 'none'
- if (meetOrSlice == MOS_NONE) {
- // Let scale be set the smaller value of scale-x and scale-y.
- // Assign scale-x and scale-y to scale.
- double scale = scaleX = scaleY = Math.min(scaleX, scaleY);
-
- // If scale is greater than 1
- if (scale > 1) {
- // Minus translateX by (eWidth / scale - vbWidth) / 2
- // Minus translateY by (eHeight / scale - vbHeight) / 2
- translateX -= (eWidth / scale - vbWidth) / 2;
- translateY -= (eHeight / scale - vbHeight) / 2;
- } else {
- translateX -= (eWidth - vbWidth * scale) / 2;
- translateY -= (eHeight - vbHeight * scale) / 2;
- }
- } else {
- // If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller.
- // Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger.
-
- if (!align.equals("none") && meetOrSlice == MOS_MEET) {
- scaleX = scaleY = Math.min(scaleX, scaleY);
- } else if (!align.equals("none") && meetOrSlice == MOS_SLICE) {
- scaleX = scaleY = Math.max(scaleX, scaleY);
- }
-
- // If align contains 'xMid', add (e-width - vb-width * scale-x) / 2 to translate-x.
- if (align.contains("xMid")) {
- translateX += (eWidth - vbWidth * scaleX) / 2.0d;
- }
-
- // If align contains 'xMax', add (e-width - vb-width * scale-x) to translate-x.
- if (align.contains("xMax")) {
- translateX += (eWidth - vbWidth * scaleX);
- }
-
- // If align contains 'yMid', add (e-height - vb-height * scale-y) / 2 to translate-y.
- if (align.contains("YMid")) {
- translateY += (eHeight - vbHeight * scaleY) / 2.0d;
- }
-
- // If align contains 'yMax', add (e-height - vb-height * scale-y) to translate-y.
- if (align.contains("YMax")) {
- translateY += (eHeight - vbHeight * scaleY);
- }
-
- }
-
- // The transform applied to content contained by the element is given by
- // translate(translate-x, translate-y) scale(scale-x, scale-y).
- Matrix transform = new Matrix();
- transform.postTranslate((float) translateX, (float) translateY);
- transform.preScale((float) scaleX, (float) scaleY);
- return transform;
- }
-}
diff --git a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/VirtualView.java b/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/VirtualView.java
deleted file mode 100644
index fff5d6fc13e0e..0000000000000
--- a/android/expoview/src/main/java/versioned/host/exp/exponent/modules/api/components/svg/VirtualView.java
+++ /dev/null
@@ -1,601 +0,0 @@
-package versioned.host.exp.exponent.modules.api.components.svg;
-
-import android.annotation.SuppressLint;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.view.View;
-import android.view.ViewParent;
-
-import com.facebook.common.logging.FLog;
-import com.facebook.react.bridge.Dynamic;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.bridge.ReadableType;
-import com.facebook.react.common.ReactConstants;
-import com.facebook.react.uimanager.DisplayMetricsHolder;
-import com.facebook.react.uimanager.OnLayoutEvent;
-import com.facebook.react.uimanager.PointerEvents;
-import com.facebook.react.uimanager.UIManagerModule;
-import com.facebook.react.uimanager.annotations.ReactProp;
-import com.facebook.react.uimanager.events.EventDispatcher;
-import com.facebook.react.views.view.ReactViewGroup;
-
-import java.util.ArrayList;
-
-import javax.annotation.Nullable;
-
-import static versioned.host.exp.exponent.modules.api.components.svg.FontData.DEFAULT_FONT_SIZE;
-
-@SuppressLint("ViewConstructor")
-abstract public class VirtualView extends ReactViewGroup {
- final ReactContext mContext;
-
- VirtualView(ReactContext reactContext) {
- super(reactContext);
- mContext = reactContext;
- mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
- }
-
- /*
- N[1/Sqrt[2], 36]
- The inverse of the square root of 2.
- Provide enough digits for the 128-bit IEEE quad (36 significant digits).
- */
- private static final double M_SQRT1_2l = 0.707106781186547524400844362104849039;
-
- private static final float[] sRawMatrix = new float[]{
- 1, 0, 0,
- 0, 1, 0,
- 0, 0, 1
- };
- float mOpacity = 1f;
- Matrix mCTM = new Matrix();
- Matrix mMatrix = new Matrix();
- Matrix mTransform = new Matrix();
- Matrix mInvCTM = new Matrix();
- Matrix mInvMatrix = new Matrix();
- final Matrix mInvTransform = new Matrix();
- boolean mInvertible = true;
- boolean mCTMInvertible = true;
- boolean mTransformInvertible = true;
- private RectF mClientRect;
-
- int mClipRule;
- private @Nullable String mClipPath;
- @Nullable String mMask;
- @Nullable String mMarkerStart;
- @Nullable String mMarkerMid;
- @Nullable String mMarkerEnd;
-
- private static final int CLIP_RULE_EVENODD = 0;
- static final int CLIP_RULE_NONZERO = 1;
-
- final float mScale;
- private boolean mResponsible;
- private boolean mOnLayout;
- String mDisplay;
- String mName;
-
- private SvgView svgView;
- private Path mCachedClipPath;
- private GroupView mTextRoot;
- private double fontSize = -1;
- private double canvasDiagonal = -1;
- private float canvasHeight = -1;
- private float canvasWidth = -1;
- private GlyphContext glyphContext;
-
- Path mPath;
- Path mFillPath;
- Path mStrokePath;
- Path mMarkerPath;
- Path mClipRegionPath;
- RectF mBox;
- RectF mFillBounds;
- RectF mStrokeBounds;
- RectF mMarkerBounds;
- RectF mClipBounds;
- Region mRegion;
- Region mMarkerRegion;
- Region mStrokeRegion;
- Region mClipRegion;
- ArrayList elements;
- PointerEvents mPointerEvents;
-
- void setPointerEvents(PointerEvents pointerEvents) {
- mPointerEvents = pointerEvents;
- }
-
- @Override
- public void invalidate() {
- if (this instanceof RenderableView && mPath == null) {
- return;
- }
- clearCache();
- clearParentCache();
- super.invalidate();
- }
-
- void clearCache() {
- canvasDiagonal = -1;
- canvasHeight = -1;
- canvasWidth = -1;
- fontSize = -1;
- mStrokeRegion = null;
- mMarkerRegion = null;
- mRegion = null;
- mPath = null;
- }
-
- void clearChildCache() {
- clearCache();
- for (int i = 0; i < getChildCount(); i++) {
- View node = getChildAt(i);
- if (node instanceof VirtualView) {
- ((VirtualView)node).clearChildCache();
- }
- }
- }
-
- private void clearParentCache() {
- VirtualView node = this;
- while (true) {
- ViewParent parent = node.getParent();
- if (!(parent instanceof VirtualView)) {
- return;
- }
- node = (VirtualView)parent;
- if (node.mPath == null) {
- return;
- }
- node.clearCache();
- }
- }
-
- @Nullable
- GroupView getTextRoot() {
- VirtualView node = this;
- if (mTextRoot == null) {
- while (node != null) {
- if (node instanceof GroupView && ((GroupView) node).getGlyphContext() != null) {
- mTextRoot = (GroupView)node;
- break;
- }
-
- ViewParent parent = node.getParent();
-
- if (!(parent instanceof VirtualView)) {
- node = null;
- } else {
- node = (VirtualView)parent;
- }
- }
- }
-
- return mTextRoot;
- }
-
- @Nullable
- GroupView getParentTextRoot() {
- ViewParent parent = this.getParent();
- if (!(parent instanceof VirtualView)) {
- return null;
- } else {
- return ((VirtualView) parent).getTextRoot();
- }
- }
-
-
- private double getFontSizeFromContext() {
- if (fontSize != -1) {
- return fontSize;
- }
- GroupView root = getTextRoot();
- if (root == null) {
- return DEFAULT_FONT_SIZE;
- }
-
- if (glyphContext == null) {
- glyphContext = root.getGlyphContext();
- }
-
- fontSize = glyphContext.getFontSize();
-
- return fontSize;
- }
-
- abstract void draw(Canvas canvas, Paint paint, float opacity);
- void render(Canvas canvas, Paint paint, float opacity) {
- draw(canvas, paint, opacity);
- }
-
- /**
- * Sets up the transform matrix on the canvas before an element is drawn.
- *
- * NB: for perf reasons this does not apply opacity, as that would mean creating a new canvas
- * layer (which allocates an offscreen bitmap) and having it composited afterwards. Instead, the
- * drawing code should apply opacity recursively.
- *
- * @param canvas the canvas to set up
- * @param ctm current transformation matrix
- */
- int saveAndSetupCanvas(Canvas canvas, Matrix ctm) {
- int count = canvas.save();
- mCTM.setConcat(mMatrix, mTransform);
- canvas.concat(mCTM);
- mCTM.preConcat(ctm);
- mCTMInvertible = mCTM.invert(mInvCTM);
- return count;
- }
-
- /**
- * Restore the canvas after an element was drawn. This is always called in mirror with
- * {@link #saveAndSetupCanvas}.
- *
- * @param canvas the canvas to restore
- */
- void restoreCanvas(Canvas canvas, int count) {
- canvas.restoreToCount(count);
- }
-
- @ReactProp(name = "name")
- public void setName(String name) {
- mName = name;
- invalidate();
- }
-
- @ReactProp(name = "display")
- public void setDisplay(String display) {
- mDisplay = display;
- invalidate();
- }
-
- @ReactProp(name = "onLayout")
- public void setOnLayout(boolean onLayout) {
- mOnLayout = onLayout;
- invalidate();
- }
-
- @ReactProp(name = "mask")
- public void setMask(String mask) {
- mMask = mask;
- invalidate();
- }
-
- @ReactProp(name = "markerStart")
- public void setMarkerStart(String markerStart) {
- mMarkerStart = markerStart;
- invalidate();
- }
-
- @ReactProp(name = "markerMid")
- public void setMarkerMid(String markerMid) {
- mMarkerMid = markerMid;
- invalidate();
- }
-
- @ReactProp(name = "markerEnd")
- public void setMarkerEnd(String markerEnd) {
- mMarkerEnd = markerEnd;
- invalidate();
- }
-
- @ReactProp(name = "clipPath")
- public void setClipPath(String clipPath) {
- mCachedClipPath = null;
- mClipPath = clipPath;
- invalidate();
- }
-
- @ReactProp(name = "clipRule", defaultInt = CLIP_RULE_NONZERO)
- public void setClipRule(int clipRule) {
- mClipRule = clipRule;
- invalidate();
- }
-
- @ReactProp(name = "opacity", defaultFloat = 1f)
- public void setOpacity(float opacity) {
- mOpacity = opacity;
- invalidate();
- }
-
- @ReactProp(name = "matrix")
- public void setMatrix(Dynamic matrixArray) {
- ReadableType type = matrixArray.getType();
- if (!matrixArray.isNull() && type.equals(ReadableType.Array)) {
- int matrixSize = PropHelper.toMatrixData(matrixArray.asArray(), sRawMatrix, mScale);
- if (matrixSize == 6) {
- if (mMatrix == null) {
- mMatrix = new Matrix();
- mInvMatrix = new Matrix();
- }
- mMatrix.setValues(sRawMatrix);
- mInvertible = mMatrix.invert(mInvMatrix);
- } else if (matrixSize != -1) {
- FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
- }
- } else {
- mMatrix.reset();
- mInvMatrix.reset();
- mInvertible = true;
- }
-
- super.invalidate();
- clearParentCache();
- }
-
- @ReactProp(name = "responsible")
- public void setResponsible(boolean responsible) {
- mResponsible = responsible;
- invalidate();
- }
-
- @Nullable Path getClipPath() {
- return mCachedClipPath;
- }
-
- @Nullable Path getClipPath(Canvas canvas, Paint paint) {
- if (mClipPath != null) {
- ClipPathView mClipNode = (ClipPathView) getSvgView().getDefinedClipPath(mClipPath);
-
- if (mClipNode != null) {
- Path clipPath = mClipRule == CLIP_RULE_EVENODD ? mClipNode.getPath(canvas, paint) :
- mClipNode.getPath(canvas, paint, Region.Op.UNION);
- clipPath.transform(mClipNode.mMatrix);
- clipPath.transform(mClipNode.mTransform);
- switch (mClipRule) {
- case CLIP_RULE_EVENODD:
- clipPath.setFillType(Path.FillType.EVEN_ODD);
- break;
- case CLIP_RULE_NONZERO:
- break;
- default:
- FLog.w(ReactConstants.TAG, "RNSVG: clipRule: " + mClipRule + " unrecognized");
- }
- mCachedClipPath = clipPath;
- } else {
- FLog.w(ReactConstants.TAG, "RNSVG: Undefined clipPath: " + mClipPath);
- }
- }
-
- return getClipPath();
- }
-
- void clip(Canvas canvas, Paint paint) {
- Path clip = getClipPath(canvas, paint);
-
- if (clip != null) {
- canvas.clipPath(clip);
- }
- }
-
- abstract int hitTest(final float[] point);
-
- boolean isResponsible() {
- return mResponsible;
- }
-
- abstract Path getPath(Canvas canvas, Paint paint);
-
- SvgView getSvgView() {
- if (svgView != null) {
- return svgView;
- }
-
- ViewParent parent = getParent();
-
- if (parent == null) {
- return null;
- } else if (parent instanceof SvgView) {
- svgView = (SvgView)parent;
- } else if (parent instanceof VirtualView) {
- svgView = ((VirtualView) parent).getSvgView();
- } else {
- FLog.e(ReactConstants.TAG, "RNSVG: " + getClass().getName() + " should be descendant of a SvgView.");
- }
-
- return svgView;
- }
-
- double relativeOnWidth(SVGLength length) {
- SVGLength.UnitType unit = length.unit;
- if (unit == SVGLength.UnitType.NUMBER){
- return length.value * mScale;
- } else if (unit == SVGLength.UnitType.PERCENTAGE){
- return length.value / 100 * getCanvasWidth();
- }
- return fromRelativeFast(length);
- }
-
- double relativeOnHeight(SVGLength length) {
- SVGLength.UnitType unit = length.unit;
- if (unit == SVGLength.UnitType.NUMBER){
- return length.value * mScale;
- } else if (unit == SVGLength.UnitType.PERCENTAGE){
- return length.value / 100 * getCanvasHeight();
- }
- return fromRelativeFast(length);
- }
-
- double relativeOnOther(SVGLength length) {
- SVGLength.UnitType unit = length.unit;
- if (unit == SVGLength.UnitType.NUMBER){
- return length.value * mScale;
- } else if (unit == SVGLength.UnitType.PERCENTAGE){
- return length.value / 100 * getCanvasDiagonal();
- }
- return fromRelativeFast(length);
- }
-
- /**
- * Converts SVGLength into px / user units
- * in the current user coordinate system
- *
- * @param length length string
- * @return value in the current user coordinate system
- */
- private double fromRelativeFast(SVGLength length) {
- double unit;
- switch (length.unit) {
- case EMS:
- unit = getFontSizeFromContext();
- break;
- case EXS:
- unit = getFontSizeFromContext() / 2;
- break;
-
- case CM:
- unit = 35.43307;
- break;
- case MM:
- unit = 3.543307;
- break;
- case IN:
- unit = 90;
- break;
- case PT:
- unit = 1.25;
- break;
- case PC:
- unit = 15;
- break;
-
- default:
- unit = 1;
- }
- return length.value * unit * mScale;
- }
-
- private float getCanvasWidth() {
- if (canvasWidth != -1) {
- return canvasWidth;
- }
- GroupView root = getTextRoot();
- if (root == null) {
- canvasWidth = getSvgView().getCanvasBounds().width();
- } else {
- canvasWidth = root.getGlyphContext().getWidth();
- }
-
- return canvasWidth;
- }
-
- private float getCanvasHeight() {
- if (canvasHeight != -1) {
- return canvasHeight;
- }
- GroupView root = getTextRoot();
- if (root == null) {
- canvasHeight = getSvgView().getCanvasBounds().height();
- } else {
- canvasHeight = root.getGlyphContext().getHeight();
- }
-
- return canvasHeight;
- }
-
- private double getCanvasDiagonal() {
- if (canvasDiagonal != -1) {
- return canvasDiagonal;
- }
- double powX = Math.pow((getCanvasWidth()), 2);
- double powY = Math.pow((getCanvasHeight()), 2);
- canvasDiagonal = Math.sqrt(powX + powY) * M_SQRT1_2l;
- return canvasDiagonal;
- }
-
- void saveDefinition() {
- if (mName != null) {
- getSvgView().defineTemplate(this, mName);
- }
- }
-
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = mClientRect != null ?
- (int) Math.ceil(mClientRect.width())
- : getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
-
- int height = mClientRect != null ?
- (int) Math.ceil(mClientRect.height())
- : getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
-
- setMeasuredDimension(width, height);
- }
-
- /**
- * Called from layout when this view should
- * assign a size and position to each of its children.
- *
- * Derived classes with children should override
- * this method and call layout on each of
- * their children.
- * @param changed This is a new size or position for this view
- * @param pleft Left position, relative to parent
- * @param ptop Top position, relative to parent
- * @param pright Right position, relative to parent
- * @param pbottom Bottom position, relative to parent
- */
- protected void onLayout(boolean changed, int pleft, int ptop, int pright, int pbottom) {
- if (mClientRect == null) {
- return;
- }
-
- if (!(this instanceof GroupView)) {
- int left = (int) Math.floor(mClientRect.left);
- int top = (int) Math.floor(mClientRect.top);
- int right = (int) Math.ceil(mClientRect.right);
- int bottom = (int) Math.ceil(mClientRect.bottom);
- setLeft(left);
- setTop(top);
- setRight(right);
- setBottom(bottom);
- }
- int width = (int) Math.ceil(mClientRect.width());
- int height = (int) Math.ceil(mClientRect.height());
- setMeasuredDimension(width, height);
- }
-
- void setClientRect(RectF rect) {
- if (mClientRect != null && mClientRect.equals(rect)) {
- return;
- }
- mClientRect = rect;
- if (mClientRect == null) {
- return;
- }
- int width = (int) Math.ceil(mClientRect.width());
- int height = (int) Math.ceil(mClientRect.height());
- int left = (int) Math.floor(mClientRect.left);
- int top = (int) Math.floor(mClientRect.top);
- int right = (int) Math.ceil(mClientRect.right);
- int bottom = (int) Math.ceil(mClientRect.bottom);
- setMeasuredDimension(width, height);
- if (!(this instanceof GroupView)) {
- setLeft(left);
- setTop(top);
- setRight(right);
- setBottom(bottom);
- }
- if (!mOnLayout) {
- return;
- }
- EventDispatcher eventDispatcher = mContext
- .getNativeModule(UIManagerModule.class)
- .getEventDispatcher();
- eventDispatcher.dispatchEvent(OnLayoutEvent.obtain(
- this.getId(),
- left,
- top,
- width,
- height
- ));
- }
-
- RectF getClientRect() {
- return mClientRect;
- }
-
-}
diff --git a/android/vendored/unversioned/react-native-svg/android/build.gradle b/android/vendored/unversioned/react-native-svg/android/build.gradle
new file mode 100644
index 0000000000000..a4a57b5568c32
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/build.gradle
@@ -0,0 +1,104 @@
+buildscript {
+ // The Android Gradle plugin is only required when opening the android folder stand-alone.
+ // This avoids unnecessary downloads and potential conflicts when the library is included as a
+ // module dependency in an application project.
+ if (project == rootProject) {
+ repositories {
+ mavenCentral()
+ google()
+ }
+
+ dependencies {
+ classpath("com.android.tools.build:gradle:3.6.1")
+ classpath "com.diffplug.spotless:spotless-plugin-gradle:5.15.0"
+ }
+ }
+}
+
+def isNewArchitectureEnabled() {
+ // To opt-in for the New Architecture, you can either:
+ // - Set `newArchEnabled` to true inside the `gradle.properties` file
+ // - Invoke gradle with `-newArchEnabled=true`
+ // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
+ return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
+}
+
+if (isNewArchitectureEnabled()) {
+ apply plugin: "com.facebook.react"
+}
+
+if (project == rootProject) {
+ apply from: 'spotless.gradle'
+}
+
+apply plugin: 'com.android.library'
+
+def safeExtGet(prop, fallback) {
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
+}
+
+android {
+ compileSdkVersion safeExtGet('compileSdkVersion', 28)
+
+ // Used to override the NDK path/version on internal CI or by allowing
+ // users to customize the NDK path/version from their root project (e.g. for M1 support)
+ if (rootProject.hasProperty("ndkPath")) {
+ ndkPath rootProject.ext.ndkPath
+ }
+ if (rootProject.hasProperty("ndkVersion")) {
+ ndkVersion rootProject.ext.ndkVersion
+ }
+
+ defaultConfig {
+ minSdkVersion safeExtGet('minSdkVersion', 16)
+ //noinspection OldTargetApi
+ targetSdkVersion safeExtGet('targetSdkVersion', 28)
+ buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
+ }
+ lintOptions {
+ abortOnError false
+ }
+
+ sourceSets.main {
+ java {
+ if (isNewArchitectureEnabled()) {
+ srcDirs += [
+ "src/fabric/java",
+ ]
+ } else {
+ srcDirs += [
+ "src/paper/java",
+ "build/generated/source/codegen/java"
+ ]
+ }
+ }
+ }
+}
+
+repositories {
+ mavenCentral()
+ mavenLocal()
+ google()
+ maven {
+ // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
+ url "$rootDir/../node_modules/react-native/android"
+ }
+}
+
+dependencies {
+ if (isNewArchitectureEnabled()) {
+ implementation project(":ReactAndroid")
+ } else {
+ implementation 'com.facebook.react:react-native:+'
+ }
+}
+
+if (isNewArchitectureEnabled()) {
+ react {
+ reactRoot = rootProject.file("../node_modules/react-native/")
+ jsRootDir = file("../src/fabric/")
+ codegenDir = rootProject.file("../node_modules/react-native-codegen/")
+ libraryName = "rnsvg"
+ codegenJavaPackageName = "com.horcrux.rnsvg"
+ }
+}
\ No newline at end of file
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/AndroidManifest.xml b/android/vendored/unversioned/react-native-svg/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000000..d85bd62ede9ad
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/Brush.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/Brush.java
new file mode 100644
index 0000000000000..0492cda35c94c
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/Brush.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.common.ReactConstants;
+
+class Brush {
+ private final BrushType mType;
+ private final SVGLength[] mPoints;
+ private ReadableArray mColors;
+ private final boolean mUseObjectBoundingBox;
+
+ // TODO implement pattern units
+ @SuppressWarnings({"unused"})
+ private boolean mUseContentObjectBoundingBoxUnits;
+
+ private Matrix mMatrix;
+ private Rect mUserSpaceBoundingBox;
+ private PatternView mPattern;
+
+ Brush(BrushType type, SVGLength[] points, BrushUnits units) {
+ mType = type;
+ mPoints = points;
+ mUseObjectBoundingBox = units == BrushUnits.OBJECT_BOUNDING_BOX;
+ }
+
+ void setContentUnits(BrushUnits units) {
+ mUseContentObjectBoundingBoxUnits = units == BrushUnits.OBJECT_BOUNDING_BOX;
+ }
+
+ void setPattern(PatternView pattern) {
+ mPattern = pattern;
+ }
+
+ enum BrushType {
+ LINEAR_GRADIENT,
+ RADIAL_GRADIENT,
+ PATTERN
+ }
+
+ enum BrushUnits {
+ OBJECT_BOUNDING_BOX,
+ USER_SPACE_ON_USE
+ }
+
+ private static void parseGradientStops(
+ ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, float opacity) {
+ for (int i = 0; i < stopsCount; i++) {
+ int stopIndex = i * 2;
+ stops[i] = (float) value.getDouble(stopIndex);
+ int color = value.getInt(stopIndex + 1);
+ int alpha = color >>> 24;
+ int combined = Math.round((float) alpha * opacity);
+ stopsColors[i] = combined << 24 | (color & 0x00ffffff);
+ }
+ }
+
+ void setUserSpaceBoundingBox(Rect userSpaceBoundingBox) {
+ mUserSpaceBoundingBox = userSpaceBoundingBox;
+ }
+
+ void setGradientColors(ReadableArray colors) {
+ mColors = colors;
+ }
+
+ void setGradientTransform(Matrix matrix) {
+ mMatrix = matrix;
+ }
+
+ private RectF getPaintRect(RectF pathBoundingBox) {
+ RectF rect = mUseObjectBoundingBox ? pathBoundingBox : new RectF(mUserSpaceBoundingBox);
+ float width = rect.width();
+ float height = rect.height();
+ float x = 0f;
+ float y = 0f;
+
+ if (mUseObjectBoundingBox) {
+ x = rect.left;
+ y = rect.top;
+ }
+
+ return new RectF(x, y, x + width, y + height);
+ }
+
+ private double getVal(SVGLength length, double relative, float scale, float textSize) {
+ return PropHelper.fromRelative(
+ length,
+ relative,
+ 0,
+ mUseObjectBoundingBox && length.unit == SVGLength.UnitType.NUMBER ? relative : scale,
+ textSize);
+ }
+
+ void setupPaint(Paint paint, RectF pathBoundingBox, float scale, float opacity) {
+ RectF rect = getPaintRect(pathBoundingBox);
+ float width = rect.width();
+ float height = rect.height();
+ float offsetX = rect.left;
+ float offsetY = rect.top;
+
+ float textSize = paint.getTextSize();
+ if (mType == BrushType.PATTERN) {
+ double x = getVal(mPoints[0], width, scale, textSize);
+ double y = getVal(mPoints[1], height, scale, textSize);
+ double w = getVal(mPoints[2], width, scale, textSize);
+ double h = getVal(mPoints[3], height, scale, textSize);
+
+ if (!(w > 1 && h > 1)) {
+ return;
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ RectF vbRect = mPattern.getViewBox();
+ if (vbRect != null && vbRect.width() > 0 && vbRect.height() > 0) {
+ RectF eRect = new RectF((float) x, (float) y, (float) w, (float) h);
+ Matrix mViewBoxMatrix =
+ ViewBox.getTransform(vbRect, eRect, mPattern.mAlign, mPattern.mMeetOrSlice);
+ canvas.concat(mViewBoxMatrix);
+ }
+
+ if (mUseContentObjectBoundingBoxUnits) {
+ canvas.scale(width / scale, height / scale);
+ }
+
+ mPattern.draw(canvas, new Paint(), opacity);
+
+ Matrix patternMatrix = new Matrix();
+ if (mMatrix != null) {
+ patternMatrix.preConcat(mMatrix);
+ }
+
+ BitmapShader bitmapShader =
+ new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
+ bitmapShader.setLocalMatrix(patternMatrix);
+ paint.setShader(bitmapShader);
+ return;
+ }
+
+ int size = mColors.size();
+ if (size == 0) {
+ FLog.w(ReactConstants.TAG, "Gradient contains no stops");
+ return;
+ }
+ int stopsCount = size / 2;
+ int[] stopsColors = new int[stopsCount];
+ float[] stops = new float[stopsCount];
+ parseGradientStops(mColors, stopsCount, stops, stopsColors, opacity);
+
+ if (stops.length == 1) {
+ // Gradient with only one stop will make LinearGradient/RadialGradient
+ // throw. It may happen when source SVG contains only one stop or
+ // two stops at the same spot (see lib/extract/extractGradient.js).
+ // Although it's mistake SVGs like this can be produced by vector
+ // editors or other tools, so let's handle that gracefully.
+ stopsColors = new int[] {stopsColors[0], stopsColors[0]};
+ stops = new float[] {stops[0], stops[0]};
+ FLog.w(ReactConstants.TAG, "Gradient contains only one stop");
+ }
+
+ if (mType == BrushType.LINEAR_GRADIENT) {
+ double x1 = getVal(mPoints[0], width, scale, textSize) + offsetX;
+ double y1 = getVal(mPoints[1], height, scale, textSize) + offsetY;
+ double x2 = getVal(mPoints[2], width, scale, textSize) + offsetX;
+ double y2 = getVal(mPoints[3], height, scale, textSize) + offsetY;
+
+ Shader linearGradient =
+ new LinearGradient(
+ (float) x1,
+ (float) y1,
+ (float) x2,
+ (float) y2,
+ stopsColors,
+ stops,
+ Shader.TileMode.CLAMP);
+
+ if (mMatrix != null) {
+ Matrix m = new Matrix();
+ m.preConcat(mMatrix);
+ linearGradient.setLocalMatrix(m);
+ }
+
+ paint.setShader(linearGradient);
+ } else if (mType == BrushType.RADIAL_GRADIENT) {
+ double rx = getVal(mPoints[2], width, scale, textSize);
+ double ry = getVal(mPoints[3], height, scale, textSize);
+
+ double ratio = ry / rx;
+
+ double cx = getVal(mPoints[4], width, scale, textSize) + offsetX;
+ double cy = getVal(mPoints[5], height / ratio, scale, textSize) + offsetY / ratio;
+
+ // TODO: support focus point.
+ // double fx = PropHelper.fromRelative(mPoints[0], width, offsetX, scale);
+ // double fy = PropHelper.fromRelative(mPoints[1], height, offsetY, scale) / (ry / rx);
+
+ Shader radialGradient =
+ new RadialGradient(
+ (float) cx, (float) cy, (float) rx, stopsColors, stops, Shader.TileMode.CLAMP);
+
+ Matrix radialMatrix = new Matrix();
+ radialMatrix.preScale(1f, (float) ratio);
+
+ if (mMatrix != null) {
+ radialMatrix.preConcat(mMatrix);
+ }
+
+ radialGradient.setLocalMatrix(radialMatrix);
+ paint.setShader(radialGradient);
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/CircleView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/CircleView.java
new file mode 100644
index 0000000000000..e31664e203734
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/CircleView.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class CircleView extends RenderableView {
+ private SVGLength mCx;
+ private SVGLength mCy;
+ private SVGLength mR;
+
+ public CircleView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setCx(Dynamic cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCx(String cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCx(Double cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCy(Dynamic cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setCy(String cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setCy(Double cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setR(Dynamic r) {
+ mR = SVGLength.from(r);
+ invalidate();
+ }
+
+ public void setR(String r) {
+ mR = SVGLength.from(r);
+ invalidate();
+ }
+
+ public void setR(Double r) {
+ mR = SVGLength.from(r);
+ invalidate();
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ Path path = new Path();
+
+ double cx = relativeOnWidth(mCx);
+ double cy = relativeOnHeight(mCy);
+ double r = relativeOnOther(mR);
+
+ path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW);
+ return path;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ClipPathView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ClipPathView.java
new file mode 100644
index 0000000000000..a818916748d6d
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ClipPathView.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.common.ReactConstants;
+
+@SuppressLint("ViewConstructor")
+class ClipPathView extends GroupView {
+
+ public ClipPathView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ void draw(Canvas canvas, Paint paint, float opacity) {
+ FLog.w(
+ ReactConstants.TAG,
+ "RNSVG: ClipPath can't be drawn, it should be defined as a child component for `Defs` ");
+ }
+
+ @Override
+ void saveDefinition() {
+ getSvgView().defineClipPath(this, mName);
+ }
+
+ @Override
+ boolean isResponsible() {
+ return false;
+ }
+
+ @Override
+ int hitTest(float[] src) {
+ return -1;
+ }
+
+ @Override
+ void mergeProperties(RenderableView target) {}
+
+ @Override
+ void resetProperties() {}
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/DefinitionView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/DefinitionView.java
new file mode 100644
index 0000000000000..ddc8a71f6a932
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/DefinitionView.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class DefinitionView extends VirtualView {
+
+ DefinitionView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ @SuppressWarnings("EmptyMethod")
+ void draw(Canvas canvas, Paint paint, float opacity) {}
+
+ @Override
+ boolean isResponsible() {
+ return false;
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ return null;
+ }
+
+ @Override
+ int hitTest(float[] src) {
+ return -1;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/DefsView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/DefsView.java
new file mode 100644
index 0000000000000..7a4f4d8e05f81
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/DefsView.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class DefsView extends DefinitionView {
+
+ public DefsView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ void draw(Canvas canvas, Paint paint, float opacity) {}
+
+ void saveDefinition() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof VirtualView) {
+ ((VirtualView) child).saveDefinition();
+ }
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/EllipseView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/EllipseView.java
new file mode 100644
index 0000000000000..2f91d13de360f
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/EllipseView.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class EllipseView extends RenderableView {
+ private SVGLength mCx;
+ private SVGLength mCy;
+ private SVGLength mRx;
+ private SVGLength mRy;
+
+ public EllipseView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setCx(Dynamic cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCx(String cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCx(Double cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCy(Dynamic cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setCy(String cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setCy(Double cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setRx(Dynamic rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRx(String rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRx(Double rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRy(Dynamic ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ public void setRy(String ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ public void setRy(Double ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ Path path = new Path();
+ double cx = relativeOnWidth(mCx);
+ double cy = relativeOnHeight(mCy);
+ double rx = relativeOnWidth(mRx);
+ double ry = relativeOnHeight(mRy);
+ RectF oval =
+ new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry));
+ path.addOval(oval, Path.Direction.CW);
+
+ return path;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/FontData.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/FontData.java
new file mode 100644
index 0000000000000..fb789068f6790
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/FontData.java
@@ -0,0 +1,232 @@
+package com.horcrux.svg;
+
+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.*;
+
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.ReadableType;
+
+class FontData {
+
+ static class AbsoluteFontWeight {
+
+ static final int normal = 400;
+
+ private static final FontWeight[] WEIGHTS =
+ new FontWeight[] {
+ FontWeight.w100,
+ FontWeight.w100,
+ FontWeight.w200,
+ FontWeight.w300,
+ FontWeight.Normal,
+ FontWeight.w500,
+ FontWeight.w600,
+ FontWeight.Bold,
+ FontWeight.w800,
+ FontWeight.w900,
+ FontWeight.w900,
+ };
+
+ static FontWeight nearestFontWeight(int absoluteFontWeight) {
+ return WEIGHTS[Math.round(absoluteFontWeight / 100f)];
+ }
+
+ private static final int[] absoluteFontWeights =
+ new int[] {400, 700, 100, 200, 300, 400, 500, 600, 700, 800, 900};
+
+ // https://drafts.csswg.org/css-fonts-4/#relative-weights
+ static int from(FontWeight fontWeight, FontData parent) {
+ if (fontWeight == FontWeight.Bolder) {
+ return bolder(parent.absoluteFontWeight);
+ } else if (fontWeight == FontWeight.Lighter) {
+ return lighter(parent.absoluteFontWeight);
+ } else {
+ return absoluteFontWeights[fontWeight.ordinal()];
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+
+ 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 FONT_DATA = "fontData";
+ 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";
+ 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;
+
+ FontWeight fontWeight;
+ int absoluteFontWeight;
+
+ final String fontFeatureSettings;
+ final String fontVariationSettings;
+ final FontVariantLigatures fontVariantLigatures;
+
+ final TextAnchor textAnchor;
+ private final TextDecoration textDecoration;
+
+ final double kerning;
+ final double wordSpacing;
+ final double letterSpacing;
+
+ final boolean manualKerning;
+
+ static final FontData Defaults = new FontData();
+
+ private FontData() {
+ fontData = null;
+ fontFamily = "";
+ fontStyle = FontStyle.normal;
+ fontWeight = FontWeight.Normal;
+ absoluteFontWeight = AbsoluteFontWeight.normal;
+ fontFeatureSettings = "";
+ fontVariationSettings = "";
+ fontVariantLigatures = FontVariantLigatures.normal;
+
+ textAnchor = TextAnchor.start;
+ textDecoration = TextDecoration.None;
+
+ manualKerning = false;
+ kerning = DEFAULT_KERNING;
+ fontSize = DEFAULT_FONT_SIZE;
+ wordSpacing = DEFAULT_WORD_SPACING;
+ letterSpacing = DEFAULT_LETTER_SPACING;
+ }
+
+ private double toAbsolute(
+ ReadableMap font, String prop, double scale, double fontSize, double relative) {
+ ReadableType propType = font.getType(prop);
+ if (propType == ReadableType.Number) {
+ return font.getDouble(prop);
+ } else {
+ String string = font.getString(prop);
+ return PropHelper.fromRelative(string, relative, scale, fontSize);
+ }
+ }
+
+ private void setInheritedWeight(FontData parent) {
+ absoluteFontWeight = parent.absoluteFontWeight;
+ fontWeight = parent.fontWeight;
+ }
+
+ private void handleNumericWeight(FontData parent, double number) {
+ long weight = Math.round(number);
+ if (weight >= 1 && weight <= 1000) {
+ absoluteFontWeight = (int) weight;
+ fontWeight = AbsoluteFontWeight.nearestFontWeight(absoluteFontWeight);
+ } else {
+ setInheritedWeight(parent);
+ }
+ }
+
+ FontData(ReadableMap font, FontData parent, double scale) {
+ double parentFontSize = parent.fontSize;
+
+ if (font.hasKey(FONT_SIZE)) {
+ fontSize = toAbsolute(font, FONT_SIZE, 1, parentFontSize, parentFontSize);
+ } else {
+ fontSize = parentFontSize;
+ }
+
+ if (font.hasKey(FONT_WEIGHT)) {
+ ReadableType fontWeightType = font.getType(FONT_WEIGHT);
+ if (fontWeightType == ReadableType.Number) {
+ handleNumericWeight(parent, font.getDouble(FONT_WEIGHT));
+ } else {
+ String string = font.getString(FONT_WEIGHT);
+ if (FontWeight.hasEnum(string)) {
+ absoluteFontWeight = AbsoluteFontWeight.from(FontWeight.get(string), parent);
+ fontWeight = AbsoluteFontWeight.nearestFontWeight(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;
+ 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;
+ textDecoration =
+ font.hasKey(TEXT_DECORATION)
+ ? TextDecoration.getEnum(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, KERNING, scale, fontSize, 0) : parent.kerning;
+ wordSpacing =
+ font.hasKey(WORD_SPACING)
+ ? toAbsolute(font, WORD_SPACING, scale, fontSize, 0)
+ : parent.wordSpacing;
+ letterSpacing =
+ font.hasKey(LETTER_SPACING)
+ ? toAbsolute(font, LETTER_SPACING, scale, fontSize, 0)
+ : parent.letterSpacing;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ForeignObjectView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ForeignObjectView.java
new file mode 100644
index 0000000000000..80217d0326e34
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ForeignObjectView.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.view.View;
+import androidx.annotation.NonNull;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class ForeignObjectView extends GroupView {
+
+ SVGLength mX;
+ SVGLength mY;
+ SVGLength mW;
+ SVGLength mH;
+
+ public ForeignObjectView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ @Override
+ void draw(Canvas canvas, Paint paint, float opacity) {
+ float x = (float) relativeOnWidth(mX);
+ float y = (float) relativeOnHeight(mY);
+ float w = (float) relativeOnWidth(mW);
+ float h = (float) relativeOnHeight(mH);
+ canvas.translate(x, y);
+ canvas.clipRect(0, 0, w, h);
+ super.draw(canvas, paint, opacity);
+ }
+
+ @Override
+ public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
+ super.onDescendantInvalidated(child, target);
+ invalidate();
+ }
+
+ public void setX(Dynamic x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(String x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(Double x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setY(Dynamic y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(String y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(Double y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setWidth(Dynamic width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(String width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(Double width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setHeight(Dynamic height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(String height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(Double height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
+ pushGlyphContext();
+ final SvgView svg = getSvgView();
+ final GroupView self = this;
+ final RectF groupRect = new RectF();
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof MaskView) {
+ continue;
+ }
+ if (child instanceof VirtualView) {
+ VirtualView node = ((VirtualView) child);
+ if ("none".equals(node.mDisplay)) {
+ continue;
+ }
+ if (node instanceof RenderableView) {
+ ((RenderableView) node).mergeProperties(self);
+ }
+
+ int count = node.saveAndSetupCanvas(canvas, mCTM);
+ node.render(canvas, paint, opacity * mOpacity);
+ RectF r = node.getClientRect();
+ if (r != null) {
+ groupRect.union(r);
+ }
+
+ node.restoreCanvas(canvas, count);
+
+ if (node instanceof RenderableView) {
+ ((RenderableView) node).resetProperties();
+ }
+
+ if (node.isResponsible()) {
+ svg.enableTouchEvents();
+ }
+ } else if (child instanceof SvgView) {
+ SvgView svgView = (SvgView) child;
+ svgView.drawChildren(canvas);
+ if (svgView.isResponsible()) {
+ svg.enableTouchEvents();
+ }
+ } else {
+ // Enable rendering other native ancestor views in e.g. masks
+ child.draw(canvas);
+ }
+ }
+ this.setClientRect(groupRect);
+ popGlyphContext();
+ }
+
+ // Enable rendering other native ancestor views in e.g. masks, but don't render them another time
+ Bitmap fakeBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ Canvas fake = new Canvas(fakeBitmap);
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(fake);
+ }
+
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ return super.drawChild(fake, child, drawingTime);
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GlyphContext.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GlyphContext.java
new file mode 100644
index 0000000000000..9de43aa39d4de
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GlyphContext.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import com.facebook.react.bridge.ReadableMap;
+import java.util.ArrayList;
+import javax.annotation.Nullable;
+
+// https://www.w3.org/TR/SVG/text.html#TSpanElement
+class GlyphContext {
+
+ // Current stack (one per node push/pop)
+ final ArrayList mFontContext = new ArrayList<>();
+
+ // 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 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 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 mRsIndices = new ArrayList<>();
+
+ // Calculated on push context, percentage and em length depends on parent font size
+ private double mFontSize = FontData.DEFAULT_FONT_SIZE;
+ private FontData topFont = FontData.Defaults;
+
+ // Current accumulated values
+ // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinate
+ // syntax is the same as that for
+ private double mX;
+ private double mY;
+
+ // https://www.w3.org/TR/SVG/types.html#Length
+ private double mDX;
+ private double mDY;
+
+ // Current SVGLengthList
+ // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLengthList
+ // https://www.w3.org/TR/SVG/types.html#DataTypeCoordinates
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
+ private SVGLength[] mXs = new SVGLength[] {};
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
+ private SVGLength[] mYs = new SVGLength[] {};
+
+ // Current SVGLengthList
+ // https://www.w3.org/TR/SVG/types.html#DataTypeLengths
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
+ private SVGLength[] mDXs = new SVGLength[] {};
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
+ private SVGLength[] mDYs = new SVGLength[] {};
+
+ // Current SVGLengthList
+ // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
+ private double[] mRs = new double[] {0};
+
+ // Current attribute list index
+ private int mXsIndex;
+ private int mYsIndex;
+ 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 mRIndex = -1;
+
+ // Top index of stack
+ private int mTop;
+
+ // 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;
+ mHeight = height;
+
+ mXsContext.add(mXs);
+ mYsContext.add(mYs);
+ mDXsContext.add(mDXs);
+ mDYsContext.add(mDYs);
+ mRsContext.add(mRs);
+
+ mXIndices.add(mXIndex);
+ mYIndices.add(mYIndex);
+ mDXIndices.add(mDXIndex);
+ mDYIndices.add(mDYIndex);
+ mRIndices.add(mRIndex);
+
+ mFontContext.add(topFont);
+
+ pushIndices();
+ }
+
+ private void reset() {
+ mXsIndex = mYsIndex = mDXsIndex = mDYsIndex = mRsIndex = 0;
+ mXIndex = mYIndex = mDXIndex = mDYIndex = mRIndex = -1;
+ mX = mY = mDX = mDY = 0;
+ }
+
+ FontData getFont() {
+ return topFont;
+ }
+
+ private FontData getTopOrParentFont(GroupView child) {
+ if (mTop > 0) {
+ return topFont;
+ } else {
+ GroupView parentRoot = child.getParentTextRoot();
+
+ while (parentRoot != null) {
+ FontData map = parentRoot.getGlyphContext().getFont();
+ if (map != FontData.Defaults) {
+ return map;
+ }
+ parentRoot = parentRoot.getParentTextRoot();
+ }
+
+ return FontData.Defaults;
+ }
+ }
+
+ private void pushNodeAndFont(GroupView node, @Nullable ReadableMap font) {
+ FontData parent = getTopOrParentFont(node);
+ mTop++;
+
+ if (font == null) {
+ mFontContext.add(parent);
+ return;
+ }
+
+ FontData data = new FontData(font, parent, mScale);
+ mFontSize = data.fontSize;
+ mFontContext.add(data);
+ topFont = data;
+ }
+
+ void pushContext(GroupView node, @Nullable ReadableMap font) {
+ pushNodeAndFont(node, font);
+ pushIndices();
+ }
+
+ private SVGLength[] getStringArrayFromReadableArray(ArrayList readableArray) {
+ int size = readableArray.size();
+ SVGLength[] strings = new SVGLength[size];
+ for (int i = 0; i < size; i++) {
+ strings[i] = readableArray.get(i);
+ }
+ return strings;
+ }
+
+ private double[] getDoubleArrayFromReadableArray(ArrayList readableArray) {
+ int size = readableArray.size();
+ double[] doubles = new double[size];
+ for (int i = 0; i < size; i++) {
+ SVGLength length = readableArray.get(i);
+ doubles[i] = length.value;
+ }
+ return doubles;
+ }
+
+ void pushContext(
+ boolean reset,
+ TextView node,
+ @Nullable ReadableMap font,
+ @Nullable ArrayList x,
+ @Nullable ArrayList y,
+ @Nullable ArrayList deltaX,
+ @Nullable ArrayList deltaY,
+ @Nullable ArrayList rotate) {
+ if (reset) {
+ this.reset();
+ }
+
+ pushNodeAndFont(node, font);
+
+ if (x != null && x.size() != 0) {
+ mXsIndex++;
+ mXIndex = -1;
+ mXIndices.add(mXIndex);
+ mXs = getStringArrayFromReadableArray(x);
+ mXsContext.add(mXs);
+ }
+
+ if (y != null && y.size() != 0) {
+ mYsIndex++;
+ mYIndex = -1;
+ mYIndices.add(mYIndex);
+ mYs = getStringArrayFromReadableArray(y);
+ mYsContext.add(mYs);
+ }
+
+ if (deltaX != null && deltaX.size() != 0) {
+ 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);
+ }
+
+ if (rotate != null && rotate.size() != 0) {
+ mRsIndex++;
+ mRIndex = -1;
+ mRIndices.add(mRIndex);
+ mRs = getDoubleArrayFromReadableArray(rotate);
+ mRsContext.add(mRs);
+ }
+
+ pushIndices();
+ }
+
+ void popContext() {
+ mFontContext.remove(mTop);
+ mXsIndices.remove(mTop);
+ mYsIndices.remove(mTop);
+ mDXsIndices.remove(mTop);
+ mDYsIndices.remove(mTop);
+ mRsIndices.remove(mTop);
+
+ mTop--;
+
+ int x = mXsIndex;
+ int y = mYsIndex;
+ int dx = mDXsIndex;
+ int dy = mDYsIndex;
+ int r = mRsIndex;
+
+ topFont = mFontContext.get(mTop);
+ mXsIndex = mXsIndices.get(mTop);
+ mYsIndex = mYsIndices.get(mTop);
+ mDXsIndex = mDXsIndices.get(mTop);
+ mDYsIndex = mDYsIndices.get(mTop);
+ mRsIndex = mRsIndices.get(mTop);
+
+ if (x != mXsIndex) {
+ mXsContext.remove(x);
+ mXs = mXsContext.get(mXsIndex);
+ mXIndex = mXIndices.get(mXsIndex);
+ }
+ if (y != mYsIndex) {
+ mYsContext.remove(y);
+ mYs = mYsContext.get(mYsIndex);
+ mYIndex = mYIndices.get(mYsIndex);
+ }
+ 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 (r != mRsIndex) {
+ mRsContext.remove(r);
+ mRs = mRsContext.get(mRsIndex);
+ mRIndex = mRIndices.get(mRsIndex);
+ }
+ }
+
+ private static void incrementIndices(ArrayList indices, int topIndex) {
+ for (int index = topIndex; index >= 0; index--) {
+ int xIndex = indices.get(index);
+ indices.set(index, xIndex + 1);
+ }
+ }
+
+ // https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
+
+ /**
+ * Get font size from context.
+ *
+ * ‘font-size’ Value: < absolute-size > | < relative-size > | < length > | < percentage > |
+ * inherit Initial: medium Applies to: text content elements Inherited: yes, the computed value is
+ * inherited Percentages: refer to parent element's font size Media: visual Animatable: yes
+ *
+ *
This property refers to the size of the font from baseline to baseline when multiple lines
+ * of text are set solid in a multiline layout environment.
+ *
+ *
For SVG, if a < length > is provided without a unit identifier (e.g., an unqualified number
+ * such as 128), the SVG user agent processes the < length > as a height value in the current user
+ * coordinate system.
+ *
+ *
If a < length > is provided with one of the unit identifiers (e.g., 12pt or 10%), then the
+ * SVG user agent converts the < length > into a corresponding value in the current user
+ * coordinate system by applying the rules described in Units.
+ *
+ *
Except for any additional information provided in this specification, the normative
+ * definition of the property is in CSS2 ([CSS2], section 15.2.4).
+ */
+ double getFontSize() {
+ return mFontSize;
+ }
+
+ double nextX(double advance) {
+ incrementIndices(mXIndices, mXsIndex);
+
+ int nextIndex = mXIndex + 1;
+ if (nextIndex < mXs.length) {
+ mDX = 0;
+ mXIndex = nextIndex;
+ SVGLength string = mXs[nextIndex];
+ mX = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
+ }
+
+ mX += advance;
+
+ return mX;
+ }
+
+ double nextY() {
+ incrementIndices(mYIndices, mYsIndex);
+
+ int nextIndex = mYIndex + 1;
+ if (nextIndex < mYs.length) {
+ mDY = 0;
+ mYIndex = nextIndex;
+ SVGLength string = mYs[nextIndex];
+ mY = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
+ }
+
+ return mY;
+ }
+
+ double nextDeltaX() {
+ incrementIndices(mDXIndices, mDXsIndex);
+
+ int nextIndex = mDXIndex + 1;
+ if (nextIndex < mDXs.length) {
+ mDXIndex = nextIndex;
+ SVGLength string = mDXs[nextIndex];
+ double val = PropHelper.fromRelative(string, mWidth, 0, mScale, mFontSize);
+ mDX += val;
+ }
+
+ return mDX;
+ }
+
+ double nextDeltaY() {
+ incrementIndices(mDYIndices, mDYsIndex);
+
+ int nextIndex = mDYIndex + 1;
+ if (nextIndex < mDYs.length) {
+ mDYIndex = nextIndex;
+ SVGLength string = mDYs[nextIndex];
+ double val = PropHelper.fromRelative(string, mHeight, 0, mScale, mFontSize);
+ mDY += val;
+ }
+
+ return mDY;
+ }
+
+ double nextRotation() {
+ incrementIndices(mRIndices, mRsIndex);
+
+ mRIndex = Math.min(mRIndex + 1, mRs.length - 1);
+
+ return mRs[mRIndex];
+ }
+
+ float getWidth() {
+ return mWidth;
+ }
+
+ float getHeight() {
+ return mHeight;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GlyphPathBag.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GlyphPathBag.java
new file mode 100644
index 0000000000000..1a8dca344b34f
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GlyphPathBag.java
@@ -0,0 +1,47 @@
+package com.horcrux.svg;
+
+import android.graphics.Paint;
+import android.graphics.Path;
+import java.util.ArrayList;
+
+class GlyphPathBag {
+ private final ArrayList paths = new ArrayList<>();
+ private final int[][] data = new int[256][];
+ private final Paint paint;
+
+ GlyphPathBag(Paint paint) {
+ this.paint = paint;
+ // Make indexed-by-one, to allow zero to represent non-cached
+ paths.add(new Path());
+ }
+
+ Path getOrCreateAndCache(char ch, String current) {
+ int index = getIndex(ch);
+ Path cached;
+
+ if (index != 0) {
+ cached = paths.get(index);
+ } else {
+ cached = new Path();
+ paint.getTextPath(current, 0, 1, 0, 0, cached);
+
+ int[] bin = data[ch >> 8];
+ if (bin == null) {
+ bin = data[ch >> 8] = new int[256];
+ }
+ bin[ch & 0xFF] = paths.size();
+
+ paths.add(cached);
+ }
+
+ Path glyph = new Path();
+ glyph.addPath(cached);
+ return glyph;
+ }
+
+ private int getIndex(char ch) {
+ int[] bin = data[ch >> 8];
+ if (bin == null) return 0;
+ return bin[ch & 0xFF];
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GroupView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GroupView.java
new file mode 100644
index 0000000000000..4b0a88adf7c72
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/GroupView.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Build;
+import android.view.View;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableMap;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class GroupView extends RenderableView {
+ @Nullable ReadableMap mFont;
+ private GlyphContext mGlyphContext;
+
+ public GroupView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setFont(@Nullable ReadableMap font) {
+ mFont = font;
+ invalidate();
+ }
+
+ void setupGlyphContext(Canvas canvas) {
+ RectF clipBounds = new RectF(canvas.getClipBounds());
+ if (mMatrix != null) {
+ mMatrix.mapRect(clipBounds);
+ }
+ if (mTransform != null) {
+ mTransform.mapRect(clipBounds);
+ }
+ mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
+ }
+
+ GlyphContext getGlyphContext() {
+ return mGlyphContext;
+ }
+
+ private static T requireNonNull(T obj) {
+ if (obj == null) throw new NullPointerException();
+ return obj;
+ }
+
+ GlyphContext getTextRootGlyphContext() {
+ return requireNonNull(getTextRoot()).getGlyphContext();
+ }
+
+ void pushGlyphContext() {
+ getTextRootGlyphContext().pushContext(this, mFont);
+ }
+
+ void popGlyphContext() {
+ getTextRootGlyphContext().popContext();
+ }
+
+ void draw(final Canvas canvas, final Paint paint, final float opacity) {
+ setupGlyphContext(canvas);
+ clip(canvas, paint);
+ drawGroup(canvas, paint, opacity);
+ }
+
+ void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
+ pushGlyphContext();
+ final SvgView svg = getSvgView();
+ final GroupView self = this;
+ final RectF groupRect = new RectF();
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof MaskView) {
+ continue;
+ }
+ if (child instanceof VirtualView) {
+ VirtualView node = ((VirtualView) child);
+ if ("none".equals(node.mDisplay)) {
+ continue;
+ }
+ if (node instanceof RenderableView) {
+ ((RenderableView) node).mergeProperties(self);
+ }
+
+ int count = node.saveAndSetupCanvas(canvas, mCTM);
+ node.render(canvas, paint, opacity * mOpacity);
+ RectF r = node.getClientRect();
+ if (r != null) {
+ groupRect.union(r);
+ }
+
+ node.restoreCanvas(canvas, count);
+
+ if (node instanceof RenderableView) {
+ ((RenderableView) node).resetProperties();
+ }
+
+ if (node.isResponsible()) {
+ svg.enableTouchEvents();
+ }
+ } else if (child instanceof SvgView) {
+ SvgView svgView = (SvgView) child;
+ svgView.drawChildren(canvas);
+ if (svgView.isResponsible()) {
+ svg.enableTouchEvents();
+ }
+ }
+ }
+ this.setClientRect(groupRect);
+ popGlyphContext();
+ }
+
+ void drawPath(Canvas canvas, Paint paint, float opacity) {
+ super.draw(canvas, paint, opacity);
+ }
+
+ @Override
+ Path getPath(final Canvas canvas, final Paint paint) {
+ if (mPath != null) {
+ return mPath;
+ }
+ mPath = new Path();
+
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof MaskView) {
+ continue;
+ }
+ if (node instanceof VirtualView) {
+ VirtualView n = (VirtualView) node;
+ Matrix transform = n.mMatrix;
+ mPath.addPath(n.getPath(canvas, paint), transform);
+ }
+ }
+
+ return mPath;
+ }
+
+ Path getPath(final Canvas canvas, final Paint paint, final Region.Op op) {
+ final Path path = new Path();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ final Path.Op pop = Path.Op.valueOf(op.name());
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof MaskView) {
+ continue;
+ }
+ if (node instanceof VirtualView) {
+ VirtualView n = (VirtualView) node;
+ Matrix transform = n.mMatrix;
+ Path p2;
+ if (n instanceof GroupView) {
+ p2 = ((GroupView) n).getPath(canvas, paint, op);
+ } else {
+ p2 = n.getPath(canvas, paint);
+ }
+ p2.transform(transform);
+ path.op(p2, pop);
+ }
+ }
+ } else {
+ Rect clipBounds = canvas.getClipBounds();
+ final Region bounds = new Region(clipBounds);
+ final Region r = new Region();
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof MaskView) {
+ continue;
+ }
+ if (node instanceof VirtualView) {
+ VirtualView n = (VirtualView) node;
+ Matrix transform = n.mMatrix;
+ Path p2;
+ if (n instanceof GroupView) {
+ p2 = ((GroupView) n).getPath(canvas, paint, op);
+ } else {
+ p2 = n.getPath(canvas, paint);
+ }
+ if (transform != null) {
+ p2.transform(transform);
+ }
+ Region r2 = new Region();
+ r2.setPath(p2, bounds);
+ r.op(r2, op);
+ }
+ }
+ path.addPath(r.getBoundaryPath());
+ }
+
+ return path;
+ }
+
+ @Override
+ int hitTest(final float[] src) {
+ if (!mInvertible || !mTransformInvertible) {
+ return -1;
+ }
+
+ float[] dst = new float[2];
+ mInvMatrix.mapPoints(dst, src);
+ mInvTransform.mapPoints(dst);
+
+ int x = Math.round(dst[0]);
+ int y = Math.round(dst[1]);
+
+ Path clipPath = getClipPath();
+ if (clipPath != null) {
+ if (mClipRegionPath != clipPath) {
+ mClipRegionPath = clipPath;
+ mClipBounds = new RectF();
+ clipPath.computeBounds(mClipBounds, true);
+ mClipRegion = getRegion(clipPath, mClipBounds);
+ }
+ if (!mClipRegion.contains(x, y)) {
+ return -1;
+ }
+ }
+
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (child instanceof VirtualView) {
+ if (child instanceof MaskView) {
+ continue;
+ }
+
+ VirtualView node = (VirtualView) child;
+
+ int hitChild = node.hitTest(dst);
+ if (hitChild != -1) {
+ return (node.isResponsible() || hitChild != child.getId()) ? hitChild : getId();
+ }
+ } else if (child instanceof SvgView) {
+ SvgView node = (SvgView) child;
+
+ int hitChild = node.reactTagForTouch(dst[0], dst[1]);
+ if (hitChild != child.getId()) {
+ return hitChild;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ void saveDefinition() {
+ if (mName != null) {
+ getSvgView().defineTemplate(this, mName);
+ }
+
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof VirtualView) {
+ ((VirtualView) node).saveDefinition();
+ }
+ }
+ }
+
+ @Override
+ void resetProperties() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof RenderableView) {
+ ((RenderableView) node).resetProperties();
+ }
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ImageView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ImageView.java
new file mode 100644
index 0000000000000..07a2f89ffaceb
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/ImageView.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.net.Uri;
+import com.facebook.common.executors.UiThreadImmediateExecutorService;
+import com.facebook.common.logging.FLog;
+import com.facebook.common.references.CloseableReference;
+import com.facebook.datasource.DataSource;
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.imagepipeline.core.ImagePipeline;
+import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
+import com.facebook.imagepipeline.image.CloseableBitmap;
+import com.facebook.imagepipeline.image.CloseableImage;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.common.ReactConstants;
+import com.facebook.react.views.imagehelper.ImageSource;
+import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
+import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class ImageView extends RenderableView {
+ private SVGLength mX;
+ private SVGLength mY;
+ private SVGLength mW;
+ private SVGLength mH;
+ private String uriString;
+ private int mImageWidth;
+ private int mImageHeight;
+ private String mAlign;
+ private int mMeetOrSlice;
+ private final AtomicBoolean mLoading = new AtomicBoolean(false);
+
+ public ImageView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setX(Dynamic x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(String x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(Double x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setY(Dynamic y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(String y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(Double y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setWidth(Dynamic width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(String width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(Double width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setHeight(Dynamic height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(String height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(Double height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setSrc(@Nullable ReadableMap src) {
+ if (src != null) {
+ uriString = src.getString("uri");
+
+ if (uriString == null || uriString.isEmpty()) {
+ // TODO: give warning about this
+ return;
+ }
+
+ if (src.hasKey("width") && src.hasKey("height")) {
+ mImageWidth = src.getInt("width");
+ mImageHeight = src.getInt("height");
+ } else {
+ mImageWidth = 0;
+ mImageHeight = 0;
+ }
+ Uri mUri = Uri.parse(uriString);
+ if (mUri.getScheme() == null) {
+ ResourceDrawableIdHelper.getInstance().getResourceDrawableUri(mContext, uriString);
+ }
+ }
+ }
+
+ public void setAlign(String align) {
+ mAlign = align;
+ invalidate();
+ }
+
+ public void setMeetOrSlice(int meetOrSlice) {
+ mMeetOrSlice = meetOrSlice;
+ invalidate();
+ }
+
+ @Override
+ void draw(final Canvas canvas, final Paint paint, final float opacity) {
+ if (!mLoading.get()) {
+ ImagePipeline imagePipeline = Fresco.getImagePipeline();
+ ImageSource imageSource = new ImageSource(mContext, uriString);
+ ImageRequest request = ImageRequest.fromUri(imageSource.getUri());
+ boolean inMemoryCache = imagePipeline.isInBitmapMemoryCache(request);
+
+ if (inMemoryCache) {
+ tryRenderFromBitmapCache(imagePipeline, request, canvas, paint, opacity * mOpacity);
+ } else {
+ loadBitmap(imagePipeline, request);
+ }
+ }
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ mPath = new Path();
+ mPath.addRect(getRect(), Path.Direction.CW);
+ return mPath;
+ }
+
+ private void loadBitmap(final ImagePipeline imagePipeline, final ImageRequest request) {
+ mLoading.set(true);
+ final DataSource> dataSource =
+ imagePipeline.fetchDecodedImage(request, mContext);
+ BaseBitmapDataSubscriber subscriber =
+ new BaseBitmapDataSubscriber() {
+ @Override
+ public void onNewResultImpl(Bitmap bitmap) {
+ mLoading.set(false);
+ SvgView view = getSvgView();
+ if (view != null) {
+ view.invalidate();
+ }
+ }
+
+ @Override
+ public void onFailureImpl(DataSource dataSource) {
+ // No cleanup required here.
+ // TODO: more details about this failure
+ mLoading.set(false);
+ FLog.w(
+ ReactConstants.TAG,
+ dataSource.getFailureCause(),
+ "RNSVG: fetchDecodedImage failed!");
+ }
+ };
+ dataSource.subscribe(subscriber, UiThreadImmediateExecutorService.getInstance());
+ }
+
+ @Nonnull
+ private RectF getRect() {
+ double x = relativeOnWidth(mX);
+ double y = relativeOnHeight(mY);
+ double w = relativeOnWidth(mW);
+ double h = relativeOnHeight(mH);
+ if (w == 0) {
+ w = mImageWidth * mScale;
+ }
+ if (h == 0) {
+ h = mImageHeight * mScale;
+ }
+
+ return new RectF((float) x, (float) y, (float) (x + w), (float) (y + h));
+ }
+
+ private void doRender(Canvas canvas, Paint paint, Bitmap bitmap, float opacity) {
+ if (mImageWidth == 0 || mImageHeight == 0) {
+ mImageWidth = bitmap.getWidth();
+ mImageHeight = bitmap.getHeight();
+ }
+
+ RectF renderRect = getRect();
+ RectF vbRect = new RectF(0, 0, mImageWidth, mImageHeight);
+ Matrix transform = ViewBox.getTransform(vbRect, renderRect, mAlign, mMeetOrSlice);
+ transform.mapRect(vbRect);
+
+ canvas.clipPath(getPath(canvas, paint));
+
+ Path clipPath = getClipPath(canvas, paint);
+ if (clipPath != null) {
+ canvas.clipPath(clipPath);
+ }
+
+ Paint alphaPaint = new Paint();
+ alphaPaint.setAlpha((int) (opacity * 255));
+ canvas.drawBitmap(bitmap, null, vbRect, alphaPaint);
+ mCTM.mapRect(vbRect);
+ this.setClientRect(vbRect);
+ }
+
+ private void tryRenderFromBitmapCache(
+ ImagePipeline imagePipeline,
+ ImageRequest request,
+ Canvas canvas,
+ Paint paint,
+ float opacity) {
+ final DataSource> dataSource =
+ imagePipeline.fetchImageFromBitmapCache(request, mContext);
+
+ try {
+ final CloseableReference imageReference = dataSource.getResult();
+ if (imageReference == null) {
+ return;
+ }
+
+ try {
+ CloseableImage closeableImage = imageReference.get();
+ if (!(closeableImage instanceof CloseableBitmap)) {
+ return;
+ }
+
+ CloseableBitmap closeableBitmap = (CloseableBitmap) closeableImage;
+ final Bitmap bitmap = closeableBitmap.getUnderlyingBitmap();
+
+ if (bitmap == null) {
+ return;
+ }
+
+ doRender(canvas, paint, bitmap, opacity);
+
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ } finally {
+ CloseableReference.closeSafely(imageReference);
+ }
+
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ } finally {
+ dataSource.close();
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/LineView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/LineView.java
new file mode 100644
index 0000000000000..d927f4578aa1b
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/LineView.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class LineView extends RenderableView {
+ private SVGLength mX1;
+ private SVGLength mY1;
+ private SVGLength mX2;
+ private SVGLength mY2;
+
+ public LineView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setX1(Dynamic x1) {
+ mX1 = SVGLength.from(x1);
+ invalidate();
+ }
+
+ public void setX1(String x1) {
+ mX1 = SVGLength.from(x1);
+ invalidate();
+ }
+
+ public void setX1(Double x1) {
+ mX1 = SVGLength.from(x1);
+ invalidate();
+ }
+
+ public void setY1(Dynamic y1) {
+ mY1 = SVGLength.from(y1);
+ invalidate();
+ }
+
+ public void setY1(String y1) {
+ mY1 = SVGLength.from(y1);
+ invalidate();
+ }
+
+ public void setY1(Double y1) {
+ mY1 = SVGLength.from(y1);
+ invalidate();
+ }
+
+ public void setX2(Dynamic x2) {
+ mX2 = SVGLength.from(x2);
+ invalidate();
+ }
+
+ public void setX2(String x2) {
+ mX2 = SVGLength.from(x2);
+ invalidate();
+ }
+
+ public void setX2(Double x2) {
+ mX2 = SVGLength.from(x2);
+ invalidate();
+ }
+
+ public void setY2(Dynamic y2) {
+ mY2 = SVGLength.from(y2);
+ invalidate();
+ }
+
+ public void setY2(String y2) {
+ mY2 = SVGLength.from(y2);
+ invalidate();
+ }
+
+ public void setY2(Double y2) {
+ mY2 = SVGLength.from(y2);
+ invalidate();
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ Path path = new Path();
+ double x1 = relativeOnWidth(mX1);
+ double y1 = relativeOnHeight(mY1);
+ double x2 = relativeOnWidth(mX2);
+ double y2 = relativeOnHeight(mY2);
+
+ path.moveTo((float) x1, (float) y1);
+ path.lineTo((float) x2, (float) y2);
+ return path;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/LinearGradientView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/LinearGradientView.java
new file mode 100644
index 0000000000000..891d8473cef84
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/LinearGradientView.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Matrix;
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.common.ReactConstants;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class LinearGradientView extends DefinitionView {
+
+ private SVGLength mX1;
+ private SVGLength mY1;
+ private SVGLength mX2;
+ private SVGLength mY2;
+ private ReadableArray mGradient;
+ private Brush.BrushUnits mGradientUnits;
+
+ private static final float[] sRawMatrix =
+ new float[] {
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+ };
+ private Matrix mMatrix = null;
+
+ public LinearGradientView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setX1(Dynamic x1) {
+ mX1 = SVGLength.from(x1);
+ invalidate();
+ }
+
+ public void setX1(String x1) {
+ mX1 = SVGLength.from(x1);
+ invalidate();
+ }
+
+ public void setX1(Double x1) {
+ mX1 = SVGLength.from(x1);
+ invalidate();
+ }
+
+ public void setY1(Dynamic y1) {
+ mY1 = SVGLength.from(y1);
+ invalidate();
+ }
+
+ public void setY1(String y1) {
+ mY1 = SVGLength.from(y1);
+ invalidate();
+ }
+
+ public void setY1(Double y1) {
+ mY1 = SVGLength.from(y1);
+ invalidate();
+ }
+
+ public void setX2(Dynamic x2) {
+ mX2 = SVGLength.from(x2);
+ invalidate();
+ }
+
+ public void setX2(String x2) {
+ mX2 = SVGLength.from(x2);
+ invalidate();
+ }
+
+ public void setX2(Double x2) {
+ mX2 = SVGLength.from(x2);
+ invalidate();
+ }
+
+ public void setY2(Dynamic y2) {
+ mY2 = SVGLength.from(y2);
+ invalidate();
+ }
+
+ public void setY2(String y2) {
+ mY2 = SVGLength.from(y2);
+ invalidate();
+ }
+
+ public void setY2(Double y2) {
+ mY2 = SVGLength.from(y2);
+ invalidate();
+ }
+
+ public void setGradient(ReadableArray gradient) {
+ mGradient = gradient;
+ invalidate();
+ }
+
+ public void setGradientUnits(int gradientUnits) {
+ switch (gradientUnits) {
+ case 0:
+ mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
+ break;
+ case 1:
+ mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
+ break;
+ }
+ invalidate();
+ }
+
+ public void setGradientTransform(@Nullable ReadableArray matrixArray) {
+ if (matrixArray != null) {
+ int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
+ if (matrixSize == 6) {
+ if (mMatrix == null) {
+ mMatrix = new Matrix();
+ }
+ mMatrix.setValues(sRawMatrix);
+ } else if (matrixSize != -1) {
+ FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
+ }
+ } else {
+ mMatrix = null;
+ }
+
+ invalidate();
+ }
+
+ @Override
+ void saveDefinition() {
+ if (mName != null) {
+ SVGLength[] points = new SVGLength[] {mX1, mY1, mX2, mY2};
+ Brush brush = new Brush(Brush.BrushType.LINEAR_GRADIENT, points, mGradientUnits);
+ brush.setGradientColors(mGradient);
+ if (mMatrix != null) {
+ brush.setGradientTransform(mMatrix);
+ }
+
+ SvgView svg = getSvgView();
+ if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
+ brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
+ }
+
+ svg.defineBrush(brush, mName);
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/MarkerView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/MarkerView.java
new file mode 100644
index 0000000000000..9ea9403bed327
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/MarkerView.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.view.View;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class MarkerView extends GroupView {
+
+ private SVGLength mRefX;
+ private SVGLength mRefY;
+ private SVGLength mMarkerWidth;
+ private SVGLength mMarkerHeight;
+ private String mMarkerUnits;
+ private String mOrient;
+
+ private float mMinX;
+ private float mMinY;
+ private float mVbWidth;
+ private float mVbHeight;
+ String mAlign;
+ int mMeetOrSlice;
+
+ Matrix markerTransform = new Matrix();
+
+ public MarkerView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setRefX(Dynamic refX) {
+ mRefX = SVGLength.from(refX);
+ invalidate();
+ }
+
+ public void setRefX(String refX) {
+ mRefX = SVGLength.from(refX);
+ invalidate();
+ }
+
+ public void setRefX(Double refX) {
+ mRefX = SVGLength.from(refX);
+ invalidate();
+ }
+
+ public void setRefY(Dynamic refY) {
+ mRefY = SVGLength.from(refY);
+ invalidate();
+ }
+
+ public void setRefY(String refY) {
+ mRefY = SVGLength.from(refY);
+ invalidate();
+ }
+
+ public void setRefY(Double refY) {
+ mRefY = SVGLength.from(refY);
+ invalidate();
+ }
+
+ public void setMarkerWidth(Dynamic markerWidth) {
+ mMarkerWidth = SVGLength.from(markerWidth);
+ invalidate();
+ }
+
+ public void setMarkerWidth(String markerWidth) {
+ mMarkerWidth = SVGLength.from(markerWidth);
+ invalidate();
+ }
+
+ public void setMarkerWidth(Double markerWidth) {
+ mMarkerWidth = SVGLength.from(markerWidth);
+ invalidate();
+ }
+
+ public void setMarkerHeight(Dynamic markerHeight) {
+ mMarkerHeight = SVGLength.from(markerHeight);
+ invalidate();
+ }
+
+ public void setMarkerHeight(String markerHeight) {
+ mMarkerHeight = SVGLength.from(markerHeight);
+ invalidate();
+ }
+
+ public void setMarkerHeight(Double markerHeight) {
+ mMarkerHeight = SVGLength.from(markerHeight);
+ invalidate();
+ }
+
+ public void setMarkerUnits(String markerUnits) {
+ mMarkerUnits = markerUnits;
+ invalidate();
+ }
+
+ public void setOrient(String orient) {
+ mOrient = orient;
+ invalidate();
+ }
+
+ public void setMinX(float minX) {
+ mMinX = minX;
+ invalidate();
+ }
+
+ public void setMinY(float minY) {
+ mMinY = minY;
+ invalidate();
+ }
+
+ public void setVbWidth(float vbWidth) {
+ mVbWidth = vbWidth;
+ invalidate();
+ }
+
+ public void setVbHeight(float vbHeight) {
+ mVbHeight = vbHeight;
+ invalidate();
+ }
+
+ public void setAlign(String align) {
+ mAlign = align;
+ invalidate();
+ }
+
+ public void setMeetOrSlice(int meetOrSlice) {
+ mMeetOrSlice = meetOrSlice;
+ invalidate();
+ }
+
+ @Override
+ void saveDefinition() {
+ if (mName != null) {
+ SvgView svg = getSvgView();
+ svg.defineMarker(this, mName);
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof VirtualView) {
+ ((VirtualView) node).saveDefinition();
+ }
+ }
+ }
+ }
+
+ void renderMarker(
+ Canvas canvas, Paint paint, float opacity, RNSVGMarkerPosition position, float strokeWidth) {
+ int count = saveAndSetupCanvas(canvas, mCTM);
+
+ markerTransform.reset();
+ Point origin = position.origin;
+ markerTransform.setTranslate((float) origin.x * mScale, (float) origin.y * mScale);
+
+ double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
+ float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle);
+ markerTransform.preRotate(degrees);
+
+ boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
+ if (useStrokeWidth) {
+ markerTransform.preScale(strokeWidth, strokeWidth);
+ }
+
+ double width = relativeOnWidth(mMarkerWidth) / mScale;
+ double height = relativeOnHeight(mMarkerHeight) / mScale;
+ RectF eRect = new RectF(0, 0, (float) width, (float) height);
+ if (mAlign != null) {
+ RectF vbRect =
+ new RectF(
+ mMinX * mScale,
+ mMinY * mScale,
+ (mMinX + mVbWidth) * mScale,
+ (mMinY + mVbHeight) * mScale);
+ Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
+ float[] values = new float[9];
+ viewBoxMatrix.getValues(values);
+ markerTransform.preScale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
+ }
+
+ double x = relativeOnWidth(mRefX);
+ double y = relativeOnHeight(mRefY);
+ markerTransform.preTranslate((float) -x, (float) -y);
+
+ canvas.concat(markerTransform);
+
+ drawGroup(canvas, paint, opacity);
+
+ restoreCanvas(canvas, count);
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/MaskView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/MaskView.java
new file mode 100644
index 0000000000000..8b974fce9853e
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/MaskView.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Matrix;
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.common.ReactConstants;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class MaskView extends GroupView {
+
+ SVGLength mX;
+ SVGLength mY;
+ SVGLength mW;
+ SVGLength mH;
+
+ // TODO implement proper support for units
+ @SuppressWarnings({"FieldCanBeLocal", "unused"})
+ private Brush.BrushUnits mMaskUnits;
+
+ @SuppressWarnings({"FieldCanBeLocal", "unused"})
+ private Brush.BrushUnits mMaskContentUnits;
+
+ private static final float[] sRawMatrix =
+ new float[] {
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+ };
+ private Matrix mMatrix = null;
+
+ public MaskView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setX(Dynamic x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(String x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(Double x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setY(Dynamic y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(String y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(Double y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setWidth(Dynamic width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(String width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(Double width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setHeight(Dynamic height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(String height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(Double height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setMaskUnits(int maskUnits) {
+ switch (maskUnits) {
+ case 0:
+ mMaskUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
+ break;
+ case 1:
+ mMaskUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
+ break;
+ }
+ invalidate();
+ }
+
+ public void setMaskContentUnits(int maskContentUnits) {
+ switch (maskContentUnits) {
+ case 0:
+ mMaskContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
+ break;
+ case 1:
+ mMaskContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
+ break;
+ }
+ invalidate();
+ }
+
+ public void setMaskTransform(@Nullable ReadableArray matrixArray) {
+ if (matrixArray != null) {
+ int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
+ if (matrixSize == 6) {
+ if (mMatrix == null) {
+ mMatrix = new Matrix();
+ }
+ mMatrix.setValues(sRawMatrix);
+ } else if (matrixSize != -1) {
+ FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
+ }
+ } else {
+ mMatrix = null;
+ }
+
+ invalidate();
+ }
+
+ @Override
+ void saveDefinition() {
+ if (mName != null) {
+ SvgView svg = getSvgView();
+ svg.defineMask(this, mName);
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PathParser.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PathParser.java
new file mode 100644
index 0000000000000..f618030e4bfca
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PathParser.java
@@ -0,0 +1,679 @@
+package com.horcrux.svg;
+
+import android.graphics.Path;
+import android.graphics.RectF;
+import java.util.ArrayList;
+
+class PathElement {
+ ElementType type;
+ Point[] points;
+
+ PathElement(ElementType type, Point[] points) {
+ this.type = type;
+ this.points = points;
+ }
+}
+
+class PathParser {
+ static float mScale;
+
+ private static int i;
+ private static int l;
+ private static String s;
+ private static Path mPath;
+ static ArrayList elements;
+
+ private static float mPenX;
+ private static float mPenY;
+ private static float mPivotX;
+ private static float mPivotY;
+ private static float mPenDownX;
+ private static float mPenDownY;
+ private static boolean mPenDown;
+
+ static Path parse(String d) {
+ elements = new ArrayList<>();
+ mPath = new Path();
+ if (d == null) {
+ return mPath;
+ }
+ char prev_cmd = ' ';
+ l = d.length();
+ s = d;
+ i = 0;
+
+ mPenX = 0f;
+ mPenY = 0f;
+ mPivotX = 0f;
+ mPivotY = 0f;
+ mPenDownX = 0f;
+ mPenDownY = 0f;
+ mPenDown = false;
+
+ while (i < l) {
+ skip_spaces();
+
+ if (i >= l) {
+ break;
+ }
+
+ boolean has_prev_cmd = prev_cmd != ' ';
+ char first_char = s.charAt(i);
+
+ if (!has_prev_cmd && first_char != 'M' && first_char != 'm') {
+ // The first segment must be a MoveTo.
+ throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
+ }
+
+ // TODO: simplify
+ boolean is_implicit_move_to;
+ char cmd;
+ if (is_cmd(first_char)) {
+ is_implicit_move_to = false;
+ cmd = first_char;
+ i += 1;
+ } else if (is_number_start(first_char) && has_prev_cmd) {
+ if (prev_cmd == 'Z' || prev_cmd == 'z') {
+ // ClosePath cannot be followed by a number.
+ throw new Error(String.format("Unexpected number after 'z' (s=%s)", s));
+ }
+
+ if (prev_cmd == 'M' || prev_cmd == 'm') {
+ // 'If a moveto is followed by multiple pairs of coordinates,
+ // the subsequent pairs are treated as implicit lineto commands.'
+ // So we parse them as LineTo.
+ is_implicit_move_to = true;
+ if (is_absolute(prev_cmd)) {
+ cmd = 'L';
+ } else {
+ cmd = 'l';
+ }
+ } else {
+ is_implicit_move_to = false;
+ cmd = prev_cmd;
+ }
+ } else {
+ throw new Error(String.format("Unexpected character '%c' (i=%d, s=%s)", first_char, i, s));
+ }
+
+ boolean absolute = is_absolute(cmd);
+ switch (cmd) {
+ case 'm':
+ {
+ move(parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'M':
+ {
+ moveTo(parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'l':
+ {
+ line(parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'L':
+ {
+ lineTo(parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'h':
+ {
+ line(parse_list_number(), 0);
+ break;
+ }
+ case 'H':
+ {
+ lineTo(parse_list_number(), mPenY);
+ break;
+ }
+ case 'v':
+ {
+ line(0, parse_list_number());
+ break;
+ }
+ case 'V':
+ {
+ lineTo(mPenX, parse_list_number());
+ break;
+ }
+ case 'c':
+ {
+ curve(
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number());
+ break;
+ }
+ case 'C':
+ {
+ curveTo(
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number());
+ break;
+ }
+ case 's':
+ {
+ smoothCurve(
+ parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'S':
+ {
+ smoothCurveTo(
+ parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'q':
+ {
+ quadraticBezierCurve(
+ parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'Q':
+ {
+ quadraticBezierCurveTo(
+ parse_list_number(), parse_list_number(), parse_list_number(), parse_list_number());
+ break;
+ }
+ case 't':
+ {
+ smoothQuadraticBezierCurve(parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'T':
+ {
+ smoothQuadraticBezierCurveTo(parse_list_number(), parse_list_number());
+ break;
+ }
+ case 'a':
+ {
+ arc(
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_flag(),
+ parse_flag(),
+ parse_list_number(),
+ parse_list_number());
+ break;
+ }
+ case 'A':
+ {
+ arcTo(
+ parse_list_number(),
+ parse_list_number(),
+ parse_list_number(),
+ parse_flag(),
+ parse_flag(),
+ parse_list_number(),
+ parse_list_number());
+ break;
+ }
+ case 'z':
+ case 'Z':
+ {
+ close();
+ break;
+ }
+ default:
+ {
+ throw new Error(String.format("Unexpected comand '%c' (s=%s)", cmd, s));
+ }
+ }
+
+ if (is_implicit_move_to) {
+ if (absolute) {
+ prev_cmd = 'M';
+ } else {
+ prev_cmd = 'm';
+ }
+ } else {
+ prev_cmd = cmd;
+ }
+ }
+
+ return mPath;
+ }
+
+ private static void move(float x, float y) {
+ moveTo(x + mPenX, y + mPenY);
+ }
+
+ private static void moveTo(float x, float y) {
+ // FLog.w(ReactConstants.TAG, "move x: " + x + " y: " + y);
+ mPenDownX = mPivotX = mPenX = x;
+ mPenDownY = mPivotY = mPenY = y;
+ mPath.moveTo(x * mScale, y * mScale);
+ elements.add(
+ new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[] {new Point(x, y)}));
+ }
+
+ private static void line(float x, float y) {
+ lineTo(x + mPenX, y + mPenY);
+ }
+
+ private static void lineTo(float x, float y) {
+ // FLog.w(ReactConstants.TAG, "line x: " + x + " y: " + y);
+ setPenDown();
+ mPivotX = mPenX = x;
+ mPivotY = mPenY = y;
+ mPath.lineTo(x * mScale, y * mScale);
+ elements.add(
+ new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[] {new Point(x, y)}));
+ }
+
+ private static void curve(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
+ curveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY, ex + mPenX, ey + mPenY);
+ }
+
+ private static void curveTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
+ // FLog.w(ReactConstants.TAG, "curve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: " +
+ // ey);
+ mPivotX = c2x;
+ mPivotY = c2y;
+ cubicTo(c1x, c1y, c2x, c2y, ex, ey);
+ }
+
+ private static void cubicTo(float c1x, float c1y, float c2x, float c2y, float ex, float ey) {
+ setPenDown();
+ mPenX = ex;
+ mPenY = ey;
+ mPath.cubicTo(c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale);
+ elements.add(
+ new PathElement(
+ ElementType.kCGPathElementAddCurveToPoint,
+ new Point[] {new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)}));
+ }
+
+ private static void smoothCurve(float c1x, float c1y, float ex, float ey) {
+ smoothCurveTo(c1x + mPenX, c1y + mPenY, ex + mPenX, ey + mPenY);
+ }
+
+ private static void smoothCurveTo(float c1x, float c1y, float ex, float ey) {
+ // FLog.w(ReactConstants.TAG, "smoothcurve c1x: " + c1x + " c1y: " + c1y + "ex: " + ex + " ey: "
+ // + ey);
+ float c2x = c1x;
+ float c2y = c1y;
+ c1x = (mPenX * 2) - mPivotX;
+ c1y = (mPenY * 2) - mPivotY;
+ mPivotX = c2x;
+ mPivotY = c2y;
+ cubicTo(c1x, c1y, c2x, c2y, ex, ey);
+ }
+
+ private static void quadraticBezierCurve(float c1x, float c1y, float c2x, float c2y) {
+ quadraticBezierCurveTo(c1x + mPenX, c1y + mPenY, c2x + mPenX, c2y + mPenY);
+ }
+
+ private static void quadraticBezierCurveTo(float c1x, float c1y, float c2x, float c2y) {
+ // FLog.w(ReactConstants.TAG, "quad c1x: " + c1x + " c1y: " + c1y + "c2x: " + c2x + " c2y: " +
+ // c2y);
+ mPivotX = c1x;
+ mPivotY = c1y;
+ float ex = c2x;
+ float ey = c2y;
+ c2x = (ex + c1x * 2) / 3;
+ c2y = (ey + c1y * 2) / 3;
+ c1x = (mPenX + c1x * 2) / 3;
+ c1y = (mPenY + c1y * 2) / 3;
+ cubicTo(c1x, c1y, c2x, c2y, ex, ey);
+ }
+
+ private static void smoothQuadraticBezierCurve(float c1x, float c1y) {
+ smoothQuadraticBezierCurveTo(c1x + mPenX, c1y + mPenY);
+ }
+
+ private static void smoothQuadraticBezierCurveTo(float c1x, float c1y) {
+ // FLog.w(ReactConstants.TAG, "smoothquad c1x: " + c1x + " c1y: " + c1y);
+ float c2x = c1x;
+ float c2y = c1y;
+ c1x = (mPenX * 2) - mPivotX;
+ c1y = (mPenY * 2) - mPivotY;
+ quadraticBezierCurveTo(c1x, c1y, c2x, c2y);
+ }
+
+ private static void arc(
+ float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) {
+ arcTo(rx, ry, rotation, outer, clockwise, x + mPenX, y + mPenY);
+ }
+
+ private static void arcTo(
+ float rx, float ry, float rotation, boolean outer, boolean clockwise, float x, float y) {
+ // FLog.w(ReactConstants.TAG, "arc rx: " + rx + " ry: " + ry + " rotation: " + rotation + "
+ // outer: " + outer + " clockwise: " + clockwise + " x: " + x + " y: " + y);
+ float tX = mPenX;
+ float tY = mPenY;
+
+ ry = Math.abs(ry == 0 ? (rx == 0 ? (y - tY) : rx) : ry);
+ rx = Math.abs(rx == 0 ? (x - tX) : rx);
+
+ if (rx == 0 || ry == 0 || (x == tX && y == tY)) {
+ lineTo(x, y);
+ return;
+ }
+
+ float rad = (float) Math.toRadians(rotation);
+ float cos = (float) Math.cos(rad);
+ float sin = (float) Math.sin(rad);
+ x -= tX;
+ y -= tY;
+
+ // Ellipse Center
+ float cx = cos * x / 2 + sin * y / 2;
+ float cy = -sin * x / 2 + cos * y / 2;
+ float rxry = rx * rx * ry * ry;
+ float rycx = ry * ry * cx * cx;
+ float rxcy = rx * rx * cy * cy;
+ float a = rxry - rxcy - rycx;
+
+ if (a < 0) {
+ a = (float) Math.sqrt(1 - a / rxry);
+ rx *= a;
+ ry *= a;
+ cx = x / 2;
+ cy = y / 2;
+ } else {
+ a = (float) Math.sqrt(a / (rxcy + rycx));
+
+ if (outer == clockwise) {
+ a = -a;
+ }
+ float cxd = -a * cy * rx / ry;
+ float cyd = a * cx * ry / rx;
+ cx = cos * cxd - sin * cyd + x / 2;
+ cy = sin * cxd + cos * cyd + y / 2;
+ }
+
+ // Rotation + Scale Transform
+ float xx = cos / rx;
+ float yx = sin / rx;
+ float xy = -sin / ry;
+ float yy = cos / ry;
+
+ // Start and End Angle
+ float sa = (float) Math.atan2(xy * -cx + yy * -cy, xx * -cx + yx * -cy);
+ float ea = (float) Math.atan2(xy * (x - cx) + yy * (y - cy), xx * (x - cx) + yx * (y - cy));
+
+ cx += tX;
+ cy += tY;
+ x += tX;
+ y += tY;
+
+ setPenDown();
+
+ mPenX = mPivotX = x;
+ mPenY = mPivotY = y;
+
+ if (rx != ry || rad != 0f) {
+ arcToBezier(cx, cy, rx, ry, sa, ea, clockwise, rad);
+ } else {
+
+ float start = (float) Math.toDegrees(sa);
+ float end = (float) Math.toDegrees(ea);
+ float sweep = Math.abs((start - end) % 360);
+
+ if (outer) {
+ if (sweep < 180) {
+ sweep = 360 - sweep;
+ }
+ } else {
+ if (sweep > 180) {
+ sweep = 360 - sweep;
+ }
+ }
+
+ if (!clockwise) {
+ sweep = -sweep;
+ }
+
+ RectF oval =
+ new RectF((cx - rx) * mScale, (cy - rx) * mScale, (cx + rx) * mScale, (cy + rx) * mScale);
+
+ mPath.arcTo(oval, start, sweep);
+ elements.add(
+ new PathElement(
+ ElementType.kCGPathElementAddCurveToPoint, new Point[] {new Point(x, y)}));
+ }
+ }
+
+ private static void close() {
+ if (mPenDown) {
+ mPenX = mPenDownX;
+ mPenY = mPenDownY;
+ mPenDown = false;
+ mPath.close();
+ elements.add(
+ new PathElement(
+ ElementType.kCGPathElementCloseSubpath, new Point[] {new Point(mPenX, mPenY)}));
+ }
+ }
+
+ private static void arcToBezier(
+ float cx, float cy, float rx, float ry, float sa, float ea, boolean clockwise, float rad) {
+ // Inverse Rotation + Scale Transform
+ float cos = (float) Math.cos(rad);
+ float sin = (float) Math.sin(rad);
+ float xx = cos * rx;
+ float yx = -sin * ry;
+ float xy = sin * rx;
+ float yy = cos * ry;
+
+ // Bezier Curve Approximation
+ float arc = ea - sa;
+ if (arc < 0 && clockwise) {
+ arc += Math.PI * 2;
+ } else if (arc > 0 && !clockwise) {
+ arc -= Math.PI * 2;
+ }
+
+ int n = (int) Math.ceil(Math.abs(round(arc / (Math.PI / 2))));
+
+ float step = arc / n;
+ float k = (float) ((4 / 3.0) * Math.tan(step / 4));
+
+ float x = (float) Math.cos(sa);
+ float y = (float) Math.sin(sa);
+
+ for (int i = 0; i < n; i++) {
+ float cp1x = x - k * y;
+ float cp1y = y + k * x;
+
+ sa += step;
+ x = (float) Math.cos(sa);
+ y = (float) Math.sin(sa);
+
+ float cp2x = x + k * y;
+ float cp2y = y - k * x;
+
+ float c1x = (cx + xx * cp1x + yx * cp1y);
+ float c1y = (cy + xy * cp1x + yy * cp1y);
+ float c2x = (cx + xx * cp2x + yx * cp2y);
+ float c2y = (cy + xy * cp2x + yy * cp2y);
+ float ex = (cx + xx * x + yx * y);
+ float ey = (cy + xy * x + yy * y);
+
+ mPath.cubicTo(
+ c1x * mScale, c1y * mScale, c2x * mScale, c2y * mScale, ex * mScale, ey * mScale);
+ elements.add(
+ new PathElement(
+ ElementType.kCGPathElementAddCurveToPoint,
+ new Point[] {new Point(c1x, c1y), new Point(c2x, c2y), new Point(ex, ey)}));
+ }
+ }
+
+ private static void setPenDown() {
+ if (!mPenDown) {
+ mPenDownX = mPenX;
+ mPenDownY = mPenY;
+ mPenDown = true;
+ }
+ }
+
+ private static double round(double val) {
+ double multiplier = Math.pow(10, 4);
+ return Math.round(val * multiplier) / multiplier;
+ }
+
+ private static void skip_spaces() {
+ while (i < l && Character.isWhitespace(s.charAt(i))) i++;
+ }
+
+ private static boolean is_cmd(char c) {
+ switch (c) {
+ case 'M':
+ case 'm':
+ case 'Z':
+ case 'z':
+ case 'L':
+ case 'l':
+ case 'H':
+ case 'h':
+ case 'V':
+ case 'v':
+ case 'C':
+ case 'c':
+ case 'S':
+ case 's':
+ case 'Q':
+ case 'q':
+ case 'T':
+ case 't':
+ case 'A':
+ case 'a':
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean is_number_start(char c) {
+ return (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+';
+ }
+
+ private static boolean is_absolute(char c) {
+ return Character.isUpperCase(c);
+ }
+
+ // By the SVG spec 'large-arc' and 'sweep' must contain only one char
+ // and can be written without any separators, e.g.: 10 20 30 01 10 20.
+ private static boolean parse_flag() {
+ skip_spaces();
+
+ char c = s.charAt(i);
+ switch (c) {
+ case '0':
+ case '1':
+ {
+ i += 1;
+ if (i < l && s.charAt(i) == ',') {
+ i += 1;
+ }
+ skip_spaces();
+ break;
+ }
+ default:
+ throw new Error(String.format("Unexpected flag '%c' (i=%d, s=%s)", c, i, s));
+ }
+
+ return c == '1';
+ }
+
+ private static float parse_list_number() {
+ if (i == l) {
+ throw new Error(String.format("Unexpected end (s=%s)", s));
+ }
+
+ float n = parse_number();
+ skip_spaces();
+ parse_list_separator();
+
+ return n;
+ }
+
+ private static float parse_number() {
+ // Strip off leading whitespaces.
+ skip_spaces();
+
+ if (i == l) {
+ throw new Error(String.format("Unexpected end (s=%s)", s));
+ }
+
+ int start = i;
+
+ char c = s.charAt(i);
+
+ // Consume sign.
+ if (c == '-' || c == '+') {
+ i += 1;
+ c = s.charAt(i);
+ }
+
+ // Consume integer.
+ if (c >= '0' && c <= '9') {
+ skip_digits();
+ if (i < l) {
+ c = s.charAt(i);
+ }
+ } else if (c != '.') {
+ throw new Error(
+ String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s));
+ }
+
+ // Consume fraction.
+ if (c == '.') {
+ i += 1;
+ skip_digits();
+ if (i < l) {
+ c = s.charAt(i);
+ }
+ }
+
+ if ((c == 'e' || c == 'E') && i + 1 < l) {
+ char c2 = s.charAt(i + 1);
+ // Check for `em`/`ex`.
+ if (c2 != 'm' && c2 != 'x') {
+ i += 1;
+ c = s.charAt(i);
+
+ if (c == '+' || c == '-') {
+ i += 1;
+ skip_digits();
+ } else if (c >= '0' && c <= '9') {
+ skip_digits();
+ } else {
+ throw new Error(
+ String.format("Invalid number formating character '%c' (i=%d, s=%s)", c, i, s));
+ }
+ }
+ }
+
+ String num = s.substring(start, i);
+ float n = Float.parseFloat(num);
+
+ // inf, nan, etc. are an error.
+ if (Float.isInfinite(n) || Float.isNaN(n)) {
+ throw new Error(
+ String.format("Invalid number '%s' (start=%d, i=%d, s=%s)", num, start, i, s));
+ }
+
+ return n;
+ }
+
+ private static void parse_list_separator() {
+ if (i < l && s.charAt(i) == ',') {
+ i += 1;
+ }
+ }
+
+ private static void skip_digits() {
+ while (i < l && Character.isDigit(s.charAt(i))) i++;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PathView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PathView.java
new file mode 100644
index 0000000000000..eaa4c918b550e
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PathView.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class PathView extends RenderableView {
+ private Path mPath;
+
+ public PathView(ReactContext reactContext) {
+ super(reactContext);
+ PathParser.mScale = mScale;
+ mPath = new Path();
+ }
+
+ public void setD(String d) {
+ mPath = PathParser.parse(d);
+ elements = PathParser.elements;
+ invalidate();
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ return mPath;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PatternView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PatternView.java
new file mode 100644
index 0000000000000..aeb90c4bc0c3d
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PatternView.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.common.ReactConstants;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class PatternView extends GroupView {
+
+ private SVGLength mX;
+ private SVGLength mY;
+ private SVGLength mW;
+ private SVGLength mH;
+ private Brush.BrushUnits mPatternUnits;
+ private Brush.BrushUnits mPatternContentUnits;
+
+ private float mMinX;
+ private float mMinY;
+ private float mVbWidth;
+ private float mVbHeight;
+ String mAlign;
+ int mMeetOrSlice;
+
+ private static final float[] sRawMatrix =
+ new float[] {
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+ };
+ private Matrix mMatrix = null;
+
+ public PatternView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setX(Dynamic x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(String x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(Double x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setY(Dynamic y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(String y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(Double y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setWidth(Dynamic width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(String width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(Double width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setHeight(Dynamic height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(String height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(Double height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setPatternUnits(int patternUnits) {
+ switch (patternUnits) {
+ case 0:
+ mPatternUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
+ break;
+ case 1:
+ mPatternUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
+ break;
+ }
+ invalidate();
+ }
+
+ public void setPatternContentUnits(int patternContentUnits) {
+ switch (patternContentUnits) {
+ case 0:
+ mPatternContentUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
+ break;
+ case 1:
+ mPatternContentUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
+ break;
+ }
+ invalidate();
+ }
+
+ public void setPatternTransform(@Nullable ReadableArray matrixArray) {
+ if (matrixArray != null) {
+ int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
+ if (matrixSize == 6) {
+ if (mMatrix == null) {
+ mMatrix = new Matrix();
+ }
+ mMatrix.setValues(sRawMatrix);
+ } else if (matrixSize != -1) {
+ FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
+ }
+ } else {
+ mMatrix = null;
+ }
+
+ invalidate();
+ }
+
+ public void setMinX(float minX) {
+ mMinX = minX;
+ invalidate();
+ }
+
+ public void setMinY(float minY) {
+ mMinY = minY;
+ invalidate();
+ }
+
+ public void setVbWidth(float vbWidth) {
+ mVbWidth = vbWidth;
+ invalidate();
+ }
+
+ public void setVbHeight(float vbHeight) {
+ mVbHeight = vbHeight;
+ invalidate();
+ }
+
+ public void setAlign(String align) {
+ mAlign = align;
+ invalidate();
+ }
+
+ public void setMeetOrSlice(int meetOrSlice) {
+ mMeetOrSlice = meetOrSlice;
+ invalidate();
+ }
+
+ RectF getViewBox() {
+ return new RectF(
+ mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
+ }
+
+ @Override
+ void saveDefinition() {
+ if (mName != null) {
+ SVGLength[] points = new SVGLength[] {mX, mY, mW, mH};
+ Brush brush = new Brush(Brush.BrushType.PATTERN, points, mPatternUnits);
+ brush.setContentUnits(mPatternContentUnits);
+ brush.setPattern(this);
+
+ if (mMatrix != null) {
+ brush.setGradientTransform(mMatrix);
+ }
+
+ SvgView svg = getSvgView();
+ if (mPatternUnits == Brush.BrushUnits.USER_SPACE_ON_USE
+ || mPatternContentUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
+ brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
+ }
+
+ svg.defineBrush(brush, mName);
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PropHelper.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PropHelper.java
new file mode 100644
index 0000000000000..c56e98e97c712
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/PropHelper.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import com.facebook.react.bridge.ReadableArray;
+
+/** Contains static helper methods for accessing props. */
+class PropHelper {
+
+ private static final int inputMatrixDataSize = 6;
+
+ /**
+ * Converts given {@link ReadableArray} to a matrix data array, {@code float[6]}. Writes result to
+ * the array passed in {@param into}. This method will write exactly six items to the output array
+ * from the input array.
+ *
+ * If the input array has a different size, then only the size is returned; Does not check
+ * output array size. Ensure space for at least six elements.
+ *
+ * @param value input array
+ * @param sRawMatrix output matrix
+ * @param mScale current resolution scaling
+ * @return size of input array
+ */
+ static int toMatrixData(ReadableArray value, float[] sRawMatrix, float mScale) {
+ int fromSize = value.size();
+ if (fromSize != inputMatrixDataSize) {
+ return fromSize;
+ }
+
+ sRawMatrix[0] = (float) value.getDouble(0);
+ sRawMatrix[1] = (float) value.getDouble(2);
+ sRawMatrix[2] = (float) value.getDouble(4) * mScale;
+ sRawMatrix[3] = (float) value.getDouble(1);
+ sRawMatrix[4] = (float) value.getDouble(3);
+ sRawMatrix[5] = (float) value.getDouble(5) * mScale;
+
+ return inputMatrixDataSize;
+ }
+
+ /**
+ * Converts length string into px / user units in the current user coordinate system
+ *
+ * @param length length string
+ * @param relative relative size for percentages
+ * @param scale scaling parameter
+ * @param fontSize current font size
+ * @return value in the current user coordinate system
+ */
+ static double fromRelative(String length, double relative, double scale, double fontSize) {
+ /*
+ TODO list
+
+ unit relative to
+ em font size of the element
+ ex x-height of the element’s font
+ ch width of the "0" (ZERO, U+0030) glyph in the element’s font
+ rem font size of the root element
+ vw 1% of viewport’s width
+ vh 1% of viewport’s height
+ vmin 1% of viewport’s smaller dimension
+ vmax 1% of viewport’s larger dimension
+
+ relative-size [ larger | smaller ]
+ absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
+
+ https://www.w3.org/TR/css3-values/#relative-lengths
+ https://www.w3.org/TR/css3-values/#absolute-lengths
+ https://drafts.csswg.org/css-cascade-4/#computed-value
+ https://drafts.csswg.org/css-fonts-3/#propdef-font-size
+ https://drafts.csswg.org/css2/fonts.html#propdef-font-size
+ */
+ length = length.trim();
+ int stringLength = length.length();
+ int percentIndex = stringLength - 1;
+ if (stringLength == 0 || length.equals("normal")) {
+ return 0d;
+ } else if (length.codePointAt(percentIndex) == '%') {
+ return Double.valueOf(length.substring(0, percentIndex)) / 100 * relative;
+ } else {
+ int twoLetterUnitIndex = stringLength - 2;
+ if (twoLetterUnitIndex > 0) {
+ String lastTwo = length.substring(twoLetterUnitIndex);
+ int end = twoLetterUnitIndex;
+ double unit = 1;
+
+ switch (lastTwo) {
+ case "px":
+ break;
+
+ case "em":
+ unit = fontSize;
+ break;
+
+ /*
+ "1pt" equals "1.25px" (and therefore 1.25 user units)
+ "1pc" equals "15px" (and therefore 15 user units)
+ "1mm" would be "3.543307px" (3.543307 user units)
+ "1cm" equals "35.43307px" (and therefore 35.43307 user units)
+ "1in" equals "90px" (and therefore 90 user units)
+ */
+
+ case "pt":
+ unit = 1.25d;
+ break;
+
+ case "pc":
+ unit = 15;
+ break;
+
+ case "mm":
+ unit = 3.543307d;
+ break;
+
+ case "cm":
+ unit = 35.43307d;
+ break;
+
+ case "in":
+ unit = 90;
+ break;
+
+ default:
+ end = stringLength;
+ }
+
+ return Double.valueOf(length.substring(0, end)) * unit * scale;
+ } else {
+ return Double.valueOf(length) * scale;
+ }
+ }
+ }
+ /**
+ * Converts SVGLength into px / user units in the current user coordinate system
+ *
+ * @param length length string
+ * @param relative relative size for percentages
+ * @param offset offset for all units
+ * @param scale scaling parameter
+ * @param fontSize current font size
+ * @return value in the current user coordinate system
+ */
+ static double fromRelative(
+ SVGLength length, double relative, double offset, double scale, double fontSize) {
+ /*
+ TODO list
+
+ unit relative to
+ em font size of the element
+ ex x-height of the element’s font
+ ch width of the "0" (ZERO, U+0030) glyph in the element’s font
+ rem font size of the root element
+ vw 1% of viewport’s width
+ vh 1% of viewport’s height
+ vmin 1% of viewport’s smaller dimension
+ vmax 1% of viewport’s larger dimension
+
+ relative-size [ larger | smaller ]
+ absolute-size: [ xx-small | x-small | small | medium | large | x-large | xx-large ]
+
+ https://www.w3.org/TR/css3-values/#relative-lengths
+ https://www.w3.org/TR/css3-values/#absolute-lengths
+ https://drafts.csswg.org/css-cascade-4/#computed-value
+ https://drafts.csswg.org/css-fonts-3/#propdef-font-size
+ https://drafts.csswg.org/css2/fonts.html#propdef-font-size
+ */
+ if (length == null) {
+ return offset;
+ }
+ SVGLength.UnitType unitType = length.unit;
+ double value = length.value;
+ double unit = 1;
+ switch (unitType) {
+ case NUMBER:
+ case PX:
+ break;
+
+ case PERCENTAGE:
+ return value / 100 * relative + offset;
+
+ case EMS:
+ unit = fontSize;
+ break;
+ case EXS:
+ unit = fontSize / 2;
+ break;
+
+ case CM:
+ unit = 35.43307;
+ break;
+ case MM:
+ unit = 3.543307;
+ break;
+ case IN:
+ unit = 90;
+ break;
+ case PT:
+ unit = 1.25;
+ break;
+ case PC:
+ unit = 15;
+ break;
+
+ default:
+ case UNKNOWN:
+ return value * scale + offset;
+ }
+ return value * unit * scale + offset;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RNSVGMarkerPosition.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RNSVGMarkerPosition.java
new file mode 100644
index 0000000000000..83ae08cdab86d
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RNSVGMarkerPosition.java
@@ -0,0 +1,172 @@
+package com.horcrux.svg;
+
+import java.util.ArrayList;
+
+enum RNSVGMarkerType {
+ kStartMarker,
+ kMidMarker,
+ kEndMarker
+}
+
+enum ElementType {
+ kCGPathElementAddCurveToPoint,
+ kCGPathElementAddQuadCurveToPoint,
+ kCGPathElementMoveToPoint,
+ kCGPathElementAddLineToPoint,
+ kCGPathElementCloseSubpath
+}
+
+class Point {
+ double x;
+ double y;
+
+ Point(double x, double y) {
+ this.x = x;
+ this.y = y;
+ }
+}
+
+class SegmentData {
+ Point start_tangent; // Tangent in the start point of the segment.
+ Point end_tangent; // Tangent in the end point of the segment.
+ Point position; // The end point of the segment.
+}
+
+class RNSVGMarkerPosition {
+
+ private static ArrayList positions_;
+ private static int element_index_;
+ private static Point origin_;
+ private static Point subpath_start_;
+ private static Point in_slope_;
+ private static Point out_slope_;
+
+ @SuppressWarnings("unused")
+ private static boolean auto_start_reverse_; // TODO
+
+ RNSVGMarkerType type;
+ Point origin;
+ double angle;
+
+ private RNSVGMarkerPosition(RNSVGMarkerType type, Point origin, double angle) {
+ this.type = type;
+ this.origin = origin;
+ this.angle = angle;
+ }
+
+ static ArrayList fromPath(ArrayList elements) {
+ positions_ = new ArrayList<>();
+ element_index_ = 0;
+ origin_ = new Point(0, 0);
+ subpath_start_ = new Point(0, 0);
+ for (PathElement e : elements) {
+ UpdateFromPathElement(e);
+ }
+ PathIsDone();
+ return positions_;
+ }
+
+ private static void PathIsDone() {
+ double angle = CurrentAngle(RNSVGMarkerType.kEndMarker);
+ positions_.add(new RNSVGMarkerPosition(RNSVGMarkerType.kEndMarker, origin_, angle));
+ }
+
+ private static double BisectingAngle(double in_angle, double out_angle) {
+ // WK193015: Prevent bugs due to angles being non-continuous.
+ if (Math.abs(in_angle - out_angle) > 180) in_angle += 360;
+ return (in_angle + out_angle) / 2;
+ }
+
+ private static double rad2deg(double rad) {
+ double RNSVG_radToDeg = 180 / Math.PI;
+ return rad * RNSVG_radToDeg;
+ }
+
+ private static double SlopeAngleRadians(Point p) {
+ return Math.atan2(p.y, p.x);
+ }
+
+ private static double CurrentAngle(RNSVGMarkerType type) {
+ // For details of this calculation, see:
+ // http://www.w3.org/TR/SVG/single-page.html#painting-MarkerElement
+ double in_angle = rad2deg(SlopeAngleRadians(in_slope_));
+ double out_angle = rad2deg(SlopeAngleRadians(out_slope_));
+ switch (type) {
+ case kStartMarker:
+ if (auto_start_reverse_) out_angle += 180;
+ return out_angle;
+ case kMidMarker:
+ return BisectingAngle(in_angle, out_angle);
+ case kEndMarker:
+ return in_angle;
+ }
+ return 0;
+ }
+
+ private static Point subtract(Point p1, Point p2) {
+ return new Point(p2.x - p1.x, p2.y - p1.y);
+ }
+
+ private static boolean isZero(Point p) {
+ return p.x == 0 && p.y == 0;
+ }
+
+ private static void ComputeQuadTangents(SegmentData data, Point start, Point control, Point end) {
+ data.start_tangent = subtract(control, start);
+ data.end_tangent = subtract(end, control);
+ if (isZero(data.start_tangent)) data.start_tangent = data.end_tangent;
+ else if (isZero(data.end_tangent)) data.end_tangent = data.start_tangent;
+ }
+
+ private static SegmentData ExtractPathElementFeatures(PathElement element) {
+ SegmentData data = new SegmentData();
+ Point[] points = element.points;
+ switch (element.type) {
+ case kCGPathElementAddCurveToPoint:
+ data.position = points[2];
+ data.start_tangent = subtract(points[0], origin_);
+ data.end_tangent = subtract(points[2], points[1]);
+ if (isZero(data.start_tangent)) ComputeQuadTangents(data, points[0], points[1], points[2]);
+ else if (isZero(data.end_tangent)) ComputeQuadTangents(data, origin_, points[0], points[1]);
+ break;
+ case kCGPathElementAddQuadCurveToPoint:
+ data.position = points[1];
+ ComputeQuadTangents(data, origin_, points[0], points[1]);
+ break;
+ case kCGPathElementMoveToPoint:
+ case kCGPathElementAddLineToPoint:
+ data.position = points[0];
+ data.start_tangent = subtract(data.position, origin_);
+ data.end_tangent = subtract(data.position, origin_);
+ break;
+ case kCGPathElementCloseSubpath:
+ data.position = subpath_start_;
+ data.start_tangent = subtract(data.position, origin_);
+ data.end_tangent = subtract(data.position, origin_);
+ break;
+ }
+ return data;
+ }
+
+ private static void UpdateFromPathElement(PathElement element) {
+ SegmentData segment_data = ExtractPathElementFeatures(element);
+ // First update the outgoing slope for the previous element.
+ out_slope_ = segment_data.start_tangent;
+ // Record the marker for the previous element.
+ if (element_index_ > 0) {
+ RNSVGMarkerType marker_type =
+ element_index_ == 1 ? RNSVGMarkerType.kStartMarker : RNSVGMarkerType.kMidMarker;
+ double angle = CurrentAngle(marker_type);
+ positions_.add(new RNSVGMarkerPosition(marker_type, origin_, angle));
+ }
+ // Update the incoming slope for this marker position.
+ in_slope_ = segment_data.end_tangent;
+ // Update marker position.
+ origin_ = segment_data.position;
+ // If this is a 'move to' segment, save the point for use with 'close'.
+ if (element.type == ElementType.kCGPathElementMoveToPoint) subpath_start_ = element.points[0];
+ else if (element.type == ElementType.kCGPathElementCloseSubpath)
+ subpath_start_ = new Point(0, 0);
+ ++element_index_;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java
new file mode 100644
index 0000000000000..87d2480c0c1bb
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import static com.facebook.react.common.StandardCharsets.UTF_8;
+
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.RectF;
+import android.graphics.Region;
+import com.facebook.react.bridge.Arguments;
+import com.facebook.react.bridge.Promise;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.WritableMap;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import javax.annotation.Nonnull;
+
+class RNSVGRenderableManager extends ReactContextBaseJavaModule {
+ RNSVGRenderableManager(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @Nonnull
+ @Override
+ public String getName() {
+ return "RNSVGRenderableManager";
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod(isBlockingSynchronousMethod = true)
+ public boolean isPointInFill(int tag, ReadableMap options) {
+ RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
+ if (svg == null) {
+ return false;
+ }
+
+ float scale = svg.mScale;
+ float x = (float) options.getDouble("x") * scale;
+ float y = (float) options.getDouble("y") * scale;
+
+ int i = svg.hitTest(new float[] {x, y});
+ return i != -1;
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod(isBlockingSynchronousMethod = true)
+ public boolean isPointInStroke(int tag, ReadableMap options) {
+ RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
+ if (svg == null) {
+ return false;
+ }
+
+ try {
+ svg.getPath(null, null);
+ } catch (NullPointerException e) {
+ svg.invalidate();
+ return false;
+ }
+
+ svg.initBounds();
+
+ float scale = svg.mScale;
+ int x = (int) (options.getDouble("x") * scale);
+ int y = (int) (options.getDouble("y") * scale);
+
+ Region strokeRegion = svg.mStrokeRegion;
+ return strokeRegion != null && strokeRegion.contains(x, y);
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod(isBlockingSynchronousMethod = true)
+ public float getTotalLength(int tag) {
+ RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
+ if (svg == null) {
+ return 0;
+ }
+
+ Path path;
+
+ try {
+ path = svg.getPath(null, null);
+ } catch (NullPointerException e) {
+ svg.invalidate();
+ return -1;
+ }
+
+ PathMeasure pm = new PathMeasure(path, false);
+ return pm.getLength() / svg.mScale;
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod(isBlockingSynchronousMethod = true)
+ public WritableMap getPointAtLength(int tag, ReadableMap options) {
+ RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
+ if (svg == null) {
+ return Arguments.createMap();
+ }
+
+ Path path;
+
+ try {
+ path = svg.getPath(null, null);
+ } catch (NullPointerException e) {
+ svg.invalidate();
+ return Arguments.createMap();
+ }
+
+ PathMeasure pm = new PathMeasure(path, false);
+ float length = (float) options.getDouble("length");
+ float scale = svg.mScale;
+
+ float[] pos = new float[2];
+ float[] tan = new float[2];
+ float distance = Math.max(0, Math.min(length * scale, pm.getLength()));
+ pm.getPosTan(distance, pos, tan);
+
+ double angle = Math.atan2(tan[1], tan[0]);
+ WritableMap result = Arguments.createMap();
+ result.putDouble("x", pos[0] / scale);
+ result.putDouble("y", pos[1] / scale);
+ result.putDouble("angle", angle);
+ return result;
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod(isBlockingSynchronousMethod = true)
+ public WritableMap getBBox(int tag, ReadableMap options) {
+ RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
+ if (svg == null) {
+ return Arguments.createMap();
+ }
+
+ boolean fill = options.getBoolean("fill");
+ boolean stroke = options.getBoolean("stroke");
+ boolean markers = options.getBoolean("markers");
+ boolean clipped = options.getBoolean("clipped");
+
+ try {
+ svg.getPath(null, null);
+ } catch (NullPointerException e) {
+ svg.invalidate();
+ return Arguments.createMap();
+ }
+
+ float scale = svg.mScale;
+ svg.initBounds();
+
+ RectF bounds = new RectF();
+ RectF fillBounds = svg.mFillBounds;
+ RectF strokeBounds = svg.mStrokeBounds;
+ RectF markerBounds = svg.mMarkerBounds;
+ RectF clipBounds = svg.mClipBounds;
+
+ if (fill && fillBounds != null) {
+ bounds.union(fillBounds);
+ }
+ if (stroke && strokeBounds != null) {
+ bounds.union(strokeBounds);
+ }
+ if (markers && markerBounds != null) {
+ bounds.union(markerBounds);
+ }
+ if (clipped && clipBounds != null) {
+ bounds.intersect(clipBounds);
+ }
+
+ WritableMap result = Arguments.createMap();
+ result.putDouble("x", bounds.left / scale);
+ result.putDouble("y", bounds.top / scale);
+ result.putDouble("width", bounds.width() / scale);
+ result.putDouble("height", bounds.height() / scale);
+ return result;
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod(isBlockingSynchronousMethod = true)
+ public WritableMap getCTM(int tag) {
+ RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
+ if (svg == null) {
+ return Arguments.createMap();
+ }
+
+ float scale = svg.mScale;
+ Matrix ctm = new Matrix(svg.mCTM);
+ Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix;
+ ctm.preConcat(invViewBoxMatrix);
+
+ float[] values = new float[9];
+ ctm.getValues(values);
+
+ WritableMap result = Arguments.createMap();
+ result.putDouble("a", values[Matrix.MSCALE_X]);
+ result.putDouble("b", values[Matrix.MSKEW_Y]);
+ result.putDouble("c", values[Matrix.MSKEW_X]);
+ result.putDouble("d", values[Matrix.MSCALE_Y]);
+ result.putDouble("e", values[Matrix.MTRANS_X] / scale);
+ result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
+ return result;
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod(isBlockingSynchronousMethod = true)
+ public WritableMap getScreenCTM(int tag) {
+ RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
+ if (svg == null) {
+ return Arguments.createMap();
+ }
+
+ float[] values = new float[9];
+ svg.mCTM.getValues(values);
+ float scale = svg.mScale;
+
+ WritableMap result = Arguments.createMap();
+ result.putDouble("a", values[Matrix.MSCALE_X]);
+ result.putDouble("b", values[Matrix.MSKEW_Y]);
+ result.putDouble("c", values[Matrix.MSKEW_X]);
+ result.putDouble("d", values[Matrix.MSCALE_Y]);
+ result.putDouble("e", values[Matrix.MTRANS_X] / scale);
+ result.putDouble("f", values[Matrix.MTRANS_Y] / scale);
+ return result;
+ }
+
+ @ReactMethod
+ public void getRawResource(String name, Promise promise) {
+ try {
+ ReactApplicationContext context = getReactApplicationContext();
+ Resources resources = context.getResources();
+ String packageName = context.getPackageName();
+ int id = resources.getIdentifier(name, "raw", packageName);
+ InputStream stream = resources.openRawResource(id);
+ try {
+ InputStreamReader reader = new InputStreamReader(stream, UTF_8);
+ char[] buffer = new char[DEFAULT_BUFFER_SIZE];
+ StringBuilder builder = new StringBuilder();
+ int n;
+ while ((n = reader.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != EOF) {
+ builder.append(buffer, 0, n);
+ }
+ String result = builder.toString();
+ promise.resolve(result);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ promise.reject(e);
+ }
+ }
+
+ private static final int EOF = -1;
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RadialGradientView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RadialGradientView.java
new file mode 100644
index 0000000000000..5f1e9ea72f857
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RadialGradientView.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Matrix;
+import com.facebook.common.logging.FLog;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.common.ReactConstants;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class RadialGradientView extends DefinitionView {
+ private SVGLength mFx;
+ private SVGLength mFy;
+ private SVGLength mRx;
+ private SVGLength mRy;
+ private SVGLength mCx;
+ private SVGLength mCy;
+ private ReadableArray mGradient;
+ private Brush.BrushUnits mGradientUnits;
+
+ private static final float[] sRawMatrix =
+ new float[] {
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+ };
+ private Matrix mMatrix = null;
+
+ public RadialGradientView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setFx(Dynamic fx) {
+ mFx = SVGLength.from(fx);
+ invalidate();
+ }
+
+ public void setFx(String fx) {
+ mFx = SVGLength.from(fx);
+ invalidate();
+ }
+
+ public void setFx(Double fx) {
+ mFx = SVGLength.from(fx);
+ invalidate();
+ }
+
+ public void setFy(Dynamic fy) {
+ mFy = SVGLength.from(fy);
+ invalidate();
+ }
+
+ public void setFy(String fy) {
+ mFy = SVGLength.from(fy);
+ invalidate();
+ }
+
+ public void setFy(Double fy) {
+ mFy = SVGLength.from(fy);
+ invalidate();
+ }
+
+ public void setRx(Dynamic rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRx(String rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRx(Double rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRy(Dynamic ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ public void setRy(String ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ public void setRy(Double ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ public void setCx(Dynamic cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCx(String cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCx(Double cx) {
+ mCx = SVGLength.from(cx);
+ invalidate();
+ }
+
+ public void setCy(Dynamic cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setCy(String cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setCy(Double cy) {
+ mCy = SVGLength.from(cy);
+ invalidate();
+ }
+
+ public void setGradient(ReadableArray gradient) {
+ mGradient = gradient;
+ invalidate();
+ }
+
+ public void setGradientUnits(int gradientUnits) {
+ switch (gradientUnits) {
+ case 0:
+ mGradientUnits = Brush.BrushUnits.OBJECT_BOUNDING_BOX;
+ break;
+ case 1:
+ mGradientUnits = Brush.BrushUnits.USER_SPACE_ON_USE;
+ break;
+ }
+ invalidate();
+ }
+
+ public void setGradientTransform(@Nullable ReadableArray matrixArray) {
+ if (matrixArray != null) {
+ int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
+ if (matrixSize == 6) {
+ if (mMatrix == null) {
+ mMatrix = new Matrix();
+ }
+ mMatrix.setValues(sRawMatrix);
+ } else if (matrixSize != -1) {
+ FLog.w(ReactConstants.TAG, "RNSVG: Transform matrices must be of size 6");
+ }
+ } else {
+ mMatrix = null;
+ }
+
+ invalidate();
+ }
+
+ @Override
+ void saveDefinition() {
+ if (mName != null) {
+ SVGLength[] points = new SVGLength[] {mFx, mFy, mRx, mRy, mCx, mCy};
+ Brush brush = new Brush(Brush.BrushType.RADIAL_GRADIENT, points, mGradientUnits);
+ brush.setGradientColors(mGradient);
+ if (mMatrix != null) {
+ brush.setGradientTransform(mMatrix);
+ }
+
+ SvgView svg = getSvgView();
+ if (mGradientUnits == Brush.BrushUnits.USER_SPACE_ON_USE) {
+ brush.setUserSpaceBoundingBox(svg.getCanvasBounds());
+ }
+
+ svg.defineBrush(brush, mName);
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RectView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RectView.java
new file mode 100644
index 0000000000000..a146b2d487056
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RectView.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.os.Build;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class RectView extends RenderableView {
+ private SVGLength mX;
+ private SVGLength mY;
+ private SVGLength mW;
+ private SVGLength mH;
+ private SVGLength mRx;
+ private SVGLength mRy;
+
+ public RectView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setX(Dynamic x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(String x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setX(Double x) {
+ mX = SVGLength.from(x);
+ invalidate();
+ }
+
+ public void setY(Dynamic y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(String y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setY(Double y) {
+ mY = SVGLength.from(y);
+ invalidate();
+ }
+
+ public void setWidth(Dynamic width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(String width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setWidth(Double width) {
+ mW = SVGLength.from(width);
+ invalidate();
+ }
+
+ public void setHeight(Dynamic height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(String height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setHeight(Double height) {
+ mH = SVGLength.from(height);
+ invalidate();
+ }
+
+ public void setRx(Dynamic rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRx(String rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRx(Double rx) {
+ mRx = SVGLength.from(rx);
+ invalidate();
+ }
+
+ public void setRy(Dynamic ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ public void setRy(String ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ public void setRy(Double ry) {
+ mRy = SVGLength.from(ry);
+ invalidate();
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ Path path = new Path();
+ double x = relativeOnWidth(mX);
+ double y = relativeOnHeight(mY);
+ double w = relativeOnWidth(mW);
+ double h = relativeOnHeight(mH);
+
+ if (mRx != null || mRy != null) {
+ double rx = 0d;
+ double ry = 0d;
+ if (mRx == null) {
+ ry = relativeOnHeight(mRy);
+ rx = ry;
+ } else if (mRy == null) {
+ rx = relativeOnWidth(mRx);
+ ry = rx;
+ } else {
+ rx = relativeOnWidth(mRx);
+ ry = relativeOnHeight(mRy);
+ }
+
+ if (rx > w / 2) {
+ rx = w / 2;
+ }
+
+ if (ry > h / 2) {
+ ry = h / 2;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ path.addRoundRect(
+ (float) x,
+ (float) y,
+ (float) (x + w),
+ (float) (y + h),
+ (float) rx,
+ (float) ry,
+ Path.Direction.CW);
+ } else {
+ path.addRoundRect(
+ new RectF((float) x, (float) y, (float) (x + w), (float) (y + h)),
+ (float) rx,
+ (float) ry,
+ Path.Direction.CW);
+ }
+ } else {
+ path.addRect((float) x, (float) y, (float) (x + w), (float) (y + h), Path.Direction.CW);
+ path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using
+ // integers
+ }
+ return path;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RenderableView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RenderableView.java
new file mode 100644
index 0000000000000..2aa196cdf5ce4
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RenderableView.java
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.DashPathEffect;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import com.facebook.react.bridge.ColorPropConverter;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
+import com.facebook.react.bridge.JavaOnlyArray;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.ReadableType;
+import com.facebook.react.uimanager.PointerEvents;
+import com.facebook.react.touch.ReactHitSlopView;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.annotation.Nullable;
+
+@SuppressWarnings({"WeakerAccess", "RedundantSuppression"})
+abstract public class RenderableView extends VirtualView implements ReactHitSlopView {
+
+ RenderableView(ReactContext reactContext) {
+ super(reactContext);
+ setPivotX(0);
+ setPivotY(0);
+ }
+
+ static RenderableView contextElement;
+ // strokeLinecap
+ private static final int CAP_BUTT = 0;
+ static final int CAP_ROUND = 1;
+ private static final int CAP_SQUARE = 2;
+
+ // strokeLinejoin
+ private static final int JOIN_BEVEL = 2;
+ private static final int JOIN_MITER = 0;
+ static final int JOIN_ROUND = 1;
+
+ // fillRule
+ private static final int FILL_RULE_EVENODD = 0;
+ static final int FILL_RULE_NONZERO = 1;
+
+ // vectorEffect
+ private static final int VECTOR_EFFECT_DEFAULT = 0;
+ private static final int VECTOR_EFFECT_NON_SCALING_STROKE = 1;
+ // static final int VECTOR_EFFECT_INHERIT = 2;
+ // static final int VECTOR_EFFECT_URI = 3;
+
+ /*
+ Used in mergeProperties, keep public
+ */
+
+ public int vectorEffect = VECTOR_EFFECT_DEFAULT;
+ public @Nullable ReadableArray stroke;
+ public @Nullable SVGLength[] strokeDasharray;
+
+ public SVGLength strokeWidth = new SVGLength(1);
+ public float strokeOpacity = 1;
+ public float strokeMiterlimit = 4;
+ public float strokeDashoffset = 0;
+
+ public Paint.Cap strokeLinecap = Paint.Cap.BUTT;
+ public Paint.Join strokeLinejoin = Paint.Join.MITER;
+
+ public @Nullable ReadableArray fill;
+ public float fillOpacity = 1;
+ public Path.FillType fillRule = Path.FillType.WINDING;
+
+ /*
+ End merged properties
+ */
+ private @Nullable ArrayList mLastMergedList;
+ private @Nullable ArrayList mOriginProperties;
+ private @Nullable ArrayList mPropList;
+ private @Nullable ArrayList mAttributeList;
+
+ private static final Pattern regex = Pattern.compile("[0-9.-]+");
+
+ @Nullable
+ public Rect getHitSlopRect() {
+ /*
+ * In order to make the isTouchPointInView fail we need to return a very improbable Rect for the View
+ * This way an SVG with box_none carrying its last descendent with box_none will have the expected behavior of just having events on the actual painted area
+ */
+ if (mPointerEvents == PointerEvents.BOX_NONE) {
+ return new Rect(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
+ }
+ return null;
+ }
+
+ @Override
+ public void setId(int id) {
+ super.setId(id);
+ RenderableViewManager.setRenderableView(id, this);
+ }
+
+ public void setVectorEffect(int vectorEffect) {
+ this.vectorEffect = vectorEffect;
+ invalidate();
+ }
+
+ public void setFill(@Nullable Dynamic fill) {
+ if (fill == null || fill.isNull()) {
+ this.fill = null;
+ invalidate();
+ return;
+ }
+
+ ReadableType fillType = fill.getType();
+ if (fillType.equals(ReadableType.Map)) {
+ ReadableMap fillMap = fill.asMap();
+ setFill(fillMap);
+ return;
+ }
+
+ // This code will probably never be reached with current changes
+ if (fillType.equals(ReadableType.Number)) {
+ this.fill = JavaOnlyArray.of(0, fill.asInt());
+ } else if (fillType.equals(ReadableType.Array)) {
+ this.fill = fill.asArray();
+ } else {
+ JavaOnlyArray arr = new JavaOnlyArray();
+ arr.pushInt(0);
+ Matcher m = regex.matcher(fill.asString());
+ int i = 0;
+ while (m.find()) {
+ double parsed = Double.parseDouble(m.group());
+ arr.pushDouble(i++ < 3 ? parsed / 255 : parsed);
+ }
+ this.fill = arr;
+ }
+ invalidate();
+ }
+
+ public void setFill(ReadableMap fill) {
+ if (fill == null) {
+ this.fill = null;
+ invalidate();
+ return;
+ }
+ int type = fill.getInt("type");
+ if (type == 0) {
+ ReadableType valueType = fill.getType("payload");
+ if (valueType.equals(ReadableType.Number)) {
+ this.fill = JavaOnlyArray.of(0, fill.getInt("payload"));
+ } else if (valueType.equals(ReadableType.Map)) {
+ this.fill = JavaOnlyArray.of(0, fill.getMap("payload"));
+ }
+ } else if (type == 1) {
+ this.fill = JavaOnlyArray.of(1, fill.getString("brushRef"));
+ } else {
+ this.fill = JavaOnlyArray.of(type);
+ }
+ invalidate();
+ }
+
+ public void setFillOpacity(float fillOpacity) {
+ this.fillOpacity = fillOpacity;
+ invalidate();
+ }
+
+ public void setFillRule(int fillRule) {
+ switch (fillRule) {
+ case FILL_RULE_EVENODD:
+ this.fillRule = Path.FillType.EVEN_ODD;
+ break;
+ case FILL_RULE_NONZERO:
+ break;
+ default:
+ throw new JSApplicationIllegalArgumentException("fillRule " + fillRule + " unrecognized");
+ }
+
+ invalidate();
+ }
+
+ public void setStroke(@Nullable Dynamic strokeColors) {
+ if (strokeColors == null || strokeColors.isNull()) {
+ stroke = null;
+ invalidate();
+ return;
+ }
+
+ ReadableType strokeType = strokeColors.getType();
+ if (strokeType.equals(ReadableType.Map)) {
+ ReadableMap strokeMap = strokeColors.asMap();
+ setStroke(strokeMap);
+ return;
+ }
+
+ // This code will probably never be reached with current changes
+ ReadableType type = strokeColors.getType();
+ if (type.equals(ReadableType.Number)) {
+ stroke = JavaOnlyArray.of(0, strokeColors.asInt());
+ } else if (type.equals(ReadableType.Array)) {
+ stroke = strokeColors.asArray();
+ } else {
+ JavaOnlyArray arr = new JavaOnlyArray();
+ arr.pushInt(0);
+ Matcher m = regex.matcher(strokeColors.asString());
+ int i = 0;
+ while (m.find()) {
+ double parsed = Double.parseDouble(m.group());
+ arr.pushDouble(i++ < 3 ? parsed / 255 : parsed);
+ }
+ stroke = arr;
+ }
+ invalidate();
+ }
+
+ public void setStroke(@Nullable ReadableMap stroke) {
+ if (stroke == null) {
+ this.stroke = null;
+ invalidate();
+ return;
+ }
+ int type = stroke.getInt("type");
+ if (type == 0) {
+ ReadableType payloadType = stroke.getType("payload");
+ if (payloadType.equals(ReadableType.Number)) {
+ this.stroke = JavaOnlyArray.of(0, stroke.getInt("payload"));
+ } else if (payloadType.equals(ReadableType.Map)) {
+ this.stroke = JavaOnlyArray.of(0, stroke.getMap("payload"));
+ }
+ } else if (type == 1) {
+ this.stroke = JavaOnlyArray.of(1, stroke.getString("brushRef"));
+ } else {
+ this.stroke = JavaOnlyArray.of(type);
+ }
+ invalidate();
+ }
+
+ public void setStrokeOpacity(float strokeOpacity) {
+ this.strokeOpacity = strokeOpacity;
+ invalidate();
+ }
+
+ public void setStrokeDasharray(@Nullable ReadableArray strokeDasharray) {
+ if (strokeDasharray != null) {
+ int fromSize = strokeDasharray.size();
+ this.strokeDasharray = new SVGLength[fromSize];
+ for (int i = 0; i < fromSize; i++) {
+ this.strokeDasharray[i] = SVGLength.from(strokeDasharray.getDynamic(i));
+ }
+ } else {
+ this.strokeDasharray = null;
+ }
+ invalidate();
+ }
+
+ public void setStrokeDashoffset(float strokeDashoffset) {
+ this.strokeDashoffset = strokeDashoffset * mScale;
+ invalidate();
+ }
+
+ public void setStrokeWidth(Dynamic strokeWidth) {
+ this.strokeWidth = SVGLength.from(strokeWidth);
+ invalidate();
+ }
+
+ public void setStrokeWidth(String strokeWidth) {
+ this.strokeWidth = SVGLength.from(strokeWidth);
+ invalidate();
+ }
+
+ public void setStrokeWidth(Double strokeWidth) {
+ this.strokeWidth = SVGLength.from(strokeWidth);
+ invalidate();
+ }
+
+ public void setStrokeMiterlimit(float strokeMiterlimit) {
+ this.strokeMiterlimit = strokeMiterlimit;
+ invalidate();
+ }
+
+ public void setStrokeLinecap(int strokeLinecap) {
+ switch (strokeLinecap) {
+ case CAP_BUTT:
+ this.strokeLinecap = Paint.Cap.BUTT;
+ break;
+ case CAP_SQUARE:
+ this.strokeLinecap = Paint.Cap.SQUARE;
+ break;
+ case CAP_ROUND:
+ this.strokeLinecap = Paint.Cap.ROUND;
+ break;
+ default:
+ throw new JSApplicationIllegalArgumentException(
+ "strokeLinecap " + strokeLinecap + " unrecognized");
+ }
+ invalidate();
+ }
+
+ public void setStrokeLinejoin(int strokeLinejoin) {
+ switch (strokeLinejoin) {
+ case JOIN_MITER:
+ this.strokeLinejoin = Paint.Join.MITER;
+ break;
+ case JOIN_BEVEL:
+ this.strokeLinejoin = Paint.Join.BEVEL;
+ break;
+ case JOIN_ROUND:
+ this.strokeLinejoin = Paint.Join.ROUND;
+ break;
+ default:
+ throw new JSApplicationIllegalArgumentException(
+ "strokeLinejoin " + strokeLinejoin + " unrecognized");
+ }
+ invalidate();
+ }
+
+ public void setPropList(@Nullable ReadableArray propList) {
+ if (propList != null) {
+ mPropList = mAttributeList = new ArrayList<>();
+ for (int i = 0; i < propList.size(); i++) {
+ mPropList.add(propList.getString(i));
+ }
+ }
+
+ invalidate();
+ }
+
+ private static double saturate(double v) {
+ return v <= 0 ? 0 : (v >= 1 ? 1 : v);
+ }
+
+ void render(Canvas canvas, Paint paint, float opacity) {
+ MaskView mask = null;
+ if (mMask != null) {
+ SvgView root = getSvgView();
+ mask = (MaskView) root.getDefinedMask(mMask);
+ }
+ if (mask != null) {
+ Rect clipBounds = canvas.getClipBounds();
+ int height = clipBounds.height();
+ int width = clipBounds.width();
+
+ Bitmap maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Bitmap original = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+ Canvas originalCanvas = new Canvas(original);
+ Canvas maskCanvas = new Canvas(maskBitmap);
+ Canvas resultCanvas = new Canvas(result);
+
+ // Clip to mask bounds and render the mask
+ float maskX = (float) relativeOnWidth(mask.mX);
+ float maskY = (float) relativeOnHeight(mask.mY);
+ float maskWidth = (float) relativeOnWidth(mask.mW);
+ float maskHeight = (float) relativeOnHeight(mask.mH);
+ maskCanvas.clipRect(maskX, maskY, maskWidth, maskHeight);
+
+ Paint maskPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mask.draw(maskCanvas, maskPaint, 1);
+
+ // Apply luminanceToAlpha filter primitive
+ // https://www.w3.org/TR/SVG11/filters.html#feColorMatrixElement
+ int nPixels = width * height;
+ int[] pixels = new int[nPixels];
+ maskBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+
+ for (int i = 0; i < nPixels; i++) {
+ int color = pixels[i];
+
+ int r = (color >> 16) & 0xFF;
+ int g = (color >> 8) & 0xFF;
+ int b = color & 0xFF;
+ int a = color >>> 24;
+
+ double luminance = saturate(((0.299 * r) + (0.587 * g) + (0.144 * b)) / 255);
+ int alpha = (int) (a * luminance);
+ int pixel = (alpha << 24);
+ pixels[i] = pixel;
+ }
+
+ maskBitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+
+ // Render content of current SVG Renderable to image
+ draw(originalCanvas, paint, opacity);
+
+ // Blend current element and mask
+ maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
+ resultCanvas.drawBitmap(original, 0, 0, null);
+ resultCanvas.drawBitmap(maskBitmap, 0, 0, maskPaint);
+
+ // Render composited result into current render context
+ canvas.drawBitmap(result, 0, 0, paint);
+ } else {
+ draw(canvas, paint, opacity);
+ }
+ }
+
+ @Override
+ void draw(Canvas canvas, Paint paint, float opacity) {
+ opacity *= mOpacity;
+
+ boolean computePaths = mPath == null;
+ if (computePaths) {
+ mPath = getPath(canvas, paint);
+ mPath.setFillType(fillRule);
+ }
+ boolean nonScalingStroke = vectorEffect == VECTOR_EFFECT_NON_SCALING_STROKE;
+ Path path = mPath;
+ if (nonScalingStroke) {
+ Path scaled = new Path();
+ //noinspection deprecation
+ mPath.transform(mCTM, scaled);
+ canvas.setMatrix(null);
+ path = scaled;
+ }
+
+ if (computePaths || path != mPath) {
+ mBox = new RectF();
+ path.computeBounds(mBox, true);
+ }
+
+ RectF clientRect = new RectF(mBox);
+ mCTM.mapRect(clientRect);
+ this.setClientRect(clientRect);
+
+ clip(canvas, paint);
+
+ if (setupFillPaint(paint, opacity * fillOpacity)) {
+ if (computePaths) {
+ mFillPath = new Path();
+ paint.getFillPath(path, mFillPath);
+ }
+ canvas.drawPath(path, paint);
+ }
+ if (setupStrokePaint(paint, opacity * strokeOpacity)) {
+ if (computePaths) {
+ mStrokePath = new Path();
+ paint.getFillPath(path, mStrokePath);
+ }
+ canvas.drawPath(path, paint);
+ }
+ renderMarkers(canvas, paint, opacity);
+ }
+
+ void renderMarkers(Canvas canvas, Paint paint, float opacity) {
+ MarkerView markerStart = (MarkerView) getSvgView().getDefinedMarker(mMarkerStart);
+ MarkerView markerMid = (MarkerView) getSvgView().getDefinedMarker(mMarkerMid);
+ MarkerView markerEnd = (MarkerView) getSvgView().getDefinedMarker(mMarkerEnd);
+ if (elements != null && (markerStart != null || markerMid != null || markerEnd != null)) {
+ contextElement = this;
+ ArrayList positions = RNSVGMarkerPosition.fromPath(elements);
+ float width = (float) (this.strokeWidth != null ? relativeOnOther(this.strokeWidth) : 1);
+ mMarkerPath = new Path();
+ for (RNSVGMarkerPosition position : positions) {
+ RNSVGMarkerType type = position.type;
+ MarkerView marker = null;
+ switch (type) {
+ case kStartMarker:
+ marker = markerStart;
+ break;
+
+ case kMidMarker:
+ marker = markerMid;
+ break;
+
+ case kEndMarker:
+ marker = markerEnd;
+ break;
+ }
+ if (marker == null) {
+ continue;
+ }
+ marker.renderMarker(canvas, paint, opacity, position, width);
+ Matrix transform = marker.markerTransform;
+ mMarkerPath.addPath(marker.getPath(canvas, paint), transform);
+ }
+ contextElement = null;
+ }
+ }
+
+ /**
+ * Sets up paint according to the props set on a view. Returns {@code true} if the fill should be
+ * drawn, {@code false} if not.
+ */
+ boolean setupFillPaint(Paint paint, float opacity) {
+ if (fill != null && fill.size() > 0) {
+ paint.reset();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
+ paint.setStyle(Paint.Style.FILL);
+ setupPaint(paint, opacity, fill);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Sets up paint according to the props set on a view. Returns {@code true} if the stroke should
+ * be drawn, {@code false} if not.
+ */
+ boolean setupStrokePaint(Paint paint, float opacity) {
+ paint.reset();
+ double strokeWidth = relativeOnOther(this.strokeWidth);
+ if (strokeWidth == 0 || stroke == null || stroke.size() == 0) {
+ return false;
+ }
+
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeCap(strokeLinecap);
+ paint.setStrokeJoin(strokeLinejoin);
+ paint.setStrokeMiter(strokeMiterlimit * mScale);
+ paint.setStrokeWidth((float) strokeWidth);
+ setupPaint(paint, opacity, stroke);
+
+ if (strokeDasharray != null) {
+ int length = strokeDasharray.length;
+ float[] intervals = new float[length];
+ for (int i = 0; i < length; i++) {
+ intervals[i] = (float) relativeOnOther(strokeDasharray[i]);
+ }
+ paint.setPathEffect(new DashPathEffect(intervals, strokeDashoffset));
+ }
+
+ return true;
+ }
+
+ private void setupPaint(Paint paint, float opacity, ReadableArray colors) {
+ int colorType = colors.getInt(0);
+ switch (colorType) {
+ case 0:
+ if (colors.size() == 2) {
+ int color;
+ if (colors.getType(1) == ReadableType.Map) {
+ color = ColorPropConverter.getColor(colors.getMap(1), getContext());
+ } else {
+ color = colors.getInt(1);
+ }
+ int alpha = color >>> 24;
+ int combined = Math.round((float) alpha * opacity);
+ paint.setColor(combined << 24 | (color & 0x00ffffff));
+ } else {
+ // solid color
+ paint.setARGB(
+ (int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255),
+ (int) (colors.getDouble(1) * 255),
+ (int) (colors.getDouble(2) * 255),
+ (int) (colors.getDouble(3) * 255));
+ }
+ break;
+ case 1:
+ {
+ Brush brush = getSvgView().getDefinedBrush(colors.getString(1));
+ if (brush != null) {
+ brush.setupPaint(paint, mBox, mScale, opacity);
+ }
+ break;
+ }
+ case 2:
+ {
+ int brush = getSvgView().mTintColor;
+ paint.setColor(brush);
+ break;
+ }
+ case 3:
+ {
+ if (contextElement != null && contextElement.fill != null) {
+ setupPaint(paint, opacity, contextElement.fill);
+ }
+ break;
+ }
+ case 4:
+ {
+ if (contextElement != null && contextElement.stroke != null) {
+ setupPaint(paint, opacity, contextElement.stroke);
+ }
+ break;
+ }
+ }
+ }
+
+ abstract Path getPath(Canvas canvas, Paint paint);
+
+ @Override
+ int hitTest(final float[] src) {
+ if (mPath == null || !mInvertible || !mTransformInvertible) {
+ return -1;
+ }
+
+ if (mPointerEvents == PointerEvents.NONE) {
+ return -1;
+ }
+
+ float[] dst = new float[2];
+ mInvMatrix.mapPoints(dst, src);
+ mInvTransform.mapPoints(dst);
+ int x = Math.round(dst[0]);
+ int y = Math.round(dst[1]);
+
+ initBounds();
+
+ if ((mRegion == null || !mRegion.contains(x, y))
+ && (mStrokeRegion == null
+ || !mStrokeRegion.contains(x, y)
+ && (mMarkerRegion == null || !mMarkerRegion.contains(x, y)))) {
+ return -1;
+ }
+
+ Path clipPath = getClipPath();
+ if (clipPath != null) {
+ if (!mClipRegion.contains(x, y)) {
+ return -1;
+ }
+ }
+
+ return getId();
+ }
+
+ void initBounds() {
+ if (mRegion == null && mFillPath != null) {
+ mFillBounds = new RectF();
+ mFillPath.computeBounds(mFillBounds, true);
+ mRegion = getRegion(mFillPath, mFillBounds);
+ }
+ if (mRegion == null && mPath != null) {
+ mFillBounds = new RectF();
+ mPath.computeBounds(mFillBounds, true);
+ mRegion = getRegion(mPath, mFillBounds);
+ }
+ if (mStrokeRegion == null && mStrokePath != null) {
+ mStrokeBounds = new RectF();
+ mStrokePath.computeBounds(mStrokeBounds, true);
+ mStrokeRegion = getRegion(mStrokePath, mStrokeBounds);
+ }
+ if (mMarkerRegion == null && mMarkerPath != null) {
+ mMarkerBounds = new RectF();
+ mMarkerPath.computeBounds(mMarkerBounds, true);
+ mMarkerRegion = getRegion(mMarkerPath, mMarkerBounds);
+ }
+ Path clipPath = getClipPath();
+ if (clipPath != null) {
+ if (mClipRegionPath != clipPath) {
+ mClipRegionPath = clipPath;
+ mClipBounds = new RectF();
+ clipPath.computeBounds(mClipBounds, true);
+ mClipRegion = getRegion(clipPath, mClipBounds);
+ }
+ }
+ }
+
+ Region getRegion(Path path, RectF rectF) {
+ Region region = new Region();
+ region.setPath(
+ path,
+ new Region(
+ (int) Math.floor(rectF.left),
+ (int) Math.floor(rectF.top),
+ (int) Math.ceil(rectF.right),
+ (int) Math.ceil(rectF.bottom)));
+
+ return region;
+ }
+
+ private ArrayList getAttributeList() {
+ return mAttributeList;
+ }
+
+ void mergeProperties(RenderableView target) {
+ ArrayList targetAttributeList = target.getAttributeList();
+
+ if (targetAttributeList == null || targetAttributeList.size() == 0) {
+ return;
+ }
+
+ mOriginProperties = new ArrayList<>();
+ mAttributeList = mPropList == null ? new ArrayList() : new ArrayList<>(mPropList);
+
+ for (int i = 0, size = targetAttributeList.size(); i < size; i++) {
+ try {
+ String fieldName = targetAttributeList.get(i);
+ Field field = getClass().getField(fieldName);
+ Object value = field.get(target);
+ mOriginProperties.add(field.get(this));
+
+ if (!hasOwnProperty(fieldName)) {
+ mAttributeList.add(fieldName);
+ field.set(this, value);
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ mLastMergedList = targetAttributeList;
+ }
+
+ void resetProperties() {
+ if (mLastMergedList != null && mOriginProperties != null) {
+ try {
+ for (int i = mLastMergedList.size() - 1; i >= 0; i--) {
+ Field field = getClass().getField(mLastMergedList.get(i));
+ field.set(this, mOriginProperties.get(i));
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+
+ mLastMergedList = null;
+ mOriginProperties = null;
+ mAttributeList = mPropList;
+ }
+ }
+
+ private boolean hasOwnProperty(String propName) {
+ return mAttributeList != null && mAttributeList.contains(propName);
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RenderableViewManager.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RenderableViewManager.java
new file mode 100644
index 0000000000000..72180eebb2f89
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/RenderableViewManager.java
@@ -0,0 +1,2184 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import static com.facebook.react.uimanager.MatrixMathHelper.determinant;
+import static com.facebook.react.uimanager.MatrixMathHelper.inverse;
+import static com.facebook.react.uimanager.MatrixMathHelper.multiplyVectorByMatrix;
+import static com.facebook.react.uimanager.MatrixMathHelper.roundTo3Places;
+import static com.facebook.react.uimanager.MatrixMathHelper.transpose;
+import static com.facebook.react.uimanager.MatrixMathHelper.v3Combine;
+import static com.facebook.react.uimanager.MatrixMathHelper.v3Cross;
+import static com.facebook.react.uimanager.MatrixMathHelper.v3Dot;
+import static com.facebook.react.uimanager.MatrixMathHelper.v3Length;
+import static com.facebook.react.uimanager.MatrixMathHelper.v3Normalize;
+import static com.facebook.react.uimanager.ViewProps.ALIGN_CONTENT;
+import static com.facebook.react.uimanager.ViewProps.ALIGN_ITEMS;
+import static com.facebook.react.uimanager.ViewProps.ALIGN_SELF;
+import static com.facebook.react.uimanager.ViewProps.BORDER_BOTTOM_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.BORDER_END_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.BORDER_LEFT_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.BORDER_RIGHT_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.BORDER_START_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.BORDER_TOP_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.BORDER_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.BOTTOM;
+import static com.facebook.react.uimanager.ViewProps.COLLAPSABLE;
+import static com.facebook.react.uimanager.ViewProps.DISPLAY;
+import static com.facebook.react.uimanager.ViewProps.END;
+import static com.facebook.react.uimanager.ViewProps.FLEX;
+import static com.facebook.react.uimanager.ViewProps.FLEX_BASIS;
+import static com.facebook.react.uimanager.ViewProps.FLEX_DIRECTION;
+import static com.facebook.react.uimanager.ViewProps.FLEX_GROW;
+import static com.facebook.react.uimanager.ViewProps.FLEX_SHRINK;
+import static com.facebook.react.uimanager.ViewProps.FLEX_WRAP;
+import static com.facebook.react.uimanager.ViewProps.HEIGHT;
+import static com.facebook.react.uimanager.ViewProps.JUSTIFY_CONTENT;
+import static com.facebook.react.uimanager.ViewProps.LEFT;
+import static com.facebook.react.uimanager.ViewProps.MARGIN;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_BOTTOM;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_END;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_HORIZONTAL;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_LEFT;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_RIGHT;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_START;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_TOP;
+import static com.facebook.react.uimanager.ViewProps.MARGIN_VERTICAL;
+import static com.facebook.react.uimanager.ViewProps.MAX_HEIGHT;
+import static com.facebook.react.uimanager.ViewProps.MAX_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.MIN_HEIGHT;
+import static com.facebook.react.uimanager.ViewProps.MIN_WIDTH;
+import static com.facebook.react.uimanager.ViewProps.OVERFLOW;
+import static com.facebook.react.uimanager.ViewProps.PADDING;
+import static com.facebook.react.uimanager.ViewProps.PADDING_BOTTOM;
+import static com.facebook.react.uimanager.ViewProps.PADDING_END;
+import static com.facebook.react.uimanager.ViewProps.PADDING_HORIZONTAL;
+import static com.facebook.react.uimanager.ViewProps.PADDING_LEFT;
+import static com.facebook.react.uimanager.ViewProps.PADDING_RIGHT;
+import static com.facebook.react.uimanager.ViewProps.PADDING_START;
+import static com.facebook.react.uimanager.ViewProps.PADDING_TOP;
+import static com.facebook.react.uimanager.ViewProps.PADDING_VERTICAL;
+import static com.facebook.react.uimanager.ViewProps.POSITION;
+import static com.facebook.react.uimanager.ViewProps.RIGHT;
+import static com.facebook.react.uimanager.ViewProps.START;
+import static com.facebook.react.uimanager.ViewProps.TOP;
+import static com.facebook.react.uimanager.ViewProps.WIDTH;
+import static com.horcrux.svg.RenderableView.CAP_ROUND;
+import static com.horcrux.svg.RenderableView.FILL_RULE_NONZERO;
+import static com.horcrux.svg.RenderableView.JOIN_ROUND;
+
+import android.graphics.Matrix;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.JavaOnlyMap;
+import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.ReadableType;
+import com.facebook.react.uimanager.DisplayMetricsHolder;
+import com.facebook.react.uimanager.LayoutShadowNode;
+import com.facebook.react.uimanager.MatrixMathHelper;
+import com.facebook.react.uimanager.PixelUtil;
+import com.facebook.react.uimanager.PointerEvents;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.TransformHelper;
+import com.facebook.react.uimanager.ViewGroupManager;
+import com.facebook.react.uimanager.ViewManagerDelegate;
+import com.facebook.react.uimanager.ViewProps;
+import com.facebook.react.uimanager.annotations.ReactProp;
+import com.facebook.react.uimanager.annotations.ReactPropGroup;
+import com.facebook.react.viewmanagers.RNSVGCircleManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGCircleManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGClipPathManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGClipPathManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGDefsManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGDefsManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGEllipseManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGEllipseManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGForeignObjectManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGGroupManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGGroupManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGImageManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGImageManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGLineManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGLineManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGLinearGradientManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGLinearGradientManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGMarkerManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGMarkerManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGMaskManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGMaskManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGPathManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGPathManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGPatternManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGPatternManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGRadialGradientManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGRadialGradientManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGRectManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGRectManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGSymbolManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGSymbolManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGTSpanManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGTSpanManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGTextManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGTextManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGTextPathManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGTextPathManagerInterface;
+import com.facebook.react.viewmanagers.RNSVGUseManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGUseManagerInterface;
+import java.util.Locale;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/** ViewManager for DefinitionView RNSVG views */
+class VirtualViewManager extends ViewGroupManager {
+
+ protected final SVGClass svgClass;
+ protected final String mClassName;
+
+ protected VirtualViewManager(SVGClass svgclass) {
+ svgClass = svgclass;
+ mClassName = svgclass.toString();
+ }
+
+ static class RenderableShadowNode extends LayoutShadowNode {
+
+ @SuppressWarnings({"unused", "EmptyMethod"})
+ @ReactPropGroup(
+ names = {
+ ALIGN_SELF,
+ ALIGN_ITEMS,
+ COLLAPSABLE,
+ FLEX,
+ FLEX_BASIS,
+ FLEX_DIRECTION,
+ FLEX_GROW,
+ FLEX_SHRINK,
+ FLEX_WRAP,
+ JUSTIFY_CONTENT,
+ OVERFLOW,
+ ALIGN_CONTENT,
+ DISPLAY,
+
+ /* position */
+ POSITION,
+ RIGHT,
+ TOP,
+ BOTTOM,
+ LEFT,
+ START,
+ END,
+
+ /* dimensions */
+ WIDTH,
+ HEIGHT,
+ MIN_WIDTH,
+ MAX_WIDTH,
+ MIN_HEIGHT,
+ MAX_HEIGHT,
+
+ /* margins */
+ MARGIN,
+ MARGIN_VERTICAL,
+ MARGIN_HORIZONTAL,
+ MARGIN_LEFT,
+ MARGIN_RIGHT,
+ MARGIN_TOP,
+ MARGIN_BOTTOM,
+ MARGIN_START,
+ MARGIN_END,
+
+ /* paddings */
+ PADDING,
+ PADDING_VERTICAL,
+ PADDING_HORIZONTAL,
+ PADDING_LEFT,
+ PADDING_RIGHT,
+ PADDING_TOP,
+ PADDING_BOTTOM,
+ PADDING_START,
+ PADDING_END,
+ BORDER_WIDTH,
+ BORDER_START_WIDTH,
+ BORDER_END_WIDTH,
+ BORDER_TOP_WIDTH,
+ BORDER_BOTTOM_WIDTH,
+ BORDER_LEFT_WIDTH,
+ BORDER_RIGHT_WIDTH,
+ })
+ public void ignoreLayoutProps(int index, Dynamic value) {}
+ }
+
+ @Override
+ public LayoutShadowNode createShadowNodeInstance() {
+ return new RenderableShadowNode();
+ }
+
+ @Override
+ public Class extends LayoutShadowNode> getShadowNodeClass() {
+ return RenderableShadowNode.class;
+ }
+
+ static class MatrixDecompositionContext extends MatrixMathHelper.MatrixDecompositionContext {
+ final double[] perspective = new double[4];
+ final double[] scale = new double[3];
+ final double[] skew = new double[3];
+ final double[] translation = new double[3];
+ final double[] rotationDegrees = new double[3];
+ }
+
+ private static final MatrixDecompositionContext sMatrixDecompositionContext =
+ new MatrixDecompositionContext();
+ private static final double[] sTransformDecompositionArray = new double[16];
+
+ private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2;
+ private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = 5;
+
+ private static final double EPSILON = .00001d;
+
+ private static boolean isZero(double d) {
+ return !Double.isNaN(d) && Math.abs(d) < EPSILON;
+ }
+
+ private static void decomposeMatrix() {
+
+ // output values
+ final double[] perspective = sMatrixDecompositionContext.perspective;
+ final double[] scale = sMatrixDecompositionContext.scale;
+ final double[] skew = sMatrixDecompositionContext.skew;
+ final double[] translation = sMatrixDecompositionContext.translation;
+ final double[] rotationDegrees = sMatrixDecompositionContext.rotationDegrees;
+
+ // create normalized, 2d array matrix
+ // and normalized 1d array perspectiveMatrix with redefined 4th column
+ if (isZero(sTransformDecompositionArray[15])) {
+ return;
+ }
+ double[][] matrix = new double[4][4];
+ double[] perspectiveMatrix = new double[16];
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ double value = sTransformDecompositionArray[(i * 4) + j] / sTransformDecompositionArray[15];
+ matrix[i][j] = value;
+ perspectiveMatrix[(i * 4) + j] = j == 3 ? 0 : value;
+ }
+ }
+ perspectiveMatrix[15] = 1;
+
+ // test for singularity of upper 3x3 part of the perspective matrix
+ if (isZero(determinant(perspectiveMatrix))) {
+ return;
+ }
+
+ // isolate perspective
+ if (!isZero(matrix[0][3]) || !isZero(matrix[1][3]) || !isZero(matrix[2][3])) {
+ // rightHandSide is the right hand side of the equation.
+ // rightHandSide is a vector, or point in 3d space relative to the origin.
+ double[] rightHandSide = {matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]};
+
+ // Solve the equation by inverting perspectiveMatrix and multiplying
+ // rightHandSide by the inverse.
+ double[] inversePerspectiveMatrix = inverse(perspectiveMatrix);
+ double[] transposedInversePerspectiveMatrix = transpose(inversePerspectiveMatrix);
+ multiplyVectorByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspective);
+ } else {
+ // no perspective
+ perspective[0] = perspective[1] = perspective[2] = 0d;
+ perspective[3] = 1d;
+ }
+
+ // translation is simple
+ System.arraycopy(matrix[3], 0, translation, 0, 3);
+
+ // Now get scale and shear.
+ // 'row' is a 3 element array of 3 component vectors
+ double[][] row = new double[3][3];
+ for (int i = 0; i < 3; i++) {
+ row[i][0] = matrix[i][0];
+ row[i][1] = matrix[i][1];
+ row[i][2] = matrix[i][2];
+ }
+
+ // Compute X scale factor and normalize first row.
+ scale[0] = v3Length(row[0]);
+ row[0] = v3Normalize(row[0], scale[0]);
+
+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
+ skew[0] = v3Dot(row[0], row[1]);
+ row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
+
+ // Compute XY shear factor and make 2nd row orthogonal to 1st.
+ skew[0] = v3Dot(row[0], row[1]);
+ row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
+
+ // Now, compute Y scale and normalize 2nd row.
+ scale[1] = v3Length(row[1]);
+ row[1] = v3Normalize(row[1], scale[1]);
+ skew[0] /= scale[1];
+
+ // Compute XZ and YZ shears, orthogonalize 3rd row
+ skew[1] = v3Dot(row[0], row[2]);
+ row[2] = v3Combine(row[2], row[0], 1.0, -skew[1]);
+ skew[2] = v3Dot(row[1], row[2]);
+ row[2] = v3Combine(row[2], row[1], 1.0, -skew[2]);
+
+ // Next, get Z scale and normalize 3rd row.
+ scale[2] = v3Length(row[2]);
+ row[2] = v3Normalize(row[2], scale[2]);
+ skew[1] /= scale[2];
+ skew[2] /= scale[2];
+
+ // At this point, the matrix (in rows) is orthonormal.
+ // Check for a coordinate system flip. If the determinant
+ // is -1, then negate the matrix and the scaling factors.
+ double[] pdum3 = v3Cross(row[1], row[2]);
+ if (v3Dot(row[0], pdum3) < 0) {
+ for (int i = 0; i < 3; i++) {
+ scale[i] *= -1;
+ row[i][0] *= -1;
+ row[i][1] *= -1;
+ row[i][2] *= -1;
+ }
+ }
+
+ // Now, get the rotations out
+ // Based on: http://nghiaho.com/?page_id=846
+ double conv = 180 / Math.PI;
+ rotationDegrees[0] = roundTo3Places(-Math.atan2(row[2][1], row[2][2]) * conv);
+ rotationDegrees[1] =
+ roundTo3Places(
+ -Math.atan2(-row[2][0], Math.sqrt(row[2][1] * row[2][1] + row[2][2] * row[2][2]))
+ * conv);
+ rotationDegrees[2] = roundTo3Places(-Math.atan2(row[1][0], row[0][0]) * conv);
+ }
+
+ private static void setTransformProperty(View view, ReadableArray transforms) {
+ TransformHelper.processTransform(transforms, sTransformDecompositionArray);
+ decomposeMatrix();
+ view.setTranslationX(
+ PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[0]));
+ view.setTranslationY(
+ PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[1]));
+ view.setRotation((float) sMatrixDecompositionContext.rotationDegrees[2]);
+ view.setRotationX((float) sMatrixDecompositionContext.rotationDegrees[0]);
+ view.setRotationY((float) sMatrixDecompositionContext.rotationDegrees[1]);
+ view.setScaleX((float) sMatrixDecompositionContext.scale[0]);
+ view.setScaleY((float) sMatrixDecompositionContext.scale[1]);
+
+ double[] perspectiveArray = sMatrixDecompositionContext.perspective;
+
+ if (perspectiveArray.length > PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) {
+ float invertedCameraDistance =
+ (float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX];
+ if (invertedCameraDistance == 0) {
+ // Default camera distance, before scale multiplier (1280)
+ invertedCameraDistance = 0.00078125f;
+ }
+ float cameraDistance = -1 / invertedCameraDistance;
+ float scale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
+
+ // The following converts the matrix's perspective to a camera distance
+ // such that the camera perspective looks the same on Android and iOS.
+ // The native Android implementation removed the screen density from the
+ // calculation, so squaring and a normalization value of
+ // sqrt(5) produces an exact replica with iOS.
+ // For more information, see https://github.com/facebook/react-native/pull/18302
+ float normalizedCameraDistance =
+ scale * scale * cameraDistance * CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER;
+ view.setCameraDistance(normalizedCameraDistance);
+ }
+ }
+
+ private static void resetTransformProperty(View view) {
+ view.setTranslationX(0);
+ view.setTranslationY(0);
+ view.setRotation(0);
+ view.setRotationX(0);
+ view.setRotationY(0);
+ view.setScaleX(1);
+ view.setScaleY(1);
+ view.setCameraDistance(0);
+ }
+
+ @Nonnull
+ public String getName() {
+ return mClassName;
+ }
+
+ @ReactProp(name = "mask")
+ public void setMask(V node, String mask) {
+ node.setMask(mask);
+ }
+
+ @ReactProp(name = "markerStart")
+ public void setMarkerStart(V node, String markerStart) {
+ node.setMarkerStart(markerStart);
+ }
+
+ @ReactProp(name = "markerMid")
+ public void setMarkerMid(V node, String markerMid) {
+ node.setMarkerMid(markerMid);
+ }
+
+ @ReactProp(name = "markerEnd")
+ public void setMarkerEnd(V node, String markerEnd) {
+ node.setMarkerEnd(markerEnd);
+ }
+
+ @ReactProp(name = "clipPath")
+ public void setClipPath(V node, String clipPath) {
+ node.setClipPath(clipPath);
+ }
+
+ @ReactProp(name = "clipRule")
+ public void setClipRule(V node, int clipRule) {
+ node.setClipRule(clipRule);
+ }
+
+ @ReactProp(name = "opacity", defaultFloat = 1f)
+ public void setOpacity(@Nonnull V node, float opacity) {
+ node.setOpacity(opacity);
+ }
+
+ @ReactProp(name = "responsible")
+ public void setResponsible(V node, boolean responsible) {
+ node.setResponsible(responsible);
+ }
+
+ @ReactProp(name = ViewProps.POINTER_EVENTS)
+ public void setPointerEvents(V view, @Nullable String pointerEventsStr) {
+ if (pointerEventsStr == null) {
+ view.setPointerEvents(PointerEvents.AUTO);
+ } else {
+ PointerEvents pointerEvents =
+ PointerEvents.valueOf(pointerEventsStr.toUpperCase(Locale.US).replace("-", "_"));
+ view.setPointerEvents(pointerEvents);
+ }
+ }
+
+ @ReactProp(name = "onLayout")
+ public void setOnLayout(V node, boolean onLayout) {
+ node.setOnLayout(onLayout);
+ }
+
+ @ReactProp(name = "name")
+ public void setName(V node, String name) {
+ node.setName(name);
+ }
+
+ @ReactProp(name = "display")
+ public void setDisplay(V node, String display) {
+ node.setDisplay(display);
+ }
+
+ @ReactProp(name = "matrix")
+ public void setMatrix(V node, Dynamic matrixArray) {
+ node.setMatrix(matrixArray);
+ }
+
+ public void setMatrix(V view, @Nullable ReadableArray value) {
+ view.setMatrix(value);
+ }
+
+ @Override
+ public void setTransform(VirtualView node, @Nullable ReadableArray matrix) {
+ if (matrix == null) {
+ resetTransformProperty(node);
+ } else {
+ setTransformProperty(node, matrix);
+ }
+
+ Matrix m = node.getMatrix();
+ node.mTransform = m;
+ node.mTransformInvertible = m.invert(node.mInvTransform);
+ }
+
+ @ReactProp(name = "transform")
+ public void setTransform(V node, Dynamic matrix) {
+ if (matrix.getType() != ReadableType.Array) {
+ return;
+ }
+ ReadableArray ma = matrix.asArray();
+ setTransform(node, ma);
+ }
+
+ private void invalidateSvgView(V node) {
+ SvgView view = node.getSvgView();
+ if (view != null) {
+ view.invalidate();
+ }
+ if (node instanceof TextView) {
+ ((TextView) node).getTextContainer().clearChildCache();
+ }
+ }
+
+ @Override
+ protected void addEventEmitters(
+ @Nonnull ThemedReactContext reactContext, @Nonnull VirtualView view) {
+ super.addEventEmitters(reactContext, view);
+ view.setOnHierarchyChangeListener(
+ new ViewGroup.OnHierarchyChangeListener() {
+ @Override
+ public void onChildViewAdded(View view, View view1) {
+ if (view instanceof VirtualView) {
+ invalidateSvgView((V) view);
+ }
+ }
+
+ @Override
+ public void onChildViewRemoved(View view, View view1) {
+ if (view instanceof VirtualView) {
+ invalidateSvgView((V) view);
+ }
+ }
+ });
+ }
+
+ /**
+ * Callback that will be triggered after all properties are updated in current update transaction
+ * (all @ReactProp handlers for properties updated in current transaction have been called). If
+ * you want to override this method you should call super.onAfterUpdateTransaction from it as the
+ * parent class of the ViewManager may rely on callback being executed.
+ */
+ @Override
+ protected void onAfterUpdateTransaction(@Nonnull VirtualView node) {
+ super.onAfterUpdateTransaction(node);
+ invalidateSvgView((V) node);
+ }
+
+ protected enum SVGClass {
+ RNSVGGroup,
+ RNSVGPath,
+ RNSVGText,
+ RNSVGTSpan,
+ RNSVGTextPath,
+ RNSVGImage,
+ RNSVGCircle,
+ RNSVGEllipse,
+ RNSVGLine,
+ RNSVGRect,
+ RNSVGClipPath,
+ RNSVGDefs,
+ RNSVGUse,
+ RNSVGSymbol,
+ RNSVGLinearGradient,
+ RNSVGRadialGradient,
+ RNSVGPattern,
+ RNSVGMask,
+ RNSVGMarker,
+ RNSVGForeignObject,
+ }
+
+ @Nonnull
+ @Override
+ protected VirtualView createViewInstance(@Nonnull ThemedReactContext reactContext) {
+ switch (svgClass) {
+ case RNSVGGroup:
+ return new GroupView(reactContext);
+ case RNSVGPath:
+ return new PathView(reactContext);
+ case RNSVGCircle:
+ return new CircleView(reactContext);
+ case RNSVGEllipse:
+ return new EllipseView(reactContext);
+ case RNSVGLine:
+ return new LineView(reactContext);
+ case RNSVGRect:
+ return new RectView(reactContext);
+ case RNSVGText:
+ return new TextView(reactContext);
+ case RNSVGTSpan:
+ return new TSpanView(reactContext);
+ case RNSVGTextPath:
+ return new TextPathView(reactContext);
+ case RNSVGImage:
+ return new ImageView(reactContext);
+ case RNSVGClipPath:
+ return new ClipPathView(reactContext);
+ case RNSVGDefs:
+ return new DefsView(reactContext);
+ case RNSVGUse:
+ return new UseView(reactContext);
+ case RNSVGSymbol:
+ return new SymbolView(reactContext);
+ case RNSVGLinearGradient:
+ return new LinearGradientView(reactContext);
+ case RNSVGRadialGradient:
+ return new RadialGradientView(reactContext);
+ case RNSVGPattern:
+ return new PatternView(reactContext);
+ case RNSVGMask:
+ return new MaskView(reactContext);
+ case RNSVGMarker:
+ return new MarkerView(reactContext);
+ case RNSVGForeignObject:
+ return new ForeignObjectView(reactContext);
+ default:
+ throw new IllegalStateException("Unexpected type " + svgClass.toString());
+ }
+ }
+
+ private static final SparseArray mTagToRenderableView = new SparseArray<>();
+ private static final SparseArray mTagToRunnable = new SparseArray<>();
+
+ static void setRenderableView(int tag, RenderableView svg) {
+ mTagToRenderableView.put(tag, svg);
+ Runnable task = mTagToRunnable.get(tag);
+ if (task != null) {
+ task.run();
+ mTagToRunnable.delete(tag);
+ }
+ }
+
+ static void runWhenViewIsAvailable(int tag, Runnable task) {
+ mTagToRunnable.put(tag, task);
+ }
+
+ static @Nullable RenderableView getRenderableViewByTag(int tag) {
+ return mTagToRenderableView.get(tag);
+ }
+
+ @Override
+ public void onDropViewInstance(@Nonnull VirtualView view) {
+ super.onDropViewInstance(view);
+ mTagToRenderableView.remove(view.getId());
+ }
+}
+/** ViewManager for Renderable RNSVG views */
+class RenderableViewManager extends VirtualViewManager {
+
+ RenderableViewManager(SVGClass svgclass) {
+ super(svgclass);
+ }
+
+ static class GroupViewManagerAbstract extends RenderableViewManager {
+ GroupViewManagerAbstract(SVGClass svgClass) {
+ super(svgClass);
+ }
+
+ @ReactProp(name = "font")
+ public void setFont(U node, @Nullable ReadableMap font) {
+ node.setFont(font);
+ }
+
+ @ReactProp(name = "fontSize")
+ public void setFontSize(U node, Dynamic fontSize) {
+ JavaOnlyMap map = new JavaOnlyMap();
+ switch (fontSize.getType()) {
+ case Number:
+ map.putDouble("fontSize", fontSize.asDouble());
+ break;
+ case String:
+ map.putString("fontSize", fontSize.asString());
+ break;
+ default:
+ return;
+ }
+ node.setFont(map);
+ }
+
+ public void setFontSize(U view, @Nullable String value) {
+ JavaOnlyMap map = new JavaOnlyMap();
+ map.putString("fontSize", value);
+ view.setFont(map);
+ }
+
+ public void setFontSize(U view, @Nullable Double value) {
+ JavaOnlyMap map = new JavaOnlyMap();
+ map.putDouble("fontSize", value);
+ view.setFont(map);
+ }
+
+ @ReactProp(name = "fontWeight")
+ public void setFontWeight(U node, Dynamic fontWeight) {
+ JavaOnlyMap map = new JavaOnlyMap();
+ switch (fontWeight.getType()) {
+ case Number:
+ map.putDouble("fontWeight", fontWeight.asDouble());
+ break;
+ case String:
+ map.putString("fontWeight", fontWeight.asString());
+ break;
+ default:
+ return;
+ }
+ node.setFont(map);
+ }
+
+ public void setFontWeight(U view, @Nullable String value) {
+ JavaOnlyMap map = new JavaOnlyMap();
+ map.putString("fontWeight", value);
+ view.setFont(map);
+ }
+
+ public void setFontWeight(U view, @Nullable Double value) {
+ JavaOnlyMap map = new JavaOnlyMap();
+ map.putDouble("fontWeight", value);
+ view.setFont(map);
+ }
+ }
+
+ static class GroupViewManager extends GroupViewManagerAbstract
+ implements RNSVGGroupManagerInterface {
+ GroupViewManager() {
+ super(SVGClass.RNSVGGroup);
+ mDelegate = new RNSVGGroupManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+ }
+
+ static class PathViewManager extends RenderableViewManager
+ implements RNSVGPathManagerInterface {
+ PathViewManager() {
+ super(SVGClass.RNSVGPath);
+ mDelegate = new RNSVGPathManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "d")
+ public void setD(PathView node, String d) {
+ node.setD(d);
+ }
+ }
+
+ static class TextViewManagerAbstract extends GroupViewManagerAbstract {
+ TextViewManagerAbstract(SVGClass svgClass) {
+ super(svgClass);
+ }
+
+ @ReactProp(name = "inlineSize")
+ public void setInlineSize(K node, Dynamic inlineSize) {
+ node.setInlineSize(inlineSize);
+ }
+
+ @ReactProp(name = "textLength")
+ public void setTextLength(K node, Dynamic length) {
+ node.setTextLength(length);
+ }
+
+ @ReactProp(name = "lengthAdjust")
+ public void setLengthAdjust(K node, @Nullable String adjustment) {
+ node.setLengthAdjust(adjustment);
+ }
+
+ @ReactProp(name = "alignmentBaseline")
+ public void setMethod(K node, @Nullable String alignment) {
+ node.setMethod(alignment);
+ }
+
+ @ReactProp(name = "baselineShift")
+ public void setBaselineShift(K node, Dynamic baselineShift) {
+ node.setBaselineShift(baselineShift);
+ }
+
+ @ReactProp(name = "verticalAlign")
+ public void setVerticalAlign(K node, @Nullable String verticalAlign) {
+ node.setVerticalAlign(verticalAlign);
+ }
+
+ @ReactProp(name = "rotate")
+ public void setRotate(K node, Dynamic rotate) {
+ node.setRotate(rotate);
+ }
+
+ @ReactProp(name = "dx")
+ public void setDeltaX(K node, Dynamic deltaX) {
+ node.setDeltaX(deltaX);
+ }
+
+ @ReactProp(name = "dy")
+ public void setDeltaY(K node, Dynamic deltaY) {
+ node.setDeltaY(deltaY);
+ }
+
+ @ReactProp(name = "x")
+ public void setX(K node, Dynamic positionX) {
+ node.setPositionX(positionX);
+ }
+
+ @ReactProp(name = "y")
+ public void setY(K node, Dynamic positionY) {
+ node.setPositionY(positionY);
+ }
+
+ @ReactProp(name = "font")
+ public void setFont(K node, @Nullable ReadableMap font) {
+ node.setFont(font);
+ }
+
+ public void setAlignmentBaseline(K view, @Nullable String value) {
+ view.setMethod(value);
+ }
+
+ public void setTextAnchor(K view, @Nullable String value) {
+ // TODO: is it available on Android?
+ }
+
+ public void setDx(K view, @Nullable ReadableArray value) {
+ view.setDeltaX(value);
+ }
+
+ public void setDy(K view, @Nullable ReadableArray value) {
+ view.setDeltaY(value);
+ }
+
+ public void setPositionX(K view, @Nullable ReadableArray value) {
+ view.setPositionX(value);
+ }
+
+ public void setPositionY(K view, @Nullable ReadableArray value) {
+ view.setPositionY(value);
+ }
+
+ public void setX(K view, @Nullable ReadableArray value) {
+ view.setPositionX(value);
+ }
+
+ public void setY(K view, @Nullable ReadableArray value) {
+ view.setPositionY(value);
+ }
+
+ public void setRotate(K view, @Nullable ReadableArray value) {
+ view.setRotate(value);
+ }
+
+ public void setInlineSize(K view, @Nullable String value) {
+ view.setInlineSize(value);
+ }
+
+ public void setTextLength(K view, @Nullable String value) {
+ view.setTextLength(value);
+ }
+
+ public void setBaselineShift(K view, @Nullable String value) {
+ view.setBaselineShift(value);
+ }
+
+ public void setInlineSize(K view, @Nullable Double value) {
+ view.setInlineSize(value);
+ }
+
+ public void setTextLength(K view, @Nullable Double value) {
+ view.setTextLength(value);
+ }
+
+ public void setBaselineShift(K view, @Nullable Double value) {
+ view.setBaselineShift(value);
+ }
+ }
+
+ static class TextViewManager extends TextViewManagerAbstract
+ implements RNSVGTextManagerInterface {
+ TextViewManager() {
+ super(SVGClass.RNSVGText);
+ mDelegate = new RNSVGTextManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ TextViewManager(SVGClass svgClass) {
+ super(svgClass);
+ mDelegate = new RNSVGTextManagerDelegate(this);
+ }
+ }
+
+ static class TSpanViewManager extends TextViewManagerAbstract
+ implements RNSVGTSpanManagerInterface {
+ TSpanViewManager() {
+ super(SVGClass.RNSVGTSpan);
+ mDelegate = new RNSVGTSpanManagerDelegate(this);
+ }
+
+ TSpanViewManager(SVGClass svgClass) {
+ super(svgClass);
+ mDelegate = new RNSVGTSpanManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "content")
+ public void setContent(TSpanView node, @Nullable String content) {
+ node.setContent(content);
+ }
+ }
+
+ static class TextPathViewManager extends TextViewManagerAbstract
+ implements RNSVGTextPathManagerInterface {
+ TextPathViewManager() {
+ super(SVGClass.RNSVGTextPath);
+ mDelegate = new RNSVGTextPathManagerDelegate(this);
+ }
+
+ TextPathViewManager(SVGClass svgClass) {
+ super(svgClass);
+ mDelegate = new RNSVGTextPathManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "href")
+ public void setHref(TextPathView node, String href) {
+ node.setHref(href);
+ }
+
+ @ReactProp(name = "startOffset")
+ public void setStartOffset(TextPathView node, Dynamic startOffset) {
+ node.setStartOffset(startOffset);
+ }
+
+ @ReactProp(name = "method")
+ public void setMethod(TextPathView node, @Nullable String method) {
+ node.setMethod(method);
+ }
+
+ @Override
+ public void setMidLine(TextPathView view, @Nullable String value) {
+ view.setSharp(value);
+ }
+
+ @ReactProp(name = "spacing")
+ public void setSpacing(TextPathView node, @Nullable String spacing) {
+ node.setSpacing(spacing);
+ }
+
+ @Override
+ public void setStartOffset(TextPathView view, @Nullable String value) {
+ view.setStartOffset(value);
+ }
+
+ public void setStartOffset(TextPathView view, @Nullable Double value) {
+ view.setStartOffset(value);
+ }
+
+ @ReactProp(name = "side")
+ public void setSide(TextPathView node, @Nullable String side) {
+ node.setSide(side);
+ }
+
+ @ReactProp(name = "midLine")
+ public void setSharp(TextPathView node, @Nullable String midLine) {
+ node.setSharp(midLine);
+ }
+ }
+
+ static class ImageViewManager extends RenderableViewManager
+ implements RNSVGImageManagerInterface {
+ ImageViewManager() {
+ super(SVGClass.RNSVGImage);
+ mDelegate = new RNSVGImageManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "x")
+ public void setX(ImageView node, Dynamic x) {
+ node.setX(x);
+ }
+
+ @ReactProp(name = "y")
+ public void setY(ImageView node, Dynamic y) {
+ node.setY(y);
+ }
+
+ @ReactProp(name = "width")
+ public void setWidth(ImageView node, Dynamic width) {
+ node.setWidth(width);
+ }
+
+ @ReactProp(name = "height")
+ public void setHeight(ImageView node, Dynamic height) {
+ node.setHeight(height);
+ }
+
+ @Override
+ public void setX(ImageView view, @Nullable String value) {
+ view.setX(value);
+ }
+
+ @Override
+ public void setY(ImageView view, @Nullable String value) {
+ view.setY(value);
+ }
+
+ @Override
+ public void setWidth(ImageView view, @Nullable String value) {
+ view.setWidth(value);
+ }
+
+ @Override
+ public void setHeight(ImageView view, @Nullable String value) {
+ view.setHeight(value);
+ }
+
+ public void setX(ImageView view, @Nullable Double value) {
+ view.setX(value);
+ }
+
+ public void setY(ImageView view, @Nullable Double value) {
+ view.setY(value);
+ }
+
+ public void setWidth(ImageView view, @Nullable Double value) {
+ view.setWidth(value);
+ }
+
+ public void setHeight(ImageView view, @Nullable Double value) {
+ view.setHeight(value);
+ }
+
+ @ReactProp(name = "src", customType = "ImageSource")
+ public void setSrc(ImageView node, @Nullable ReadableMap src) {
+ node.setSrc(src);
+ }
+
+ @ReactProp(name = "align")
+ public void setAlign(ImageView node, String align) {
+ node.setAlign(align);
+ }
+
+ @ReactProp(name = "meetOrSlice")
+ public void setMeetOrSlice(ImageView node, int meetOrSlice) {
+ node.setMeetOrSlice(meetOrSlice);
+ }
+ }
+
+ static class CircleViewManager extends RenderableViewManager
+ implements RNSVGCircleManagerInterface {
+ CircleViewManager() {
+ super(SVGClass.RNSVGCircle);
+ mDelegate = new RNSVGCircleManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "cx")
+ public void setCx(CircleView node, Dynamic cx) {
+ node.setCx(cx);
+ }
+
+ @ReactProp(name = "cy")
+ public void setCy(CircleView node, Dynamic cy) {
+ node.setCy(cy);
+ }
+
+ @ReactProp(name = "r")
+ public void setR(CircleView node, Dynamic r) {
+ node.setR(r);
+ }
+
+ @Override
+ public void setCx(CircleView view, String value) {
+ view.setCx(value);
+ }
+
+ public void setCx(CircleView view, Double value) {
+ view.setCx(value);
+ }
+
+ @Override
+ public void setCy(CircleView view, String value) {
+ view.setCy(value);
+ }
+
+ public void setCy(CircleView view, Double value) {
+ view.setCy(value);
+ }
+
+ @Override
+ public void setR(CircleView view, String value) {
+ view.setR(value);
+ }
+
+ public void setR(CircleView view, Double value) {
+ view.setR(value);
+ }
+ }
+
+ static class EllipseViewManager extends RenderableViewManager
+ implements RNSVGEllipseManagerInterface {
+ EllipseViewManager() {
+ super(SVGClass.RNSVGEllipse);
+ mDelegate = new RNSVGEllipseManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "cx")
+ public void setCx(EllipseView node, Dynamic cx) {
+ node.setCx(cx);
+ }
+
+ @ReactProp(name = "cy")
+ public void setCy(EllipseView node, Dynamic cy) {
+ node.setCy(cy);
+ }
+
+ @ReactProp(name = "rx")
+ public void setRx(EllipseView node, Dynamic rx) {
+ node.setRx(rx);
+ }
+
+ @ReactProp(name = "ry")
+ public void setRy(EllipseView node, Dynamic ry) {
+ node.setRy(ry);
+ }
+
+ @Override
+ public void setCx(EllipseView view, @Nullable String value) {
+ view.setCx(value);
+ }
+
+ @Override
+ public void setCy(EllipseView view, @Nullable String value) {
+ view.setCy(value);
+ }
+
+ @Override
+ public void setRx(EllipseView view, @Nullable String value) {
+ view.setRx(value);
+ }
+
+ @Override
+ public void setRy(EllipseView view, @Nullable String value) {
+ view.setRy(value);
+ }
+
+ public void setCx(EllipseView view, @Nullable Double value) {
+ view.setCx(value);
+ }
+
+ public void setCy(EllipseView view, @Nullable Double value) {
+ view.setCy(value);
+ }
+
+ public void setRx(EllipseView view, @Nullable Double value) {
+ view.setRx(value);
+ }
+
+ public void setRy(EllipseView view, @Nullable Double value) {
+ view.setRy(value);
+ }
+ }
+
+ static class LineViewManager extends RenderableViewManager
+ implements RNSVGLineManagerInterface {
+
+ LineViewManager() {
+ super(SVGClass.RNSVGLine);
+ mDelegate = new RNSVGLineManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "x1")
+ public void setX1(LineView node, Dynamic x1) {
+ node.setX1(x1);
+ }
+
+ @ReactProp(name = "y1")
+ public void setY1(LineView node, Dynamic y1) {
+ node.setY1(y1);
+ }
+
+ @ReactProp(name = "x2")
+ public void setX2(LineView node, Dynamic x2) {
+ node.setX2(x2);
+ }
+
+ @ReactProp(name = "y2")
+ public void setY2(LineView node, Dynamic y2) {
+ node.setY2(y2);
+ }
+
+ @Override
+ public void setX1(LineView view, @Nullable String value) {
+ view.setX1(value);
+ }
+
+ @Override
+ public void setY1(LineView view, @Nullable String value) {
+ view.setY1(value);
+ }
+
+ @Override
+ public void setX2(LineView view, @Nullable String value) {
+ view.setX2(value);
+ }
+
+ @Override
+ public void setY2(LineView view, @Nullable String value) {
+ view.setY2(value);
+ }
+
+ public void setX1(LineView view, @Nullable Double value) {
+ view.setX1(value);
+ }
+
+ public void setY1(LineView view, @Nullable Double value) {
+ view.setY1(value);
+ }
+
+ public void setX2(LineView view, @Nullable Double value) {
+ view.setX2(value);
+ }
+
+ public void setY2(LineView view, @Nullable Double value) {
+ view.setY2(value);
+ }
+ }
+
+ static class RectViewManager extends RenderableViewManager
+ implements RNSVGRectManagerInterface {
+
+ RectViewManager() {
+ super(SVGClass.RNSVGRect);
+ mDelegate = new RNSVGRectManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "x")
+ public void setX(RectView node, Dynamic x) {
+ node.setX(x);
+ }
+
+ @ReactProp(name = "y")
+ public void setY(RectView node, Dynamic y) {
+ node.setY(y);
+ }
+
+ @ReactProp(name = "width")
+ public void setWidth(RectView node, Dynamic width) {
+ node.setWidth(width);
+ }
+
+ @ReactProp(name = "height")
+ public void setHeight(RectView node, Dynamic height) {
+ node.setHeight(height);
+ }
+
+ @ReactProp(name = "rx")
+ public void setRx(RectView node, Dynamic rx) {
+ node.setRx(rx);
+ }
+
+ @ReactProp(name = "ry")
+ public void setRy(RectView node, Dynamic ry) {
+ node.setRy(ry);
+ }
+
+ @Override
+ public void setX(RectView view, @Nullable String value) {
+ view.setX(value);
+ }
+
+ @Override
+ public void setY(RectView view, @Nullable String value) {
+ view.setY(value);
+ }
+
+ @Override
+ public void setHeight(RectView view, @Nullable String value) {
+ view.setHeight(value);
+ }
+
+ @Override
+ public void setWidth(RectView view, @Nullable String value) {
+ view.setWidth(value);
+ }
+
+ @Override
+ public void setRx(RectView view, @Nullable String value) {
+ view.setRx(value);
+ }
+
+ @Override
+ public void setRy(RectView view, @Nullable String value) {
+ view.setRy(value);
+ }
+
+ public void setX(RectView view, @Nullable Double value) {
+ view.setX(value);
+ }
+
+ public void setY(RectView view, @Nullable Double value) {
+ view.setY(value);
+ }
+
+ public void setHeight(RectView view, @Nullable Double value) {
+ view.setHeight(value);
+ }
+
+ public void setWidth(RectView view, @Nullable Double value) {
+ view.setWidth(value);
+ }
+
+ public void setRx(RectView view, @Nullable Double value) {
+ view.setRx(value);
+ }
+
+ public void setRy(RectView view, @Nullable Double value) {
+ view.setRy(value);
+ }
+ }
+
+ static class ClipPathViewManager extends GroupViewManagerAbstract
+ implements RNSVGClipPathManagerInterface {
+ ClipPathViewManager() {
+ super(SVGClass.RNSVGClipPath);
+ mDelegate = new RNSVGClipPathManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+ }
+
+ static class DefsViewManager extends VirtualViewManager
+ implements RNSVGDefsManagerInterface {
+
+ DefsViewManager() {
+ super(SVGClass.RNSVGDefs);
+ mDelegate = new RNSVGDefsManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+ }
+
+ static class UseViewManager extends RenderableViewManager
+ implements RNSVGUseManagerInterface {
+
+ UseViewManager() {
+ super(SVGClass.RNSVGUse);
+ mDelegate = new RNSVGUseManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "href")
+ public void setHref(UseView node, String href) {
+ node.setHref(href);
+ }
+
+ @Override
+ public void setX(UseView view, @Nullable String value) {
+ view.setX(value);
+ }
+
+ @Override
+ public void setY(UseView view, @Nullable String value) {
+ view.setY(value);
+ }
+
+ @Override
+ public void setHeight(UseView view, @Nullable String value) {
+ view.setHeight(value);
+ }
+
+ public void setWidth(UseView view, @Nullable Double value) {
+ view.setWidth(value);
+ }
+
+ public void setX(UseView view, @Nullable Double value) {
+ view.setX(value);
+ }
+
+ public void setY(UseView view, @Nullable Double value) {
+ view.setY(value);
+ }
+
+ public void setHeight(UseView view, @Nullable Double value) {
+ view.setHeight(value);
+ }
+
+ @Override
+ public void setWidth(UseView view, @Nullable String value) {
+ view.setWidth(value);
+ }
+
+ @ReactProp(name = "x")
+ public void setX(UseView node, Dynamic x) {
+ node.setX(x);
+ }
+
+ @ReactProp(name = "y")
+ public void setY(UseView node, Dynamic y) {
+ node.setY(y);
+ }
+
+ @ReactProp(name = "width")
+ public void setWidth(UseView node, Dynamic width) {
+ node.setWidth(width);
+ }
+
+ @ReactProp(name = "height")
+ public void setHeight(UseView node, Dynamic height) {
+ node.setHeight(height);
+ }
+ }
+
+ static class SymbolManager extends GroupViewManagerAbstract
+ implements RNSVGSymbolManagerInterface {
+ SymbolManager() {
+ super(SVGClass.RNSVGSymbol);
+ mDelegate = new RNSVGSymbolManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "minX")
+ public void setMinX(SymbolView node, float minX) {
+ node.setMinX(minX);
+ }
+
+ @ReactProp(name = "minY")
+ public void setMinY(SymbolView node, float minY) {
+ node.setMinY(minY);
+ }
+
+ @ReactProp(name = "vbWidth")
+ public void setVbWidth(SymbolView node, float vbWidth) {
+ node.setVbWidth(vbWidth);
+ }
+
+ @ReactProp(name = "vbHeight")
+ public void setVbHeight(SymbolView node, float vbHeight) {
+ node.setVbHeight(vbHeight);
+ }
+
+ @ReactProp(name = "align")
+ public void setAlign(SymbolView node, String align) {
+ node.setAlign(align);
+ }
+
+ @ReactProp(name = "meetOrSlice")
+ public void setMeetOrSlice(SymbolView node, int meetOrSlice) {
+ node.setMeetOrSlice(meetOrSlice);
+ }
+ }
+
+ static class PatternManager extends GroupViewManagerAbstract
+ implements RNSVGPatternManagerInterface {
+ PatternManager() {
+ super(SVGClass.RNSVGPattern);
+ mDelegate = new RNSVGPatternManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "x")
+ public void setX(PatternView node, Dynamic x) {
+ node.setX(x);
+ }
+
+ @ReactProp(name = "y")
+ public void setY(PatternView node, Dynamic y) {
+ node.setY(y);
+ }
+
+ @ReactProp(name = "width")
+ public void setWidth(PatternView node, Dynamic width) {
+ node.setWidth(width);
+ }
+
+ @ReactProp(name = "height")
+ public void setHeight(PatternView node, Dynamic height) {
+ node.setHeight(height);
+ }
+
+ @Override
+ public void setX(PatternView view, @Nullable String value) {
+ view.setX(value);
+ }
+
+ @Override
+ public void setY(PatternView view, @Nullable String value) {
+ view.setY(value);
+ }
+
+ @Override
+ public void setHeight(PatternView view, @Nullable String value) {
+ view.setHeight(value);
+ }
+
+ @Override
+ public void setWidth(PatternView view, @Nullable String value) {
+ view.setWidth(value);
+ }
+
+ public void setX(PatternView view, @Nullable Double value) {
+ view.setX(value);
+ }
+
+ public void setY(PatternView view, @Nullable Double value) {
+ view.setY(value);
+ }
+
+ public void setHeight(PatternView view, @Nullable Double value) {
+ view.setHeight(value);
+ }
+
+ public void setWidth(PatternView view, @Nullable Double value) {
+ view.setWidth(value);
+ }
+
+ @ReactProp(name = "patternUnits")
+ public void setPatternUnits(PatternView node, int patternUnits) {
+ node.setPatternUnits(patternUnits);
+ }
+
+ @ReactProp(name = "patternContentUnits")
+ public void setPatternContentUnits(PatternView node, int patternContentUnits) {
+ node.setPatternContentUnits(patternContentUnits);
+ }
+
+ @ReactProp(name = "patternTransform")
+ public void setPatternTransform(PatternView node, @Nullable ReadableArray matrixArray) {
+ node.setPatternTransform(matrixArray);
+ }
+
+ @ReactProp(name = "minX")
+ public void setMinX(PatternView node, float minX) {
+ node.setMinX(minX);
+ }
+
+ @ReactProp(name = "minY")
+ public void setMinY(PatternView node, float minY) {
+ node.setMinY(minY);
+ }
+
+ @ReactProp(name = "vbWidth")
+ public void setVbWidth(PatternView node, float vbWidth) {
+ node.setVbWidth(vbWidth);
+ }
+
+ @ReactProp(name = "vbHeight")
+ public void setVbHeight(PatternView node, float vbHeight) {
+ node.setVbHeight(vbHeight);
+ }
+
+ @ReactProp(name = "align")
+ public void setAlign(PatternView node, String align) {
+ node.setAlign(align);
+ }
+
+ @ReactProp(name = "meetOrSlice")
+ public void setMeetOrSlice(PatternView node, int meetOrSlice) {
+ node.setMeetOrSlice(meetOrSlice);
+ }
+ }
+
+ static class MaskManager extends GroupViewManagerAbstract
+ implements RNSVGMaskManagerInterface {
+ MaskManager() {
+ super(SVGClass.RNSVGMask);
+ mDelegate = new RNSVGMaskManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "x")
+ public void setX(MaskView node, Dynamic x) {
+ node.setX(x);
+ }
+
+ @ReactProp(name = "y")
+ public void setY(MaskView node, Dynamic y) {
+ node.setY(y);
+ }
+
+ @ReactProp(name = "width")
+ public void setWidth(MaskView node, Dynamic width) {
+ node.setWidth(width);
+ }
+
+ @ReactProp(name = "height")
+ public void setHeight(MaskView node, Dynamic height) {
+ node.setHeight(height);
+ }
+
+ @Override
+ public void setX(MaskView view, @Nullable String value) {
+ view.setX(value);
+ }
+
+ @Override
+ public void setY(MaskView view, @Nullable String value) {
+ view.setY(value);
+ }
+
+ @Override
+ public void setHeight(MaskView view, @Nullable String value) {
+ view.setHeight(value);
+ }
+
+ @Override
+ public void setWidth(MaskView view, @Nullable String value) {
+ view.setWidth(value);
+ }
+
+ public void setX(MaskView view, @Nullable Double value) {
+ view.setX(value);
+ }
+
+ public void setY(MaskView view, @Nullable Double value) {
+ view.setY(value);
+ }
+
+ public void setHeight(MaskView view, @Nullable Double value) {
+ view.setHeight(value);
+ }
+
+ public void setWidth(MaskView view, @Nullable Double value) {
+ view.setWidth(value);
+ }
+
+ @ReactProp(name = "maskUnits")
+ public void setMaskUnits(MaskView node, int maskUnits) {
+ node.setMaskUnits(maskUnits);
+ }
+
+ @ReactProp(name = "maskContentUnits")
+ public void setMaskContentUnits(MaskView node, int maskContentUnits) {
+ node.setMaskContentUnits(maskContentUnits);
+ }
+
+ @ReactProp(name = "maskTransform")
+ public void setMaskTransform(MaskView node, @Nullable ReadableArray matrixArray) {
+ node.setMaskTransform(matrixArray);
+ }
+ }
+
+ static class ForeignObjectManager extends GroupViewManagerAbstract
+ implements RNSVGForeignObjectManagerInterface {
+ ForeignObjectManager() {
+ super(SVGClass.RNSVGForeignObject);
+ mDelegate = new RNSVGForeignObjectManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "x")
+ public void setX(ForeignObjectView node, Dynamic x) {
+ node.setX(x);
+ }
+
+ @ReactProp(name = "y")
+ public void setY(ForeignObjectView node, Dynamic y) {
+ node.setY(y);
+ }
+
+ @ReactProp(name = "width")
+ public void setWidth(ForeignObjectView node, Dynamic width) {
+ node.setWidth(width);
+ }
+
+ @ReactProp(name = "height")
+ public void setHeight(ForeignObjectView node, Dynamic height) {
+ node.setHeight(height);
+ }
+
+ @Override
+ public void setX(ForeignObjectView view, @Nullable String value) {
+ view.setX(value);
+ }
+
+ @Override
+ public void setY(ForeignObjectView view, @Nullable String value) {
+ view.setY(value);
+ }
+
+ @Override
+ public void setHeight(ForeignObjectView view, @Nullable String value) {
+ view.setHeight(value);
+ }
+
+ @Override
+ public void setWidth(ForeignObjectView view, @Nullable String value) {
+ view.setWidth(value);
+ }
+
+ public void setX(ForeignObjectView view, @Nullable Double value) {
+ view.setX(value);
+ }
+
+ public void setY(ForeignObjectView view, @Nullable Double value) {
+ view.setY(value);
+ }
+
+ public void setHeight(ForeignObjectView view, @Nullable Double value) {
+ view.setHeight(value);
+ }
+
+ public void setWidth(ForeignObjectView view, @Nullable Double value) {
+ view.setWidth(value);
+ }
+ }
+
+ static class MarkerManager extends GroupViewManagerAbstract
+ implements RNSVGMarkerManagerInterface {
+ MarkerManager() {
+ super(SVGClass.RNSVGMarker);
+ mDelegate = new RNSVGMarkerManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "refX")
+ public void setRefX(MarkerView node, Dynamic refX) {
+ node.setRefX(refX);
+ }
+
+ @ReactProp(name = "refY")
+ public void setRefY(MarkerView node, Dynamic refY) {
+ node.setRefY(refY);
+ }
+
+ @ReactProp(name = "markerWidth")
+ public void setMarkerWidth(MarkerView node, Dynamic markerWidth) {
+ node.setMarkerWidth(markerWidth);
+ }
+
+ @ReactProp(name = "markerHeight")
+ public void setMarkerHeight(MarkerView node, Dynamic markerHeight) {
+ node.setMarkerHeight(markerHeight);
+ }
+
+ @Override
+ public void setRefX(MarkerView view, @Nullable String value) {
+ view.setRefX(value);
+ }
+
+ @Override
+ public void setRefY(MarkerView view, @Nullable String value) {
+ view.setRefY(value);
+ }
+
+ @Override
+ public void setMarkerHeight(MarkerView view, @Nullable String value) {
+ view.setMarkerHeight(value);
+ }
+
+ @Override
+ public void setMarkerWidth(MarkerView view, @Nullable String value) {
+ view.setMarkerWidth(value);
+ }
+
+ public void setRefX(MarkerView view, @Nullable Double value) {
+ view.setRefX(value);
+ }
+
+ public void setRefY(MarkerView view, @Nullable Double value) {
+ view.setRefY(value);
+ }
+
+ public void setMarkerHeight(MarkerView view, @Nullable Double value) {
+ view.setMarkerHeight(value);
+ }
+
+ public void setMarkerWidth(MarkerView view, @Nullable Double value) {
+ view.setMarkerWidth(value);
+ }
+
+ @ReactProp(name = "markerUnits")
+ public void setMarkerUnits(MarkerView node, String markerUnits) {
+ node.setMarkerUnits(markerUnits);
+ }
+
+ @ReactProp(name = "orient")
+ public void setOrient(MarkerView node, String orient) {
+ node.setOrient(orient);
+ }
+
+ @ReactProp(name = "minX")
+ public void setMinX(MarkerView node, float minX) {
+ node.setMinX(minX);
+ }
+
+ @ReactProp(name = "minY")
+ public void setMinY(MarkerView node, float minY) {
+ node.setMinY(minY);
+ }
+
+ @ReactProp(name = "vbWidth")
+ public void setVbWidth(MarkerView node, float vbWidth) {
+ node.setVbWidth(vbWidth);
+ }
+
+ @ReactProp(name = "vbHeight")
+ public void setVbHeight(MarkerView node, float vbHeight) {
+ node.setVbHeight(vbHeight);
+ }
+
+ @ReactProp(name = "align")
+ public void setAlign(MarkerView node, String align) {
+ node.setAlign(align);
+ }
+
+ @ReactProp(name = "meetOrSlice")
+ public void setMeetOrSlice(MarkerView node, int meetOrSlice) {
+ node.setMeetOrSlice(meetOrSlice);
+ }
+ }
+
+ static class LinearGradientManager extends VirtualViewManager
+ implements RNSVGLinearGradientManagerInterface {
+
+ LinearGradientManager() {
+ super(SVGClass.RNSVGLinearGradient);
+ mDelegate = new RNSVGLinearGradientManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "x1")
+ public void setX1(LinearGradientView node, Dynamic x1) {
+ node.setX1(x1);
+ }
+
+ @ReactProp(name = "y1")
+ public void setY1(LinearGradientView node, Dynamic y1) {
+ node.setY1(y1);
+ }
+
+ @ReactProp(name = "x2")
+ public void setX2(LinearGradientView node, Dynamic x2) {
+ node.setX2(x2);
+ }
+
+ @ReactProp(name = "y2")
+ public void setY2(LinearGradientView node, Dynamic y2) {
+ node.setY2(y2);
+ }
+
+ @Override
+ public void setX1(LinearGradientView view, @Nullable String value) {
+ view.setX1(value);
+ }
+
+ @Override
+ public void setY1(LinearGradientView view, @Nullable String value) {
+ view.setY1(value);
+ }
+
+ @Override
+ public void setX2(LinearGradientView view, @Nullable String value) {
+ view.setX2(value);
+ }
+
+ @Override
+ public void setY2(LinearGradientView view, @Nullable String value) {
+ view.setY2(value);
+ }
+
+ public void setX1(LinearGradientView view, @Nullable Double value) {
+ view.setX1(value);
+ }
+
+ public void setY1(LinearGradientView view, @Nullable Double value) {
+ view.setY1(value);
+ }
+
+ public void setX2(LinearGradientView view, @Nullable Double value) {
+ view.setX2(value);
+ }
+
+ public void setY2(LinearGradientView view, @Nullable Double value) {
+ view.setY2(value);
+ }
+
+ @ReactProp(name = "gradient")
+ public void setGradient(LinearGradientView node, ReadableArray gradient) {
+ node.setGradient(gradient);
+ }
+
+ @ReactProp(name = "gradientUnits")
+ public void setGradientUnits(LinearGradientView node, int gradientUnits) {
+ node.setGradientUnits(gradientUnits);
+ }
+
+ @ReactProp(name = "gradientTransform")
+ public void setGradientTransform(LinearGradientView node, @Nullable ReadableArray matrixArray) {
+ node.setGradientTransform(matrixArray);
+ }
+ }
+
+ static class RadialGradientManager extends VirtualViewManager
+ implements RNSVGRadialGradientManagerInterface {
+
+ RadialGradientManager() {
+ super(SVGClass.RNSVGRadialGradient);
+ mDelegate = new RNSVGRadialGradientManagerDelegate(this);
+ }
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ @ReactProp(name = "fx")
+ public void setFx(RadialGradientView node, Dynamic fx) {
+ node.setFx(fx);
+ }
+
+ @ReactProp(name = "fy")
+ public void setFy(RadialGradientView node, Dynamic fy) {
+ node.setFy(fy);
+ }
+
+ @ReactProp(name = "rx")
+ public void setRx(RadialGradientView node, Dynamic rx) {
+ node.setRx(rx);
+ }
+
+ @ReactProp(name = "ry")
+ public void setRy(RadialGradientView node, Dynamic ry) {
+ node.setRy(ry);
+ }
+
+ @ReactProp(name = "cx")
+ public void setCx(RadialGradientView node, Dynamic cx) {
+ node.setCx(cx);
+ }
+
+ @ReactProp(name = "cy")
+ public void setCy(RadialGradientView node, Dynamic cy) {
+ node.setCy(cy);
+ }
+
+ @Override
+ public void setFx(RadialGradientView view, @Nullable String value) {
+ view.setFx(value);
+ }
+
+ @Override
+ public void setFy(RadialGradientView view, @Nullable String value) {
+ view.setFy(value);
+ }
+
+ @Override
+ public void setCx(RadialGradientView view, @Nullable String value) {
+ view.setCx(value);
+ }
+
+ @Override
+ public void setCy(RadialGradientView view, @Nullable String value) {
+ view.setCy(value);
+ }
+
+ @Override
+ public void setRx(RadialGradientView view, @Nullable String value) {
+ view.setRx(value);
+ }
+
+ @Override
+ public void setRy(RadialGradientView view, @Nullable String value) {
+ view.setRy(value);
+ }
+
+ public void setFx(RadialGradientView view, @Nullable Double value) {
+ view.setFx(value);
+ }
+
+ public void setFy(RadialGradientView view, @Nullable Double value) {
+ view.setFy(value);
+ }
+
+ public void setCx(RadialGradientView view, @Nullable Double value) {
+ view.setCx(value);
+ }
+
+ public void setCy(RadialGradientView view, @Nullable Double value) {
+ view.setCy(value);
+ }
+
+ public void setRx(RadialGradientView view, @Nullable Double value) {
+ view.setRx(value);
+ }
+
+ public void setRy(RadialGradientView view, @Nullable Double value) {
+ view.setRy(value);
+ }
+
+ @ReactProp(name = "gradient")
+ public void setGradient(RadialGradientView node, ReadableArray gradient) {
+ node.setGradient(gradient);
+ }
+
+ @ReactProp(name = "gradientUnits")
+ public void setGradientUnits(RadialGradientView node, int gradientUnits) {
+ node.setGradientUnits(gradientUnits);
+ }
+
+ @ReactProp(name = "gradientTransform")
+ public void setGradientTransform(RadialGradientView node, @Nullable ReadableArray matrixArray) {
+ node.setGradientTransform(matrixArray);
+ }
+ }
+
+ @ReactProp(name = "fill")
+ public void setFill(T node, @Nullable Dynamic fill) {
+ node.setFill(fill);
+ }
+
+ public void setFill(T view, @Nullable ReadableMap value) {
+ view.setFill(value);
+ }
+
+ @ReactProp(name = "fillOpacity", defaultFloat = 1f)
+ public void setFillOpacity(T node, float fillOpacity) {
+ node.setFillOpacity(fillOpacity);
+ }
+
+ @ReactProp(name = "fillRule", defaultInt = FILL_RULE_NONZERO)
+ public void setFillRule(T node, int fillRule) {
+ node.setFillRule(fillRule);
+ }
+
+ @ReactProp(name = "stroke")
+ public void setStroke(T node, @Nullable Dynamic strokeColors) {
+ node.setStroke(strokeColors);
+ }
+
+ public void setStroke(T view, @Nullable ReadableMap value) {
+ view.setStroke(value);
+ }
+
+ @ReactProp(name = "strokeOpacity", defaultFloat = 1f)
+ public void setStrokeOpacity(T node, float strokeOpacity) {
+ node.setStrokeOpacity(strokeOpacity);
+ }
+
+ @ReactProp(name = "strokeDasharray")
+ public void setStrokeDasharray(T node, @Nullable ReadableArray strokeDasharray) {
+ node.setStrokeDasharray(strokeDasharray);
+ }
+
+ @ReactProp(name = "strokeDashoffset")
+ public void setStrokeDashoffset(T node, float strokeDashoffset) {
+ node.setStrokeDashoffset(strokeDashoffset);
+ }
+
+ @ReactProp(name = "strokeWidth")
+ public void setStrokeWidth(T node, Dynamic strokeWidth) {
+ node.setStrokeWidth(strokeWidth);
+ }
+
+ public void setStrokeWidth(T view, @Nullable String value) {
+ view.setStrokeWidth(value);
+ }
+
+ public void setStrokeWidth(T view, @Nullable Double value) {
+ view.setStrokeWidth(value);
+ }
+
+ @ReactProp(name = "strokeMiterlimit", defaultFloat = 4f)
+ public void setStrokeMiterlimit(T node, float strokeMiterlimit) {
+ node.setStrokeMiterlimit(strokeMiterlimit);
+ }
+
+ @ReactProp(name = "strokeLinecap", defaultInt = CAP_ROUND)
+ public void setStrokeLinecap(T node, int strokeLinecap) {
+ node.setStrokeLinecap(strokeLinecap);
+ }
+
+ @ReactProp(name = "strokeLinejoin", defaultInt = JOIN_ROUND)
+ public void setStrokeLinejoin(T node, int strokeLinejoin) {
+ node.setStrokeLinejoin(strokeLinejoin);
+ }
+
+ @ReactProp(name = "vectorEffect")
+ public void setVectorEffect(T node, int vectorEffect) {
+ node.setVectorEffect(vectorEffect);
+ }
+
+ @ReactProp(name = "propList")
+ public void setPropList(T node, @Nullable ReadableArray propList) {
+ node.setPropList(propList);
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SVGLength.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SVGLength.java
new file mode 100644
index 0000000000000..e58998367fb95
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SVGLength.java
@@ -0,0 +1,164 @@
+package com.horcrux.svg;
+
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReadableArray;
+import java.util.ArrayList;
+
+class SVGLength {
+ // https://www.w3.org/TR/SVG/types.html#InterfaceSVGLength
+ public enum UnitType {
+ UNKNOWN,
+ NUMBER,
+ PERCENTAGE,
+ EMS,
+ EXS,
+ PX,
+ CM,
+ MM,
+ IN,
+ PT,
+ PC,
+ }
+
+ final double value;
+ final UnitType unit;
+
+ private SVGLength() {
+ value = 0;
+ unit = UnitType.UNKNOWN;
+ }
+
+ SVGLength(double number) {
+ value = number;
+ unit = UnitType.NUMBER;
+ }
+
+ private SVGLength(String length) {
+ length = length.trim();
+ int stringLength = length.length();
+ int percentIndex = stringLength - 1;
+ if (stringLength == 0 || length.equals("normal")) {
+ unit = UnitType.UNKNOWN;
+ value = 0;
+ } else if (length.codePointAt(percentIndex) == '%') {
+ unit = UnitType.PERCENTAGE;
+ value = Double.valueOf(length.substring(0, percentIndex));
+ } else {
+ int twoLetterUnitIndex = stringLength - 2;
+ if (twoLetterUnitIndex > 0) {
+ String lastTwo = length.substring(twoLetterUnitIndex);
+ int end = twoLetterUnitIndex;
+ switch (lastTwo) {
+ case "px":
+ unit = UnitType.NUMBER;
+ break;
+
+ case "em":
+ unit = UnitType.EMS;
+ break;
+ case "ex":
+ unit = UnitType.EXS;
+ break;
+
+ case "pt":
+ unit = UnitType.PT;
+ break;
+
+ case "pc":
+ unit = UnitType.PC;
+ break;
+
+ case "mm":
+ unit = UnitType.MM;
+ break;
+
+ case "cm":
+ unit = UnitType.CM;
+ break;
+
+ case "in":
+ unit = UnitType.IN;
+ break;
+
+ default:
+ unit = UnitType.NUMBER;
+ end = stringLength;
+ }
+ value = Double.valueOf(length.substring(0, end));
+ } else {
+ unit = UnitType.NUMBER;
+ value = Double.valueOf(length);
+ }
+ }
+ }
+
+ static SVGLength from(Dynamic dynamic) {
+ switch (dynamic.getType()) {
+ case Number:
+ return new SVGLength(dynamic.asDouble());
+ case String:
+ return new SVGLength(dynamic.asString());
+ default:
+ return new SVGLength();
+ }
+ }
+
+ static SVGLength from(String string) {
+ return new SVGLength(string);
+ }
+
+ static SVGLength from(Double value) {
+ return new SVGLength(value);
+ }
+
+ static String toString(Dynamic dynamic) {
+ switch (dynamic.getType()) {
+ case Number:
+ return String.valueOf(dynamic.asDouble());
+ case String:
+ return dynamic.asString();
+ default:
+ return null;
+ }
+ }
+
+ static ArrayList arrayFrom(Dynamic dynamic) {
+ switch (dynamic.getType()) {
+ case Number:
+ {
+ ArrayList list = new ArrayList<>(1);
+ list.add(new SVGLength(dynamic.asDouble()));
+ return list;
+ }
+ case Array:
+ {
+ ReadableArray arr = dynamic.asArray();
+ int size = arr.size();
+ ArrayList list = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Dynamic val = arr.getDynamic(i);
+ list.add(from(val));
+ }
+ return list;
+ }
+ case String:
+ {
+ ArrayList list = new ArrayList<>(1);
+ list.add(new SVGLength(dynamic.asString()));
+ return list;
+ }
+ default:
+ return null;
+ }
+ }
+
+ static ArrayList arrayFrom(ReadableArray arr) {
+ int size = arr.size();
+ ArrayList list = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Dynamic val = arr.getDynamic(i);
+ list.add(from(val));
+ }
+ return list;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgPackage.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgPackage.java
new file mode 100644
index 0000000000000..741f043e6f947
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgPackage.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import static com.horcrux.svg.RenderableViewManager.*;
+
+import com.facebook.react.ReactPackage;
+import com.facebook.react.bridge.JavaScriptModule;
+import com.facebook.react.bridge.NativeModule;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.uimanager.ViewManager;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnull;
+
+public class SvgPackage implements ReactPackage {
+
+ @Nonnull
+ @Override
+ public List createViewManagers(@Nonnull ReactApplicationContext reactContext) {
+ return Arrays.asList(
+ new GroupViewManager(),
+ new PathViewManager(),
+ new CircleViewManager(),
+ new EllipseViewManager(),
+ new LineViewManager(),
+ new RectViewManager(),
+ new TextViewManager(),
+ new TSpanViewManager(),
+ new TextPathViewManager(),
+ new ImageViewManager(),
+ new ClipPathViewManager(),
+ new DefsViewManager(),
+ new UseViewManager(),
+ new SymbolManager(),
+ new LinearGradientManager(),
+ new RadialGradientManager(),
+ new PatternManager(),
+ new MaskManager(),
+ new ForeignObjectManager(),
+ new MarkerManager(),
+ new SvgViewManager());
+ }
+
+ @Nonnull
+ @Override
+ public List createNativeModules(@Nonnull ReactApplicationContext reactContext) {
+ return Arrays.asList(
+ new SvgViewModule(reactContext), new RNSVGRenderableManager(reactContext));
+ }
+
+ @SuppressWarnings("unused")
+ public List> createJSModules() {
+ return Collections.emptyList();
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgView.java
new file mode 100644
index 0000000000000..523fcc16caec4
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgView.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.util.Base64;
+import android.view.View;
+import android.view.ViewParent;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.uimanager.DisplayMetricsHolder;
+import com.facebook.react.uimanager.ReactCompoundView;
+import com.facebook.react.uimanager.ReactCompoundViewGroup;
+import com.facebook.react.views.view.ReactViewGroup;
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/** Custom {@link View} implementation that draws an RNSVGSvg React view and its children. */
+@SuppressLint("ViewConstructor")
+public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactCompoundViewGroup {
+
+ @Override
+ public boolean interceptsTouchEvent(float touchX, float touchY) {
+ return true;
+ }
+
+ @SuppressWarnings("unused")
+ public enum Events {
+ EVENT_DATA_URL("onDataURL");
+
+ private final String mName;
+
+ Events(final String name) {
+ mName = name;
+ }
+
+ @Nonnull
+ public String toString() {
+ return mName;
+ }
+ }
+
+ private @Nullable Bitmap mBitmap;
+ private boolean mRemovalTransitionStarted;
+
+ public SvgView(ReactContext reactContext) {
+ super(reactContext);
+ mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
+ // for some reason on Fabric the `onDraw` won't be called without it
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public void setId(int id) {
+ super.setId(id);
+ SvgViewManager.setSvgView(id, this);
+ }
+
+ @Override
+ public void invalidate() {
+ super.invalidate();
+ ViewParent parent = getParent();
+ if (parent instanceof VirtualView) {
+ if (!mRendered) {
+ return;
+ }
+ mRendered = false;
+ ((VirtualView) parent).getSvgView().invalidate();
+ return;
+ }
+ if (!mRemovalTransitionStarted) {
+ // when view is removed from the view hierarchy, we want to recycle the mBitmap when
+ // the view is detached from window, in order to preserve it for during animation, see
+ // https://github.com/react-native-svg/react-native-svg/pull/1542
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ }
+ mBitmap = null;
+ }
+ }
+
+ @Override
+ public void startViewTransition(View view) {
+ mRemovalTransitionStarted = true;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ }
+ mBitmap = null;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (getParent() instanceof VirtualView) {
+ return;
+ }
+ super.onDraw(canvas);
+ if (mBitmap == null) {
+ mBitmap = drawOutput();
+ }
+ if (mBitmap != null) {
+ canvas.drawBitmap(mBitmap, 0, 0, null);
+ if (toDataUrlTask != null) {
+ toDataUrlTask.run();
+ toDataUrlTask = null;
+ }
+ }
+ }
+
+ private Runnable toDataUrlTask = null;
+
+ void setToDataUrlTask(Runnable task) {
+ toDataUrlTask = task;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ this.invalidate();
+ }
+
+ @Override
+ public int reactTagForTouch(float touchX, float touchY) {
+ return hitTest(touchX, touchY);
+ }
+
+ private boolean mResponsible = false;
+
+ private final Map mDefinedClipPaths = new HashMap<>();
+ private final Map mDefinedTemplates = new HashMap<>();
+ private final Map mDefinedMarkers = new HashMap<>();
+ private final Map mDefinedMasks = new HashMap<>();
+ private final Map mDefinedBrushes = new HashMap<>();
+ private Canvas mCanvas;
+ private final float mScale;
+
+ private float mMinX;
+ private float mMinY;
+ private float mVbWidth;
+ private float mVbHeight;
+ private SVGLength mbbWidth;
+ private SVGLength mbbHeight;
+ private String mAlign;
+ private int mMeetOrSlice;
+ final Matrix mInvViewBoxMatrix = new Matrix();
+ private boolean mInvertible = true;
+ private boolean mRendered = false;
+ int mTintColor = 0;
+
+ boolean notRendered() {
+ return !mRendered;
+ }
+
+ private void clearChildCache() {
+ if (!mRendered) {
+ return;
+ }
+ mRendered = false;
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof VirtualView) {
+ VirtualView n = ((VirtualView) node);
+ n.clearChildCache();
+ }
+ }
+ }
+
+ public void setTintColor(Integer tintColor) {
+ mTintColor = tintColor;
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setMinX(float minX) {
+ mMinX = minX;
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setMinY(float minY) {
+ mMinY = minY;
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setVbWidth(float vbWidth) {
+ mVbWidth = vbWidth;
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setVbHeight(float vbHeight) {
+ mVbHeight = vbHeight;
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setBbWidth(Dynamic bbWidth) {
+ mbbWidth = SVGLength.from(bbWidth);
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setBbWidth(String bbWidth) {
+ mbbWidth = SVGLength.from(bbWidth);
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setBbWidth(Double bbWidth) {
+ mbbWidth = SVGLength.from(bbWidth);
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setBbHeight(Dynamic bbHeight) {
+ mbbHeight = SVGLength.from(bbHeight);
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setBbHeight(String bbHeight) {
+ mbbHeight = SVGLength.from(bbHeight);
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setBbHeight(Double bbHeight) {
+ mbbHeight = SVGLength.from(bbHeight);
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setAlign(String align) {
+ mAlign = align;
+ invalidate();
+ clearChildCache();
+ }
+
+ public void setMeetOrSlice(int meetOrSlice) {
+ mMeetOrSlice = meetOrSlice;
+ invalidate();
+ clearChildCache();
+ }
+
+ private Bitmap drawOutput() {
+ mRendered = true;
+ float width = getWidth();
+ float height = getHeight();
+ boolean invalid =
+ Float.isNaN(width)
+ || Float.isNaN(height)
+ || width < 1
+ || height < 1
+ || (Math.log10(width) + Math.log10(height) > 42);
+ if (invalid) {
+ return null;
+ }
+ Bitmap bitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
+
+ drawChildren(new Canvas(bitmap));
+ return bitmap;
+ }
+
+ Rect getCanvasBounds() {
+ return mCanvas.getClipBounds();
+ }
+
+ synchronized void drawChildren(final Canvas canvas) {
+ mRendered = true;
+ mCanvas = canvas;
+ Matrix mViewBoxMatrix = new Matrix();
+ if (mAlign != null) {
+ RectF vbRect = getViewBox();
+ float width = canvas.getWidth();
+ float height = canvas.getHeight();
+ boolean nested = getParent() instanceof VirtualView;
+ if (nested) {
+ width = (float) PropHelper.fromRelative(mbbWidth, width, 0f, mScale, 12);
+ height = (float) PropHelper.fromRelative(mbbHeight, height, 0f, mScale, 12);
+ }
+ RectF eRect = new RectF(0, 0, width, height);
+ if (nested) {
+ canvas.clipRect(eRect);
+ }
+ mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
+ mInvertible = mViewBoxMatrix.invert(mInvViewBoxMatrix);
+ canvas.concat(mViewBoxMatrix);
+ }
+
+ final Paint paint = new Paint();
+
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG | Paint.SUBPIXEL_TEXT_FLAG);
+
+ paint.setTypeface(Typeface.DEFAULT);
+
+ for (int i = 0; i < getChildCount(); i++) {
+ View node = getChildAt(i);
+ if (node instanceof VirtualView) {
+ ((VirtualView) node).saveDefinition();
+ }
+ }
+
+ for (int i = 0; i < getChildCount(); i++) {
+ View lNode = getChildAt(i);
+ if (lNode instanceof VirtualView) {
+ VirtualView node = (VirtualView) lNode;
+ int count = node.saveAndSetupCanvas(canvas, mViewBoxMatrix);
+ node.render(canvas, paint, 1f);
+ node.restoreCanvas(canvas, count);
+
+ if (node.isResponsible() && !mResponsible) {
+ mResponsible = true;
+ }
+ }
+ }
+ }
+
+ private RectF getViewBox() {
+ return new RectF(
+ mMinX * mScale, mMinY * mScale, (mMinX + mVbWidth) * mScale, (mMinY + mVbHeight) * mScale);
+ }
+
+ String toDataURL() {
+ Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
+
+ clearChildCache();
+ drawChildren(new Canvas(bitmap));
+ clearChildCache();
+ this.invalidate();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
+ bitmap.recycle();
+ byte[] bitmapBytes = stream.toByteArray();
+ return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
+ }
+
+ String toDataURL(int width, int height) {
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+
+ clearChildCache();
+ drawChildren(new Canvas(bitmap));
+ clearChildCache();
+ this.invalidate();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
+ bitmap.recycle();
+ byte[] bitmapBytes = stream.toByteArray();
+ return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
+ }
+
+ void enableTouchEvents() {
+ if (!mResponsible) {
+ mResponsible = true;
+ }
+ }
+
+ boolean isResponsible() {
+ return mResponsible;
+ }
+
+ private int hitTest(float touchX, float touchY) {
+ if (!mResponsible || !mInvertible) {
+ return getId();
+ }
+
+ float[] transformed = {touchX, touchY};
+ mInvViewBoxMatrix.mapPoints(transformed);
+
+ int count = getChildCount();
+ int viewTag = -1;
+ for (int i = count - 1; i >= 0; i--) {
+ View child = getChildAt(i);
+ if (child instanceof VirtualView) {
+ viewTag = ((VirtualView) child).hitTest(transformed);
+ } else if (child instanceof SvgView) {
+ viewTag = ((SvgView) child).hitTest(touchX, touchY);
+ }
+ if (viewTag != -1) {
+ break;
+ }
+ }
+
+ return viewTag == -1 ? getId() : viewTag;
+ }
+
+ void defineClipPath(VirtualView clipPath, String clipPathRef) {
+ mDefinedClipPaths.put(clipPathRef, clipPath);
+ }
+
+ VirtualView getDefinedClipPath(String clipPathRef) {
+ return mDefinedClipPaths.get(clipPathRef);
+ }
+
+ void defineTemplate(VirtualView template, String templateRef) {
+ mDefinedTemplates.put(templateRef, template);
+ }
+
+ VirtualView getDefinedTemplate(String templateRef) {
+ return mDefinedTemplates.get(templateRef);
+ }
+
+ void defineBrush(Brush brush, String brushRef) {
+ mDefinedBrushes.put(brushRef, brush);
+ }
+
+ Brush getDefinedBrush(String brushRef) {
+ return mDefinedBrushes.get(brushRef);
+ }
+
+ void defineMask(VirtualView mask, String maskRef) {
+ mDefinedMasks.put(maskRef, mask);
+ }
+
+ VirtualView getDefinedMask(String maskRef) {
+ return mDefinedMasks.get(maskRef);
+ }
+
+ void defineMarker(VirtualView marker, String markerRef) {
+ mDefinedMarkers.put(markerRef, marker);
+ }
+
+ VirtualView getDefinedMarker(String markerRef) {
+ return mDefinedMarkers.get(markerRef);
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgViewManager.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgViewManager.java
new file mode 100644
index 0000000000000..54aa57af473df
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgViewManager.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.util.SparseArray;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.uimanager.PointerEvents;
+import com.facebook.react.uimanager.ThemedReactContext;
+import com.facebook.react.uimanager.ViewManagerDelegate;
+import com.facebook.react.uimanager.ViewProps;
+import com.facebook.react.uimanager.annotations.ReactProp;
+import com.facebook.react.viewmanagers.RNSVGSvgViewManagerDelegate;
+import com.facebook.react.viewmanagers.RNSVGSvgViewManagerInterface;
+import com.facebook.react.views.view.ReactViewGroup;
+import com.facebook.react.views.view.ReactViewManager;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Locale;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * ViewManager for RNSVGSvgView React views. Renders as a {@link SvgView} and handles invalidating
+ * the native view on view updates happening in the underlying tree.
+ */
+class SvgViewManager extends ReactViewManager implements RNSVGSvgViewManagerInterface {
+
+ private static final String REACT_CLASS = "RNSVGSvgView";
+
+ private static final SparseArray mTagToSvgView = new SparseArray<>();
+ private static final SparseArray mTagToRunnable = new SparseArray<>();
+
+ private final ViewManagerDelegate mDelegate;
+
+ protected ViewManagerDelegate getDelegate() {
+ return mDelegate;
+ }
+
+ public SvgViewManager() {
+ mDelegate = new RNSVGSvgViewManagerDelegate(this);
+ }
+
+ static void setSvgView(int tag, SvgView svg) {
+ mTagToSvgView.put(tag, svg);
+ Runnable task = mTagToRunnable.get(tag);
+ if (task != null) {
+ task.run();
+ mTagToRunnable.delete(tag);
+ }
+ }
+
+ static void runWhenViewIsAvailable(int tag, Runnable task) {
+ mTagToRunnable.put(tag, task);
+ }
+
+ static @Nullable SvgView getSvgViewByTag(int tag) {
+ return mTagToSvgView.get(tag);
+ }
+
+ @Nonnull
+ @Override
+ public String getName() {
+ return REACT_CLASS;
+ }
+
+ @Nonnull
+ @Override
+ public ReactViewGroup createViewInstance(ThemedReactContext reactContext) {
+ return new SvgView(reactContext);
+ }
+
+ @Override
+ public void updateExtraData(ReactViewGroup root, Object extraData) {
+ super.updateExtraData(root, extraData);
+ root.invalidate();
+ }
+
+ @Override
+ public void onDropViewInstance(@Nonnull ReactViewGroup view) {
+ super.onDropViewInstance(view);
+ mTagToSvgView.remove(view.getId());
+ }
+
+ @Override
+ public boolean needsCustomLayoutForChildren() {
+ return true;
+ }
+
+ @ReactProp(name = "tintColor", customType = "Color")
+ @Override
+ public void setTintColor(SvgView node, Integer tintColor) {
+ node.setTintColor(tintColor);
+ }
+
+ @ReactProp(name = "color", customType = "Color")
+ @Override
+ public void setColor(SvgView node, Integer color) {
+ node.setTintColor(color);
+ }
+
+ @ReactProp(name = "minX")
+ @Override
+ public void setMinX(SvgView node, float minX) {
+ node.setMinX(minX);
+ }
+
+ @ReactProp(name = "minY")
+ @Override
+ public void setMinY(SvgView node, float minY) {
+ node.setMinY(minY);
+ }
+
+ @ReactProp(name = "vbWidth")
+ @Override
+ public void setVbWidth(SvgView node, float vbWidth) {
+ node.setVbWidth(vbWidth);
+ }
+
+ @ReactProp(name = "vbHeight")
+ @Override
+ public void setVbHeight(SvgView node, float vbHeight) {
+ node.setVbHeight(vbHeight);
+ }
+
+ @ReactProp(name = "bbWidth")
+ public void setBbWidth(SvgView node, Dynamic bbWidth) {
+ node.setBbWidth(bbWidth);
+ }
+
+ @ReactProp(name = "bbHeight")
+ public void setBbHeight(SvgView node, Dynamic bbHeight) {
+ node.setBbHeight(bbHeight);
+ }
+
+ @ReactProp(name = "align")
+ @Override
+ public void setAlign(SvgView node, String align) {
+ node.setAlign(align);
+ }
+
+ @ReactProp(name = "meetOrSlice")
+ @Override
+ public void setMeetOrSlice(SvgView node, int meetOrSlice) {
+ node.setMeetOrSlice(meetOrSlice);
+ }
+
+ @Override
+ public void setBbWidth(SvgView view, @Nullable String value) {
+ view.setBbWidth(value);
+ }
+
+ public void setBbWidth(SvgView view, @Nullable Double value) {
+ view.setBbWidth(value);
+ }
+
+ @Override
+ public void setBbHeight(SvgView view, @Nullable String value) {
+ view.setBbHeight(value);
+ }
+
+ public void setBbHeight(SvgView view, @Nullable Double value) {
+ view.setBbHeight(value);
+ }
+
+ @ReactProp(name = ViewProps.POINTER_EVENTS)
+ public void setPointerEvents(SvgView view, @Nullable String pointerEventsStr) {
+ try {
+ Class> superclass = view.getClass().getSuperclass();
+ if (superclass != null) {
+ Method method = superclass.getDeclaredMethod("setPointerEvents", PointerEvents.class);
+ method.setAccessible(true);
+ method.invoke(
+ view, PointerEvents.valueOf(pointerEventsStr.toUpperCase(Locale.US).replace("-", "_")));
+ }
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgViewModule.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgViewModule.java
new file mode 100644
index 0000000000000..30397a6e2e278
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SvgViewModule.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import com.facebook.react.bridge.Callback;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContextBaseJavaModule;
+import com.facebook.react.bridge.ReactMethod;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.bridge.UiThreadUtil;
+import javax.annotation.Nonnull;
+
+class SvgViewModule extends ReactContextBaseJavaModule {
+ SvgViewModule(ReactApplicationContext reactContext) {
+ super(reactContext);
+ }
+
+ @Nonnull
+ @Override
+ public String getName() {
+ return "RNSVGSvgViewManager";
+ }
+
+ private static void toDataURL(
+ final int tag, final ReadableMap options, final Callback successCallback, final int attempt) {
+ UiThreadUtil.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ SvgView svg = SvgViewManager.getSvgViewByTag(tag);
+
+ if (svg == null) {
+ SvgViewManager.runWhenViewIsAvailable(
+ tag,
+ new Runnable() {
+ @Override
+ public void run() {
+ SvgView svg = SvgViewManager.getSvgViewByTag(tag);
+ if (svg == null) { // Should never happen
+ return;
+ }
+ svg.setToDataUrlTask(
+ new Runnable() {
+ @Override
+ public void run() {
+ toDataURL(tag, options, successCallback, attempt + 1);
+ }
+ });
+ }
+ });
+ } else if (svg.notRendered()) {
+ svg.setToDataUrlTask(
+ new Runnable() {
+ @Override
+ public void run() {
+ toDataURL(tag, options, successCallback, attempt + 1);
+ }
+ });
+ } else {
+ if (options != null) {
+ successCallback.invoke(
+ svg.toDataURL(options.getInt("width"), options.getInt("height")));
+ } else {
+ successCallback.invoke(svg.toDataURL());
+ }
+ }
+ }
+ });
+ }
+
+ @SuppressWarnings("unused")
+ @ReactMethod
+ public void toDataURL(int tag, ReadableMap options, Callback successCallback) {
+ toDataURL(tag, options, successCallback, 0);
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SymbolView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SymbolView.java
new file mode 100644
index 0000000000000..7023e5610e43f
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/SymbolView.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import com.facebook.react.bridge.ReactContext;
+
+@SuppressLint("ViewConstructor")
+class SymbolView extends GroupView {
+
+ private float mMinX;
+ private float mMinY;
+ private float mVbWidth;
+ private float mVbHeight;
+ private String mAlign;
+ private int mMeetOrSlice;
+
+ public SymbolView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setMinX(float minX) {
+ mMinX = minX;
+ invalidate();
+ }
+
+ public void setMinY(float minY) {
+ mMinY = minY;
+ invalidate();
+ }
+
+ public void setVbWidth(float vbWidth) {
+ mVbWidth = vbWidth;
+ invalidate();
+ }
+
+ public void setVbHeight(float vbHeight) {
+ mVbHeight = vbHeight;
+ invalidate();
+ }
+
+ public void setAlign(String align) {
+ mAlign = align;
+ invalidate();
+ }
+
+ public void setMeetOrSlice(int meetOrSlice) {
+ mMeetOrSlice = meetOrSlice;
+ invalidate();
+ }
+
+ @Override
+ void draw(Canvas canvas, Paint paint, float opacity) {
+ saveDefinition();
+ }
+
+ void drawSymbol(Canvas canvas, Paint paint, float opacity, float width, float height) {
+ if (mAlign != null) {
+ RectF vbRect =
+ new RectF(
+ mMinX * mScale,
+ mMinY * mScale,
+ (mMinX + mVbWidth) * mScale,
+ (mMinY + mVbHeight) * mScale);
+ RectF eRect = new RectF(0, 0, width, height);
+ Matrix viewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
+ canvas.concat(viewBoxMatrix);
+ super.draw(canvas, paint, opacity);
+ }
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TSpanView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TSpanView.java
new file mode 100644
index 0000000000000..9b5cb6b26d658
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TSpanView.java
@@ -0,0 +1,1241 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import static android.graphics.Matrix.MTRANS_X;
+import static android.graphics.Matrix.MTRANS_Y;
+import static android.graphics.PathMeasure.POSITION_MATRIX_FLAG;
+import static android.graphics.PathMeasure.TANGENT_MATRIX_FLAG;
+import static com.horcrux.svg.TextProperties.AlignmentBaseline;
+import static com.horcrux.svg.TextProperties.FontStyle;
+import static com.horcrux.svg.TextProperties.FontVariantLigatures;
+import static com.horcrux.svg.TextProperties.FontWeight;
+import static com.horcrux.svg.TextProperties.TextAnchor;
+import static com.horcrux.svg.TextProperties.TextPathMidLine;
+import static com.horcrux.svg.TextProperties.TextPathSide;
+
+import android.annotation.SuppressLint;
+import android.content.res.AssetManager;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.text.Layout;
+import android.text.SpannableString;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.view.View;
+import android.view.ViewParent;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.views.text.ReactFontManager;
+import java.text.Bidi;
+import java.util.ArrayList;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class TSpanView extends TextView {
+ private static final double tau = 2 * Math.PI;
+ private static final double radToDeg = 360 / tau;
+
+ private static final String FONTS = "fonts/";
+ private static final String OTF = ".otf";
+ private static final String TTF = ".ttf";
+
+ private Path mCachedPath;
+ @Nullable String mContent;
+ private TextPathView textPath;
+ private final ArrayList emoji = new ArrayList<>();
+ private final ArrayList emojiTransforms = new ArrayList<>();
+ private final AssetManager assets;
+
+ public TSpanView(ReactContext reactContext) {
+ super(reactContext);
+ assets = mContext.getResources().getAssets();
+ }
+
+ public void setContent(@Nullable String content) {
+ mContent = content;
+ invalidate();
+ }
+
+ @Override
+ public void invalidate() {
+ mCachedPath = null;
+ super.invalidate();
+ }
+
+ void clearCache() {
+ mCachedPath = null;
+ super.clearCache();
+ }
+
+ @Override
+ void draw(Canvas canvas, Paint paint, float opacity) {
+ if (mContent != null) {
+ if (mInlineSize != null && mInlineSize.value != 0) {
+ if (setupFillPaint(paint, opacity * fillOpacity)) {
+ drawWrappedText(canvas, paint);
+ }
+ if (setupStrokePaint(paint, opacity * strokeOpacity)) {
+ drawWrappedText(canvas, paint);
+ }
+ } else {
+ int numEmoji = emoji.size();
+ if (numEmoji > 0) {
+ GlyphContext gc = getTextRootGlyphContext();
+ FontData font = gc.getFont();
+ applyTextPropertiesToPaint(paint, font);
+ for (int i = 0; i < numEmoji; i++) {
+ String current = emoji.get(i);
+ Matrix mid = emojiTransforms.get(i);
+ canvas.save();
+ canvas.concat(mid);
+ canvas.drawText(current, 0, 0, paint);
+ canvas.restore();
+ }
+ }
+ drawPath(canvas, paint, opacity);
+ }
+ } else {
+ clip(canvas, paint);
+ drawGroup(canvas, paint, opacity);
+ }
+ }
+
+ private void drawWrappedText(Canvas canvas, Paint paint) {
+ GlyphContext gc = getTextRootGlyphContext();
+ pushGlyphContext();
+ FontData font = gc.getFont();
+ TextPaint tp = new TextPaint(paint);
+ applyTextPropertiesToPaint(tp, font);
+ applySpacingAndFeatures(tp, font);
+ double fontSize = gc.getFontSize();
+
+ Layout.Alignment align;
+ switch (font.textAnchor) {
+ default:
+ case start:
+ align = Layout.Alignment.ALIGN_NORMAL;
+ break;
+
+ case middle:
+ align = Layout.Alignment.ALIGN_CENTER;
+ break;
+
+ case end:
+ align = Layout.Alignment.ALIGN_OPPOSITE;
+ break;
+ }
+
+ boolean includeFontPadding = true;
+ SpannableString text = new SpannableString(mContent);
+ final double width =
+ PropHelper.fromRelative(mInlineSize, canvas.getWidth(), 0, mScale, fontSize);
+ StaticLayout layout = getStaticLayout(tp, align, includeFontPadding, text, (int) width);
+
+ int lineAscent = layout.getLineAscent(0);
+
+ float dx = (float) gc.nextX(0);
+ float dy = (float) (gc.nextY() + lineAscent);
+ popGlyphContext();
+
+ canvas.save();
+ canvas.translate(dx, dy);
+ layout.draw(canvas);
+ canvas.restore();
+ }
+
+ @SuppressWarnings("deprecation")
+ private StaticLayout getStaticLayout(
+ TextPaint tp,
+ Layout.Alignment align,
+ boolean includeFontPadding,
+ SpannableString text,
+ int width) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return new StaticLayout(text, tp, width, align, 1.f, 0.f, includeFontPadding);
+ } else {
+ return StaticLayout.Builder.obtain(text, 0, text.length(), tp, width)
+ .setAlignment(align)
+ .setLineSpacing(0.f, 1.f)
+ .setIncludePad(includeFontPadding)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+ .build();
+ }
+ }
+
+ /**
+ * Implements visual to logical order converter.
+ *
+ * @author Nesterovsky bros
+ * @param text an input text in visual order to convert.
+ * @return a String value in logical order.
+ */
+ public static String visualToLogical(String text) {
+ if ((text == null) || (text.length() == 0)) {
+ return text;
+ }
+
+ Bidi bidi = new Bidi(text, Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
+
+ if (bidi.isLeftToRight()) {
+ return text;
+ }
+
+ int count = bidi.getRunCount();
+ byte[] levels = new byte[count];
+ Integer[] runs = new Integer[count];
+
+ for (int i = 0; i < count; i++) {
+ levels[i] = (byte) bidi.getRunLevel(i);
+ runs[i] = i;
+ }
+
+ Bidi.reorderVisually(levels, 0, runs, 0, count);
+
+ StringBuilder result = new StringBuilder();
+
+ for (int i = 0; i < count; i++) {
+ int index = runs[i];
+ int start = bidi.getRunStart(index);
+ int end = bidi.getRunLimit(index);
+ int level = levels[index];
+
+ if ((level & 1) != 0) {
+ for (; --end >= start; ) {
+ result.append(text.charAt(end));
+ }
+ } else {
+ result.append(text, start, end);
+ }
+ }
+
+ return result.toString();
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ if (mCachedPath != null) {
+ return mCachedPath;
+ }
+
+ if (mContent == null) {
+ mCachedPath = getGroupPath(canvas, paint);
+ return mCachedPath;
+ }
+
+ setupTextPath();
+
+ pushGlyphContext();
+ mCachedPath = getLinePath(visualToLogical(mContent), paint, canvas);
+ popGlyphContext();
+
+ return mCachedPath;
+ }
+
+ double getSubtreeTextChunksTotalAdvance(Paint paint) {
+ if (!Double.isNaN(cachedAdvance)) {
+ return cachedAdvance;
+ }
+ double advance = 0;
+
+ if (mContent == null) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (child instanceof TextView) {
+ TextView text = (TextView) child;
+ advance += text.getSubtreeTextChunksTotalAdvance(paint);
+ }
+ }
+ cachedAdvance = advance;
+ return advance;
+ }
+
+ String line = mContent;
+ final int length = line.length();
+
+ if (length == 0) {
+ cachedAdvance = 0;
+ return advance;
+ }
+
+ GlyphContext gc = getTextRootGlyphContext();
+ FontData font = gc.getFont();
+ applyTextPropertiesToPaint(paint, font);
+
+ applySpacingAndFeatures(paint, font);
+
+ cachedAdvance = paint.measureText(line);
+ return cachedAdvance;
+ }
+
+ static final String requiredFontFeatures =
+ "'rlig', 'liga', 'clig', 'calt', 'locl', 'ccmp', 'mark', 'mkmk',";
+ static final String disableDiscretionaryLigatures =
+ "'liga' 0, 'clig' 0, 'dlig' 0, 'hlig' 0, 'cala' 0, ";
+ static final String defaultFeatures = requiredFontFeatures + "'kern', ";
+ static final String additionalLigatures = "'hlig', 'cala', ";
+ static final String fontWeightTag = "'wght' ";
+
+ private void applySpacingAndFeatures(Paint paint, FontData font) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ double letterSpacing = font.letterSpacing;
+ paint.setLetterSpacing((float) (letterSpacing / (font.fontSize * mScale)));
+
+ final boolean allowOptionalLigatures =
+ letterSpacing == 0 && font.fontVariantLigatures == FontVariantLigatures.normal;
+
+ if (allowOptionalLigatures) {
+ paint.setFontFeatureSettings(
+ defaultFeatures + additionalLigatures + font.fontFeatureSettings);
+ } else {
+ paint.setFontFeatureSettings(
+ defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ paint.setFontVariationSettings(
+ fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings);
+ }
+ }
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ private Path getLinePath(String line, Paint paint, Canvas canvas) {
+ final int length = line.length();
+ final Path path = new Path();
+
+ emoji.clear();
+ emojiTransforms.clear();
+
+ if (length == 0) {
+ return path;
+ }
+
+ double pathLength = 0;
+ PathMeasure pm = null;
+ boolean isClosed = false;
+ final boolean hasTextPath = textPath != null;
+ if (hasTextPath) {
+ pm = new PathMeasure(textPath.getTextPath(canvas, paint), false);
+ pathLength = pm.getLength();
+ isClosed = pm.isClosed();
+ if (pathLength == 0) {
+ return path;
+ }
+ }
+
+ GlyphContext gc = getTextRootGlyphContext();
+ FontData font = gc.getFont();
+ applyTextPropertiesToPaint(paint, font);
+ GlyphPathBag bag = new GlyphPathBag(paint);
+ boolean[] ligature = new boolean[length];
+ final char[] chars = line.toCharArray();
+
+ /*
+ *
+ * Three properties affect the space between characters and words:
+ *
+ * ‘kerning’ indicates whether the user agent should adjust inter-glyph spacing
+ * based on kerning tables that are included in the relevant font
+ * (i.e., enable auto-kerning) or instead disable auto-kerning
+ * and instead set inter-character spacing to a specific length (typically, zero).
+ *
+ * ‘letter-spacing’ indicates an amount of space that is to be added between text
+ * characters supplemental to any spacing due to the ‘kerning’ property.
+ *
+ * ‘word-spacing’ indicates the spacing behavior between words.
+ *
+ * Letter-spacing is applied after bidi reordering and is in addition to any word-spacing.
+ * Depending on the justification rules in effect, user agents may further increase
+ * or decrease the space between typographic character units in order to justify text.
+ *
+ * */
+ double kerning = font.kerning;
+ double wordSpacing = font.wordSpacing;
+ double letterSpacing = font.letterSpacing;
+ final boolean autoKerning = !font.manualKerning;
+
+ /*
+ 11.1.2. Fonts and glyphs
+
+ A font consists of a collection of glyphs together with other information (collectively,
+ the font tables) necessary to use those glyphs to present characters on some visual medium.
+
+ The combination of the collection of glyphs and the font tables is called the font data.
+
+ A font may supply substitution and positioning tables that can be used by a formatter
+ (text shaper) to re-order, combine and position a sequence of glyphs to form one or more
+ composite glyphs.
+
+ The combining may be as simple as a ligature, or as complex as an indic syllable which
+ combines, usually with some re-ordering, multiple consonants and vowel glyphs.
+
+ The tables may be language dependent, allowing the use of language appropriate letter forms.
+
+ When a glyph, simple or composite, represents an indivisible unit for typesetting purposes,
+ it is know as a typographic character.
+
+ Ligatures are an important feature of advance text layout.
+
+ Some ligatures are discretionary while others (e.g. in Arabic) are required.
+
+ The following explicit rules apply to ligature formation:
+
+ Ligature formation should not be enabled when characters are in different DOM text nodes;
+ thus, characters separated by markup should not use ligatures.
+
+ Ligature formation should not be enabled when characters are in different text chunks.
+
+ Discretionary ligatures should not be used when the spacing between two characters is not
+ the same as the default space (e.g. when letter-spacing has a non-default value,
+ or text-align has a value of justify and text-justify has a value of distribute).
+ (See CSS Text Module Level 3, ([css-text-3]).
+
+ SVG attributes such as ‘dx’, ‘textLength’, and ‘spacing’ (in ‘textPath’) that may reposition
+ typographic characters do not break discretionary ligatures.
+
+ If discretionary ligatures are not desired
+ they can be turned off by using the font-variant-ligatures property.
+
+ /*
+ When the effective letter-spacing between two characters is not zero
+ (due to either justification or non-zero computed ‘letter-spacing’),
+ user agents should not apply optional ligatures.
+ https://www.w3.org/TR/css-text-3/#letter-spacing-property
+ */
+ final boolean allowOptionalLigatures =
+ letterSpacing == 0 && font.fontVariantLigatures == FontVariantLigatures.normal;
+
+ /*
+ For OpenType fonts, discretionary ligatures include those enabled by
+ the liga, clig, dlig, hlig, and cala features;
+ required ligatures are found in the rlig feature.
+ https://svgwg.org/svg2-draft/text.html#FontsGlyphs
+
+ http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
+
+ https://www.microsoft.com/typography/otspec/featurelist.htm
+ https://www.microsoft.com/typography/otspec/featuretags.htm
+ https://www.microsoft.com/typography/otspec/features_pt.htm
+ https://www.microsoft.com/typography/otfntdev/arabicot/features.aspx
+ http://unifraktur.sourceforge.net/testcases/enable_opentype_features/
+ https://en.wikipedia.org/wiki/List_of_typographic_features
+ http://ilovetypography.com/OpenType/opentype-features.html
+ https://www.typotheque.com/articles/opentype_features_in_css
+ https://practice.typekit.com/lesson/caring-about-opentype-features/
+ http://stateofwebtype.com/
+
+ 6.12. Low-level font feature settings control: the font-feature-settings property
+
+ Name: font-feature-settings
+ Value: normal | #
+ Initial: normal
+ Applies to: all elements
+ Inherited: yes
+ Percentages: N/A
+ Media: visual
+ Computed value: as specified
+ Animatable: no
+
+ https://drafts.csswg.org/css-fonts-3/#default-features
+
+ 7.1. Default features
+
+ For OpenType fonts, user agents must enable the default features defined in the OpenType
+ documentation for a given script and writing mode.
+
+ Required ligatures, common ligatures and contextual forms must be enabled by default
+ (OpenType features: rlig, liga, clig, calt),
+ along with localized forms (OpenType feature: locl),
+ and features required for proper display of composed characters and marks
+ (OpenType features: ccmp, mark, mkmk).
+
+ These features must always be enabled, even when the value of the ‘font-variant’ and
+ ‘font-feature-settings’ properties is ‘normal’.
+
+ Individual features are only disabled when explicitly overridden by the author,
+ as when ‘font-variant-ligatures’ is set to ‘no-common-ligatures’.
+
+ TODO For handling complex scripts such as Arabic, Mongolian or Devanagari additional features
+ are required.
+
+ TODO For upright text within vertical text runs,
+ vertical alternates (OpenType feature: vert) must be enabled.
+ */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // String arabic = "'isol', 'fina', 'medi', 'init', 'rclt', 'mset', 'curs', ";
+ if (allowOptionalLigatures) {
+ paint.setFontFeatureSettings(
+ defaultFeatures + additionalLigatures + font.fontFeatureSettings);
+ } else {
+ paint.setFontFeatureSettings(
+ defaultFeatures + disableDiscretionaryLigatures + font.fontFeatureSettings);
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ paint.setFontVariationSettings(
+ fontWeightTag + font.absoluteFontWeight + font.fontVariationSettings);
+ }
+ }
+ // OpenType.js font data
+ ReadableMap fontData = font.fontData;
+
+ float[] advances = new float[length];
+ paint.getTextWidths(line, advances);
+
+ /*
+ This would give both advances and textMeasure in one call / looping over the text
+ double textMeasure = paint.getTextRunAdvances(line, 0, length, 0, length, true, advances, 0);
+ */
+ /*
+ Determine the startpoint-on-the-path for the first glyph using attribute ‘startOffset’
+ and property text-anchor.
+
+ For text-anchor:start, startpoint-on-the-path is the point
+ on the path which represents the point on the path which is ‘startOffset’ distance
+ along the path from the start of the path, calculated using the user agent's distance
+ along the path algorithm.
+
+ For text-anchor:middle, startpoint-on-the-path is the point
+ on the path which represents the point on the path which is [ ‘startOffset’ minus half
+ of the total advance values for all of the glyphs in the ‘textPath’ element ] distance
+ along the path from the start of the path, calculated using the user agent's distance
+ along the path algorithm.
+
+ For text-anchor:end, startpoint-on-the-path is the point on
+ the path which represents the point on the path which is [ ‘startOffset’ minus the
+ total advance values for all of the glyphs in the ‘textPath’ element ].
+
+ Before rendering the first glyph, the horizontal component of the startpoint-on-the-path
+ is adjusted to take into account various horizontal alignment text properties and
+ attributes, such as a ‘dx’ attribute value on a ‘tspan’ element.
+ */
+ final TextAnchor textAnchor = font.textAnchor;
+ TextView anchorRoot = getTextAnchorRoot();
+ final double textMeasure = anchorRoot.getSubtreeTextChunksTotalAdvance(paint);
+ double offset = getTextAnchorOffset(textAnchor, textMeasure);
+
+ int side = 1;
+ double startOfRendering = 0;
+ double endOfRendering = pathLength;
+ double fontSize = gc.getFontSize();
+ boolean sharpMidLine = false;
+ if (hasTextPath) {
+ sharpMidLine = textPath.getMidLine() == TextPathMidLine.sharp;
+ /*
+ Name
+ side
+ Value
+ left | right
+ initial value
+ left
+ Animatable
+ yes
+
+ Determines the side of the path the text is placed on
+ (relative to the path direction).
+
+ Specifying a value of right effectively reverses the path.
+
+ Added in SVG 2 to allow text either inside or outside closed subpaths
+ and basic shapes (e.g. rectangles, circles, and ellipses).
+
+ Adding 'side' was resolved at the Sydney (2015) meeting.
+ */
+ side = textPath.getSide() == TextPathSide.right ? -1 : 1;
+ /*
+ Name
+ startOffset
+ Value
+ | |
+ initial value
+ 0
+ Animatable
+ yes
+
+ An offset from the start of the path for the initial current text position,
+ calculated using the user agent's distance along the path algorithm,
+ after converting the path to the ‘textPath’ element's coordinate system.
+
+ If a other than a percentage is given, then the ‘startOffset’
+ represents a distance along the path measured in the current user coordinate
+ system for the ‘textPath’ element.
+
+ If a percentage is given, then the ‘startOffset’ represents a percentage
+ distance along the entire path. Thus, startOffset="0%" indicates the start
+ point of the path and startOffset="100%" indicates the end point of the path.
+
+ Negative values and values larger than the path length (e.g. 150%) are allowed.
+
+ Any typographic characters with mid-points that are not on the path are not rendered
+
+ For paths consisting of a single closed subpath (including an equivalent path for a
+ basic shape), typographic characters are rendered along one complete circuit of the
+ path. The text is aligned as determined by the text-anchor property to a position
+ along the path set by the ‘startOffset’ attribute.
+
+ For the start (end) value, the text is rendered from the start (end) of the line
+ until the initial position along the path is reached again.
+
+ For the middle, the text is rendered from the middle point in both directions until
+ a point on the path equal distance in both directions from the initial position on
+ the path is reached.
+ */
+ final double absoluteStartOffset =
+ getAbsoluteStartOffset(textPath.getStartOffset(), pathLength, fontSize);
+ offset += absoluteStartOffset;
+ if (isClosed) {
+ final double halfPathDistance = pathLength / 2;
+ startOfRendering =
+ absoluteStartOffset + (textAnchor == TextAnchor.middle ? -halfPathDistance : 0);
+ endOfRendering = startOfRendering + pathLength;
+ }
+ /*
+ TextPathSpacing spacing = textPath.getSpacing();
+ if (spacing == TextPathSpacing.auto) {
+ // Hmm, what to do here?
+ // https://svgwg.org/svg2-draft/text.html#TextPathElementSpacingAttribute
+ }
+ */
+ }
+
+ /*
+ Name
+ method
+ Value
+ align | stretch
+ initial value
+ align
+ Animatable
+ yes
+ Indicates the method by which text should be rendered along the path.
+
+ A value of align indicates that the typographic character should be rendered using
+ simple 2×3 matrix transformations such that there is no stretching/warping of the
+ typographic characters. Typically, supplemental rotation, scaling and translation
+ transformations are done for each typographic characters to be rendered.
+
+ As a result, with align, in fonts where the typographic characters are designed to be
+ connected (e.g., cursive fonts), the connections may not align properly when text is
+ rendered along a path.
+
+ A value of stretch indicates that the typographic character outlines will be converted
+ into paths, and then all end points and control points will be adjusted to be along the
+ perpendicular vectors from the path, thereby stretching and possibly warping the glyphs.
+
+ With this approach, connected typographic characters, such as in cursive scripts,
+ will maintain their connections. (Non-vertical straight path segments should be
+ converted to Bézier curves in such a way that horizontal straight paths have an
+ (approximately) constant offset from the path along which the typographic characters
+ are rendered.)
+
+ TODO implement stretch
+ */
+
+ /*
+ Name Value Initial value Animatable
+ textLength | | See below yes
+
+ The author's computation of the total sum of all of the advance values that correspond
+ to character data within this element, including the advance value on the glyph
+ (horizontal or vertical), the effect of properties letter-spacing and word-spacing and
+ adjustments due to attributes ‘dx’ and ‘dy’ on this ‘text’ or ‘tspan’ element or any
+ descendants. This value is used to calibrate the user agent's own calculations with
+ that of the author.
+
+ The purpose of this attribute is to allow the author to achieve exact alignment,
+ in visual rendering order after any bidirectional reordering, for the first and
+ last rendered glyphs that correspond to this element; thus, for the last rendered
+ character (in visual rendering order after any bidirectional reordering),
+ any supplemental inter-character spacing beyond normal glyph advances are ignored
+ (in most cases) when the user agent determines the appropriate amount to expand/compress
+ the text string to fit within a length of ‘textLength’.
+
+ If attribute ‘textLength’ is specified on a given element and also specified on an
+ ancestor, the adjustments on all character data within this element are controlled by
+ the value of ‘textLength’ on this element exclusively, with the possible side-effect
+ that the adjustment ratio for the contents of this element might be different than the
+ adjustment ratio used for other content that shares the same ancestor. The user agent
+ must assume that the total advance values for the other content within that ancestor is
+ the difference between the advance value on that ancestor and the advance value for
+ this element.
+
+ This attribute is not intended for use to obtain effects such as shrinking or
+ expanding text.
+
+ A negative value is an error (see Error processing).
+
+ The ‘textLength’ attribute is only applied when the wrapping area is not defined by the
+ TODO shape-inside or the inline-size properties. It is also not applied for any ‘text’ or
+ TODO ‘tspan’ element that has forced line breaks (due to a white-space value of pre or
+ pre-line).
+
+ If the attribute is not specified anywhere within a ‘text’ element, the effect is as if
+ the author's computation exactly matched the value calculated by the user agent;
+ thus, no advance adjustments are made.
+ */
+ double scaleSpacingAndGlyphs = 1;
+ if (mTextLength != null) {
+ final double author =
+ PropHelper.fromRelative(mTextLength, canvas.getWidth(), 0, mScale, fontSize);
+ if (author < 0) {
+ throw new IllegalArgumentException("Negative textLength value");
+ }
+ switch (mLengthAdjust) {
+ default:
+ case spacing:
+ letterSpacing += (author - textMeasure) / (length - 1);
+ break;
+ case spacingAndGlyphs:
+ scaleSpacingAndGlyphs = author / textMeasure;
+ break;
+ }
+ }
+ final double scaledDirection = scaleSpacingAndGlyphs * side;
+
+ /*
+ https://developer.mozilla.org/en/docs/Web/CSS/vertical-align
+ https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6bsln.html
+ https://www.microsoft.com/typography/otspec/base.htm
+ http://apike.ca/prog_svg_text_style.html
+ https://www.w3schools.com/tags/canvas_textbaseline.asp
+ http://vanseodesign.com/web-design/svg-text-baseline-alignment/
+ https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align
+ https://tympanus.net/codrops/css_reference/vertical-align/
+
+ https://svgwg.org/svg2-draft/text.html#AlignmentBaselineProperty
+ 11.10.2.6. The ‘alignment-baseline’ property
+
+ This property is defined in the CSS Line Layout Module 3 specification. See 'alignment-baseline'. [css-inline-3]
+ https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
+
+ The vertical-align property shorthand should be preferred in new content.
+
+ SVG 2 introduces some changes to the definition of this property.
+ In particular: the values 'auto', 'before-edge', and 'after-edge' have been removed.
+ For backwards compatibility, 'text-before-edge' should be mapped to 'text-top' and
+ 'text-after-edge' should be mapped to 'text-bottom'.
+
+ Neither 'text-before-edge' nor 'text-after-edge' should be used with the vertical-align property.
+ */
+ final Paint.FontMetrics fm = paint.getFontMetrics();
+ final double descenderDepth = fm.descent;
+ final double bottom = descenderDepth + fm.leading;
+ final double ascenderHeight = -fm.ascent + fm.leading;
+ final double top = -fm.top;
+ final double totalHeight = top + bottom;
+ double baselineShift = 0;
+ String baselineShiftString = getBaselineShift();
+ AlignmentBaseline baseline = getAlignmentBaseline();
+ if (baseline != null) {
+ // TODO alignment-baseline, test / verify behavior
+ // TODO get per glyph baselines from font baseline table, for high-precision alignment
+ switch (baseline) {
+ // https://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
+ default:
+ case baseline:
+ // Use the dominant baseline choice of the parent.
+ // Match the box’s corresponding baseline to that of its parent.
+ baselineShift = 0;
+ break;
+
+ case textBottom:
+ case afterEdge:
+ case textAfterEdge:
+ // Match the bottom of the box to the bottom of the parent’s content area.
+ // text-after-edge = text-bottom
+ // text-after-edge = descender depth
+ baselineShift = -descenderDepth;
+ break;
+
+ case alphabetic:
+ // Match the box’s alphabetic baseline to that of its parent.
+ // alphabetic = 0
+ baselineShift = 0;
+ break;
+
+ case ideographic:
+ // Match the box’s ideographic character face under-side baseline to that of its parent.
+ // ideographic = descender depth
+ baselineShift = -descenderDepth;
+ break;
+
+ case middle:
+ // Align the vertical midpoint of the box with the baseline of the parent box plus half
+ // the x-height of the parent.
+ // middle = x height / 2
+ Rect bounds = new Rect();
+ // this will just retrieve the bounding rect for 'x'
+ paint.getTextBounds("x", 0, 1, bounds);
+ int xHeight = bounds.height();
+ baselineShift = xHeight / 2.0;
+ break;
+
+ case central:
+ // Match the box’s central baseline to the central baseline of its parent.
+ // central = (ascender height - descender depth) / 2
+ baselineShift = (ascenderHeight - descenderDepth) / 2;
+ break;
+
+ case mathematical:
+ // Match the box’s mathematical baseline to that of its parent.
+ // Hanging and mathematical baselines
+ // There are no obvious formulas to calculate the position of these baselines.
+ // At the time of writing FOP puts the hanging baseline at 80% of the ascender
+ // height and the mathematical baseline at 50%.
+ baselineShift = 0.5 * ascenderHeight;
+ break;
+
+ case hanging:
+ baselineShift = 0.8 * ascenderHeight;
+ break;
+
+ case textTop:
+ case beforeEdge:
+ case textBeforeEdge:
+ // Match the top of the box to the top of the parent’s content area.
+ // text-before-edge = text-top
+ // text-before-edge = ascender height
+ baselineShift = ascenderHeight;
+ break;
+
+ case bottom:
+ // Align the top of the aligned subtree with the top of the line box.
+ baselineShift = bottom;
+ break;
+
+ case center:
+ // Align the center of the aligned subtree with the center of the line box.
+ baselineShift = totalHeight / 2;
+ break;
+
+ case top:
+ // Align the bottom of the aligned subtree with the bottom of the line box.
+ baselineShift = top;
+ break;
+ }
+ }
+ /*
+ 2.2.2. Alignment Shift: baseline-shift longhand
+
+ This property specifies by how much the box is shifted up from its alignment point.
+ It does not apply when alignment-baseline is top or bottom.
+
+ Authors should use the vertical-align shorthand instead of this property.
+
+ Values have the following meanings:
+
+
+ Raise (positive value) or lower (negative value) by the specified length.
+
+ Raise (positive value) or lower (negative value) by the specified percentage of the line-height.
+ TODO sub
+ Lower by the offset appropriate for subscripts of the parent’s box.
+ (The UA should use the parent’s font data to find this offset whenever possible.)
+ TODO super
+ Raise by the offset appropriate for superscripts of the parent’s box.
+ (The UA should use the parent’s font data to find this offset whenever possible.)
+
+ User agents may additionally support the keyword baseline as computing to 0
+ if is necessary for them to support legacy SVG content.
+ Issue: We would prefer to remove this,
+ and are looking for feedback from SVG user agents as to whether it’s necessary.
+
+ https://www.w3.org/TR/css-inline-3/#propdef-baseline-shift
+ */
+ if (baselineShiftString != null && !baselineShiftString.isEmpty()) {
+ switch (baseline) {
+ case top:
+ case bottom:
+ break;
+
+ default:
+ switch (baselineShiftString) {
+ case "sub":
+ // TODO
+ if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) {
+ int unitsPerEm = fontData.getInt("unitsPerEm");
+ ReadableMap tables = fontData.getMap("tables");
+ if (tables.hasKey("os2")) {
+ ReadableMap os2 = tables.getMap("os2");
+ if (os2.hasKey("ySubscriptYOffset")) {
+ double subOffset = os2.getDouble("ySubscriptYOffset");
+ baselineShift += mScale * fontSize * subOffset / unitsPerEm;
+ }
+ }
+ }
+ break;
+
+ case "super":
+ // TODO
+ if (fontData != null && fontData.hasKey("tables") && fontData.hasKey("unitsPerEm")) {
+ int unitsPerEm = fontData.getInt("unitsPerEm");
+ ReadableMap tables = fontData.getMap("tables");
+ if (tables.hasKey("os2")) {
+ ReadableMap os2 = tables.getMap("os2");
+ if (os2.hasKey("ySuperscriptYOffset")) {
+ double superOffset = os2.getDouble("ySuperscriptYOffset");
+ baselineShift -= mScale * fontSize * superOffset / unitsPerEm;
+ }
+ }
+ }
+ break;
+
+ case "baseline":
+ break;
+
+ default:
+ baselineShift -=
+ PropHelper.fromRelative(baselineShiftString, mScale * fontSize, mScale, fontSize);
+ }
+ break;
+ }
+ }
+
+ final Matrix start = new Matrix();
+ final Matrix mid = new Matrix();
+ final Matrix end = new Matrix();
+
+ final float[] startPointMatrixData = new float[9];
+ final float[] endPointMatrixData = new float[9];
+
+ for (int index = 0; index < length; index++) {
+ char currentChar = chars[index];
+ String current = String.valueOf(currentChar);
+ boolean alreadyRenderedGraphemeCluster = ligature[index];
+
+ /*
+ Determine the glyph's charwidth (i.e., the amount which the current text position
+ advances horizontally when the glyph is drawn using horizontal text layout).
+ */
+ boolean hasLigature = false;
+ if (alreadyRenderedGraphemeCluster) {
+ current = "";
+ } else {
+ int nextIndex = index;
+ while (++nextIndex < length) {
+ float nextWidth = advances[nextIndex];
+ if (nextWidth > 0) {
+ break;
+ }
+ String nextLigature = current + chars[nextIndex];
+ ligature[nextIndex] = true;
+ current = nextLigature;
+ hasLigature = true;
+ }
+ }
+ double charWidth = paint.measureText(current) * scaleSpacingAndGlyphs;
+
+ /*
+ For each subsequent glyph, set a new startpoint-on-the-path as the previous
+ endpoint-on-the-path, but with appropriate adjustments taking into account
+ horizontal kerning tables in the font and current values of various attributes
+ and properties, including spacing properties (e.g. letter-spacing and word-spacing)
+ and ‘tspan’ elements with values provided for attributes ‘dx’ and ‘dy’. All
+ adjustments are calculated as distance adjustments along the path, calculated
+ using the user agent's distance along the path algorithm.
+ */
+ if (autoKerning) {
+ double kerned = advances[index] * scaleSpacingAndGlyphs;
+ kerning = kerned - charWidth;
+ }
+
+ boolean isWordSeparator = currentChar == ' ';
+ double wordSpace = isWordSeparator ? wordSpacing : 0;
+ double spacing = wordSpace + letterSpacing;
+ double advance = charWidth + spacing;
+
+ double x = gc.nextX(alreadyRenderedGraphemeCluster ? 0 : kerning + advance);
+ double y = gc.nextY();
+ double dx = gc.nextDeltaX();
+ double dy = gc.nextDeltaY();
+ double r = gc.nextRotation();
+
+ if (alreadyRenderedGraphemeCluster || isWordSeparator) {
+ // Skip rendering other grapheme clusters of ligatures (already rendered),
+ // But, make sure to increment index positions by making gc.next() calls.
+ continue;
+ }
+
+ advance *= side;
+ charWidth *= side;
+ double cursor = offset + (x + dx) * side;
+ double startPoint = cursor - advance;
+
+ if (hasTextPath) {
+ /*
+ Determine the point on the curve which is charwidth distance along the path from
+ the startpoint-on-the-path for this glyph, calculated using the user agent's
+ distance along the path algorithm. This point is the endpoint-on-the-path for
+ the glyph.
+ */
+ double endPoint = startPoint + charWidth;
+
+ /*
+ Determine the midpoint-on-the-path, which is the point on the path which is
+ "halfway" (user agents can choose either a distance calculation or a parametric
+ calculation) between the startpoint-on-the-path and the endpoint-on-the-path.
+ */
+ double halfWay = charWidth / 2;
+ double midPoint = startPoint + halfWay;
+
+ // Glyphs whose midpoint-on-the-path are off the path are not rendered.
+ if (midPoint > endOfRendering) {
+ continue;
+ } else if (midPoint < startOfRendering) {
+ continue;
+ }
+
+ /*
+ Determine the glyph-midline, which is the vertical line in the glyph's
+ coordinate system that goes through the glyph's x-axis midpoint.
+
+ Position the glyph such that the glyph-midline passes through
+ the midpoint-on-the-path and is perpendicular to the line
+ through the startpoint-on-the-path and the endpoint-on-the-path.
+
+ TODO suggest adding a compatibility mid-line rendering attribute to textPath,
+ for a chrome/firefox/opera/safari compatible sharp text path rendering,
+ which doesn't bend text smoothly along a right angle curve, (like Edge does)
+ but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
+ https://github.com/w3c/svgwg/issues/337
+ */
+ final int posAndTanFlags = POSITION_MATRIX_FLAG | TANGENT_MATRIX_FLAG;
+ if (sharpMidLine) {
+ pm.getMatrix((float) midPoint, mid, posAndTanFlags);
+ } else {
+ /*
+ In the calculation above, if either the startpoint-on-the-path
+ or the endpoint-on-the-path is off the end of the path,
+ then extend the path beyond its end points with a straight line
+ that is parallel to the tangent at the path at its end point
+ so that the midpoint-on-the-path can still be calculated.
+
+ TODO suggest change in wording of svg spec:
+ so that the midpoint-on-the-path can still be calculated.
+ to
+ so that the angle of the glyph-midline to the x-axis can still be calculated.
+ or
+ so that the line through the startpoint-on-the-path and the
+ endpoint-on-the-path can still be calculated.
+ https://github.com/w3c/svgwg/issues/337#issuecomment-318056199
+ */
+ if (startPoint < 0) {
+ pm.getMatrix(0, start, posAndTanFlags);
+ start.preTranslate((float) startPoint, 0);
+ } else {
+ pm.getMatrix((float) startPoint, start, POSITION_MATRIX_FLAG);
+ }
+
+ pm.getMatrix((float) midPoint, mid, POSITION_MATRIX_FLAG);
+
+ if (endPoint > pathLength) {
+ pm.getMatrix((float) pathLength, end, posAndTanFlags);
+ end.preTranslate((float) (endPoint - pathLength), 0);
+ } else {
+ pm.getMatrix((float) endPoint, end, POSITION_MATRIX_FLAG);
+ }
+
+ start.getValues(startPointMatrixData);
+ end.getValues(endPointMatrixData);
+
+ double startX = startPointMatrixData[MTRANS_X];
+ double startY = startPointMatrixData[MTRANS_Y];
+ double endX = endPointMatrixData[MTRANS_X];
+ double endY = endPointMatrixData[MTRANS_Y];
+
+ // line through the startpoint-on-the-path and the endpoint-on-the-path
+ double lineX = endX - startX;
+ double lineY = endY - startY;
+
+ double glyphMidlineAngle = Math.atan2(lineY, lineX);
+
+ mid.preRotate((float) (glyphMidlineAngle * radToDeg * side));
+ }
+
+ /*
+ Align the glyph vertically relative to the midpoint-on-the-path based on property
+ alignment-baseline and any specified values for attribute ‘dy’ on a ‘tspan’ element.
+ */
+ mid.preTranslate((float) -halfWay, (float) (dy + baselineShift));
+ mid.preScale((float) scaledDirection, (float) side);
+ mid.postTranslate(0, (float) y);
+ } else {
+ mid.setTranslate((float) startPoint, (float) (y + dy + baselineShift));
+ }
+
+ mid.preRotate((float) r);
+
+ Path glyph;
+ if (hasLigature) {
+ glyph = new Path();
+ paint.getTextPath(current, 0, current.length(), 0, 0, glyph);
+ } else {
+ glyph = bag.getOrCreateAndCache(currentChar, current);
+ }
+ RectF bounds = new RectF();
+ glyph.computeBounds(bounds, true);
+ float width = bounds.width();
+ if (width == 0) { // Render unicode emoji
+ canvas.save();
+ canvas.concat(mid);
+ emoji.add(current);
+ emojiTransforms.add(new Matrix(mid));
+ canvas.drawText(current, 0, 0, paint);
+ canvas.restore();
+ } else {
+ glyph.transform(mid);
+ path.addPath(glyph);
+ }
+ }
+
+ return path;
+ }
+
+ private double getAbsoluteStartOffset(SVGLength startOffset, double distance, double fontSize) {
+ return PropHelper.fromRelative(startOffset, distance, 0, mScale, fontSize);
+ }
+
+ private double getTextAnchorOffset(TextAnchor textAnchor, double textMeasure) {
+ switch (textAnchor) {
+ default:
+ case start:
+ return 0;
+
+ case middle:
+ return -textMeasure / 2;
+
+ case end:
+ return -textMeasure;
+ }
+ }
+
+ private void applyTextPropertiesToPaint(Paint paint, FontData font) {
+ boolean isBold = font.fontWeight == FontWeight.Bold || font.absoluteFontWeight >= 550;
+ boolean isItalic = font.fontStyle == FontStyle.italic;
+
+ int style;
+ if (isBold && isItalic) {
+ style = Typeface.BOLD_ITALIC;
+ } else if (isBold) {
+ style = Typeface.BOLD;
+ } else if (isItalic) {
+ style = Typeface.ITALIC;
+ } else {
+ style = Typeface.NORMAL;
+ }
+
+ Typeface typeface = null;
+ int weight = font.absoluteFontWeight;
+ final String fontFamily = font.fontFamily;
+ if (fontFamily != null && fontFamily.length() > 0) {
+ String otfpath = FONTS + fontFamily + OTF;
+ String ttfpath = FONTS + fontFamily + TTF;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ Typeface.Builder builder = new Typeface.Builder(assets, otfpath);
+ builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings);
+ builder.setWeight(weight);
+ builder.setItalic(isItalic);
+ typeface = builder.build();
+ if (typeface == null) {
+ builder = new Typeface.Builder(assets, ttfpath);
+ builder.setFontVariationSettings("'wght' " + weight + font.fontVariationSettings);
+ builder.setWeight(weight);
+ builder.setItalic(isItalic);
+ typeface = builder.build();
+ }
+ } else {
+ try {
+ typeface = Typeface.createFromAsset(assets, otfpath);
+ typeface = Typeface.create(typeface, style);
+ } catch (Exception ignored) {
+ try {
+ typeface = Typeface.createFromAsset(assets, ttfpath);
+ typeface = Typeface.create(typeface, style);
+ } catch (Exception ignored2) {
+ }
+ }
+ }
+ }
+
+ if (typeface == null) {
+ try {
+ typeface = ReactFontManager.getInstance().getTypeface(fontFamily, style, assets);
+ } catch (Exception ignored) {
+ }
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ typeface = Typeface.create(typeface, weight, isItalic);
+ }
+
+ paint.setLinearText(true);
+ paint.setSubpixelText(true);
+ paint.setTypeface(typeface);
+ paint.setTextSize((float) (font.fontSize * mScale));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ paint.setLetterSpacing(0);
+ }
+ }
+
+ private void setupTextPath() {
+ ViewParent parent = getParent();
+
+ while (parent != null) {
+ if (parent.getClass() == TextPathView.class) {
+ textPath = (TextPathView) parent;
+ break;
+ } else if (!(parent instanceof TextView)) {
+ break;
+ }
+
+ parent = parent.getParent();
+ }
+ }
+
+ @Override
+ int hitTest(final float[] src) {
+ if (mContent == null) {
+ return super.hitTest(src);
+ }
+ if (mPath == null || !mInvertible || !mTransformInvertible) {
+ return -1;
+ }
+
+ float[] dst = new float[2];
+ mInvMatrix.mapPoints(dst, src);
+ mInvTransform.mapPoints(dst);
+ int x = Math.round(dst[0]);
+ int y = Math.round(dst[1]);
+
+ initBounds();
+
+ if ((mRegion == null || !mRegion.contains(x, y))
+ && (mStrokeRegion == null || !mStrokeRegion.contains(x, y))) {
+ return -1;
+ }
+
+ Path clipPath = getClipPath();
+ if (clipPath != null) {
+ if (!mClipRegion.contains(x, y)) {
+ return -1;
+ }
+ }
+
+ return getId();
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextLayoutAlgorithm.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextLayoutAlgorithm.java
new file mode 100644
index 0000000000000..b3328df5def75
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextLayoutAlgorithm.java
@@ -0,0 +1,1294 @@
+package com.horcrux.svg;
+
+// TODO implement https://www.w3.org/TR/SVG2/text.html#TextLayoutAlgorithm
+
+import static com.horcrux.svg.TextProperties.Direction;
+import static com.horcrux.svg.TextProperties.TextAnchor;
+import static com.horcrux.svg.TextProperties.TextPathSide;
+
+import android.graphics.Path;
+import android.graphics.PathMeasure;
+import android.graphics.PointF;
+import android.view.View;
+import java.util.ArrayList;
+
+@SuppressWarnings("ALL")
+class TextLayoutAlgorithm {
+ class CharacterInformation {
+ int index;
+ double x = 0;
+ double y = 0;
+ double advance;
+ char character;
+ double rotate = 0;
+ TextView element;
+ boolean hidden = false;
+ boolean middle = false;
+ boolean resolved = false;
+ boolean xSpecified = false;
+ boolean ySpecified = false;
+ boolean addressable = true;
+ boolean anchoredChunk = false;
+ boolean rotateSpecified = false;
+ boolean firstCharacterInResolvedDescendant = false;
+
+ CharacterInformation(int index, char c) {
+ this.index = index;
+ this.character = c;
+ }
+ }
+
+ class LayoutInput {
+ TextView text;
+ boolean horizontal;
+ }
+
+ private void getSubTreeTypographicCharacterPositions(
+ ArrayList inTextPath,
+ ArrayList subtree,
+ StringBuilder line,
+ View node,
+ TextPathView textPath) {
+ if (node instanceof TSpanView) {
+ final TSpanView tSpanView = (TSpanView) node;
+ String content = tSpanView.mContent;
+ if (content == null) {
+ for (int i = 0; i < tSpanView.getChildCount(); i++) {
+ getSubTreeTypographicCharacterPositions(
+ inTextPath, subtree, line, tSpanView.getChildAt(i), textPath);
+ }
+ } else {
+ for (int i = 0; i < content.length(); i++) {
+ subtree.add(tSpanView);
+ inTextPath.add(textPath);
+ }
+ line.append(content);
+ }
+ } else {
+ textPath = node instanceof TextPathView ? (TextPathView) node : textPath;
+ for (int i = 0; i < textPath.getChildCount(); i++) {
+ getSubTreeTypographicCharacterPositions(
+ inTextPath, subtree, line, textPath.getChildAt(i), textPath);
+ }
+ }
+ }
+
+ CharacterInformation[] layoutText(LayoutInput layoutInput) {
+ /*
+ Setup
+
+ Let root be the result of generating
+ typographic character positions for the
+ ‘text’ element and its subtree, laid out as if it
+ were an absolutely positioned element.
+
+ This will be a single line of text unless the
+ white-space property causes line breaks.
+ */
+ TextView text = layoutInput.text;
+ StringBuilder line = new StringBuilder();
+ ArrayList subtree = new ArrayList<>();
+ ArrayList inTextPath = new ArrayList<>();
+ getSubTreeTypographicCharacterPositions(inTextPath, subtree, line, text, null);
+ final char[] root = line.toString().toCharArray();
+ /*
+ Let count be the number of DOM characters
+ within the ‘text’ element's subtree.
+ */
+ int count = root.length;
+ /*
+
+ Let result be an array of length count
+ whose entries contain the per-character information described
+ above. Each entry is initialized as follows:
+
+ its global index number equal to its position in the array,
+ its "x" coordinate set to "unspecified",
+ its "y" coordinate set to "unspecified",
+ its "rotate" coordinate set to "unspecified",
+ its "hidden" flag is false,
+ its "addressable" flag is true,
+ its "middle" flag is false,
+ its "anchored chunk" flag is false.
+ */
+ final CharacterInformation[] result = new CharacterInformation[count];
+ for (int i = 0; i < count; i++) {
+ result[i] = new CharacterInformation(i, root[i]);
+ }
+ /*
+ If result is empty, then return result.
+ */
+ if (count == 0) {
+ return result;
+ }
+ /*
+
+ Let CSS_positions be an array of length
+ count whose entries will be filled with the
+ x and y positions of the corresponding
+ typographic character in root. The array
+ entries are initialized to (0, 0).
+ */
+ PointF[] CSS_positions = new PointF[count];
+ for (int i = 0; i < count; i++) {
+ CSS_positions[i] = new PointF(0, 0);
+ }
+ /*
+ Let "horizontal" be a flag, true if the writing mode of ‘text’
+ is horizontal, false otherwise.
+ */
+ final boolean horizontal = true;
+ /*
+ Set flags and assign initial positions
+
+ For each array element with index i in
+ result:
+ */
+ for (int i = 0; i < count; i++) {
+ /*
+ TODO Set addressable to false if the character at index i was:
+
+ part of the text content of a non-rendered element
+
+ discarded during layout due to being a
+ collapsed
+ white space character, a soft hyphen character, or a
+ bidi control character; or
+
+
+ discarded during layout due to being a
+ collapsed
+ segment break; or
+
+
+ trimmed
+ from the start or end of a line.
+
+ Since there is collapsible white space not addressable by glyph
+ positioning attributes in the following ‘text’ element
+ (with a standard font), the "B" glyph will be placed at x=300.
+
+
+ A
+ B
+
+
+ This is because the white space before the "A", and all but one white space
+ character between the "A" and "B", is collapsed away or trimmed.
+
+ */
+ result[i].addressable = true;
+ /*
+
+ Set middle to true if the character at index i
+ TODO is the second or later character that corresponds to a typographic character.
+ */
+ result[i].middle = false;
+ /*
+
+ TODO If the character at index i corresponds to a typographic character at the beginning of a line, then set the "anchored
+ chunk" flag of result[i] to true.
+
+ This ensures chunks shifted by text-anchor do not
+ span multiple lines.
+ */
+ result[i].anchoredChunk = i == 0;
+ /*
+
+ If addressable is true and middle is false then
+ set CSS_positions[i] to the position of the
+ TODO corresponding typographic character as determined by the CSS
+ renderer. Otherwise, if i > 0, then set
+ CSS_positions[i] =
+ CSS_positions[i − 1]
+
+ */
+ if (result[i].addressable && !result[i].middle) {
+ CSS_positions[i].set(0, 0);
+ } else if (i > 0) {
+ CSS_positions[i].set(CSS_positions[i - 1]);
+ }
+ }
+ /*
+
+ Resolve character positioning
+
+ Position adjustments (e.g values in a ‘x’ attribute)
+ specified by a node apply to all characters in that node including
+ characters in the node's descendants. Adjustments specified in
+ descendant nodes, however, override adjustments from ancestor
+ nodes. This section resolves which adjustments are to be applied to
+ which characters. It also directly sets the rotate coordinate
+ of result.
+
+ Set up:
+
+ Let resolve_x, resolve_y,
+ resolve_dx, and resolve_dy be arrays of
+ length count whose entries are all initialized
+ to "unspecified".
+ */
+ String[] resolve_x = new String[count];
+ String[] resolve_y = new String[count];
+ String[] resolve_dx = new String[count];
+ String[] resolve_dy = new String[count];
+ /*
+
+ Set "in_text_path" flag false.
+
+ This flag will allow ‘y’ (‘x’)
+ attribute values to be ignored for horizontal (vertical)
+ text inside ‘textPath’ elements.
+ */
+ boolean in_text_path = false;
+ /*
+ Call the following procedure with the ‘text’ element node.
+
+ Procedure: resolve character
+ positioning:
+
+ A recursive procedure that takes as input a node and
+ whose steps are as follows:
+ */
+ class CharacterPositioningResolver {
+ private int global = 0;
+ private boolean horizontal = true;
+ private boolean in_text_path = false;
+ private CharacterInformation[] result;
+ private String[] resolve_x;
+ private String[] resolve_y;
+ private String[] resolve_dx;
+ private String[] resolve_dy;
+
+ private CharacterPositioningResolver(
+ CharacterInformation[] result,
+ String[] resolve_x,
+ String[] resolve_y,
+ String[] resolve_dx,
+ String[] resolve_dy) {
+ this.result = result;
+ this.resolve_x = resolve_x;
+ this.resolve_y = resolve_y;
+ this.resolve_dx = resolve_dx;
+ this.resolve_dy = resolve_dy;
+ }
+
+ private void resolveCharacterPositioning(TextView node) {
+ /*
+ If node is a ‘text’ or ‘tspan’ node:
+ */
+ if (node.getClass() == TextView.class || node.getClass() == TSpanView.class) {
+ /*
+ Let index equal the "global index number" of the
+ first character in the node.
+ */
+ int index = global;
+ /*
+ Let x, y, dx, dy
+ and rotate be the lists of values from the
+ TODO corresponding attributes on node, or empty
+ lists if the corresponding attribute was not specified
+ or was invalid.
+ */
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementXAttribute
+ String[] x = new String[] {};
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementYAttribute
+ String[] y = new String[] {};
+
+ // Current SVGLengthList
+ // https://www.w3.org/TR/SVG/types.html#DataTypeLengths
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementDXAttribute
+ String[] dx = new String[] {};
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementDYAttribute
+ String[] dy = new String[] {};
+
+ // Current SVGLengthList
+ // https://www.w3.org/TR/SVG/types.html#DataTypeNumbers
+
+ // https://www.w3.org/TR/SVG/text.html#TSpanElementRotateAttribute
+ double[] rotate = new double[] {};
+ /*
+
+ If "in_text_path" flag is false:
+ Let new_chunk_count
+ = max(length of x, length of y).
+ */
+ int new_chunk_count;
+ if (!in_text_path) {
+ new_chunk_count = Math.max(x.length, y.length);
+ /*
+
+ Else:
+ */
+ } else {
+ /*
+ If the "horizontal" flag is true:
+
+ Let new_chunk_count = length of x.
+ */
+ if (horizontal) {
+ new_chunk_count = x.length;
+ /*
+
+ Else:
+
+ Let new_chunk_count = length of y.
+ */
+ } else {
+ new_chunk_count = y.length;
+ }
+ }
+ /*
+
+ Let length be the number of DOM characters in the
+ subtree rooted at node.
+ */
+ String content = ((TSpanView) node).mContent;
+ int length = content == null ? 0 : content.length();
+ /*
+ Let i = 0 and j = 0.
+
+ i is an index of addressable characters in the node;
+ j is an index of all characters in the node.
+ */
+ int i = 0;
+ int j = 0;
+ /*
+ While j < length, do:
+ */
+ while (j < length) {
+ /*
+ This loop applies the ‘x’, ‘y’,
+ ‘dx’, ‘dy’ and ‘rotate’
+ attributes to the content inside node.
+ If the "addressable" flag of result[index +
+ j] is true, then:
+ */
+ if (result[index + j].addressable) {
+ /*
+ If i < TODO new_check_count, then (typo)
+ set the "anchored chunk" flag of
+ result[index + j] to
+ true. Else set the flag to false.
+
+ Setting the flag to false ensures that ‘x’
+ and ‘y’ attributes set in a ‘text’
+ element don't create anchored chunk in a ‘textPath’
+ element when they should not.
+ */
+ result[index + j].anchoredChunk = i < new_chunk_count;
+ /*
+
+ If i < length of x,
+ then set resolve_x[index
+ + j] to x[i].
+ */
+ if (i < x.length) {
+ resolve_x[index + j] = x[i];
+ }
+ /*
+
+ If "in_text_path" flag is true and the "horizontal"
+ flag is false, unset
+ resolve_x[index].
+
+ The ‘x’ attribute is ignored for
+ vertical text on a path.
+ */
+ if (in_text_path && !horizontal) {
+ resolve_x[index] = "";
+ }
+ /*
+
+ If i < length of y,
+ then set resolve_y[index
+ + j] to y[i].
+ */
+ if (i < y.length) {
+ resolve_y[index + j] = y[i];
+ }
+ /*
+ If "in_text_path" flag is true and the "horizontal"
+ flag is true, unset
+ resolve_y[index].
+
+ The ‘y’ attribute is ignored for
+ horizontal text on a path.
+ */
+ if (in_text_path && horizontal) {
+ resolve_y[index] = "";
+ }
+ /*
+ If i < length of dx,
+ then set resolve_dx[index
+ + j] to TODO dy[i]. (typo)
+ */
+ if (i < dx.length) {
+ resolve_dx[index + j] = dx[i];
+ }
+ /*
+ If i < length of dy,
+ then set resolve_dy[index
+ + j] to dy[i].
+ */
+ if (i < dy.length) {
+ resolve_dy[index + j] = dy[i];
+ }
+ /*
+ If i < length of rotate,
+ then set the angle value of result[index
+ + j] to rotate[i].
+ Otherwise, if rotate is not empty, then
+ set result[index + j]
+ to result[index + j − 1].
+ */
+ if (i < rotate.length) {
+ result[index + j].rotate = rotate[i];
+ } else if (rotate.length != 0) {
+ result[index + j].rotate = result[index + j - 1].rotate;
+ }
+ /*
+ Set i = i + 1.
+ Set j = j + 1.
+ */
+ }
+ i++;
+ j++;
+ }
+ /*
+ If node is a ‘textPath’ node:
+
+ Let index equal the global index number of the
+ first character in the node (including descendant nodes).
+ */
+ } else if (node.getClass() == TextPathView.class) {
+ int index = global;
+ /*
+ Set the "anchored chunk" flag of result[index]
+ to true.
+
+ A ‘textPath’ element always creates an anchored chunk.
+ */
+ result[index].anchoredChunk = true;
+ /*
+ Set in_text_path flag true.
+ */
+ in_text_path = true;
+ /*
+ For each child node child of node:
+ Resolve glyph
+ positioning of child.
+ */
+ for (int child = 0; child < node.getChildCount(); child++) {
+ resolveCharacterPositioning((TextView) node.getChildAt(child));
+ }
+ /*
+ If node is a ‘textPath’ node:
+
+ Set "in_text_path" flag false.
+
+ */
+ if (node instanceof TextPathView) {
+ in_text_path = false;
+ }
+ }
+ }
+ }
+
+ CharacterPositioningResolver resolver =
+ new CharacterPositioningResolver(result, resolve_x, resolve_y, resolve_dx, resolve_dy);
+ /*
+ Adjust positions: dx, dy
+
+ The ‘dx’ and ‘dy’ adjustments are applied
+ before adjustments due to the ‘textLength’ attribute while
+ the ‘x’, ‘y’ and ‘rotate’
+ adjustments are applied after.
+
+ Let shift be the cumulative x and
+ y shifts due to ‘x’ and ‘y’
+ attributes, initialized to (0,0).
+ */
+ PointF shift = new PointF(0, 0);
+ /*
+ For each array element with index i in result:
+ */
+ for (int i = 0; i < count; i++) {
+ /*
+ If resolve_x[i] is unspecified, set it to 0.
+ If resolve_y[i] is unspecified, set it to 0.
+ */
+ if (resolve_x[i].equals("")) {
+ resolve_x[i] = "0";
+ }
+ if (resolve_y[i].equals("")) {
+ resolve_y[i] = "0";
+ }
+ /*
+ Let shift.x = shift.x + resolve_x[i]
+ and shift.y = shift.y + resolve_y[i].
+ */
+ shift.x = shift.x + Float.parseFloat(resolve_x[i]);
+ shift.y = shift.y + Float.parseFloat(resolve_y[i]);
+ /*
+ Let result[i].x = CSS_positions[i].x + shift.x
+ and result[i].y = CSS_positions[i].y + shift.y.
+ */
+ result[i].x = CSS_positions[i].x + shift.x;
+ result[i].y = CSS_positions[i].y + shift.y;
+ }
+ /*
+ TODO Apply ‘textLength’ attribute
+
+ Set up:
+
+ Define resolved descendant node as a
+ descendant of node with a valid ‘textLength’
+ attribute that is not itself a descendant node of a
+ descendant node that has a valid ‘textLength’
+ attribute.
+
+ Call the following procedure with the ‘text’ element
+ node.
+
+ Procedure: resolve text length:
+
+ A recursive procedure that takes as input
+ a node and whose steps are as follows:
+ For each child node child of node:
+
+ Resolve text length of child.
+
+ Child nodes are adjusted before parent nodes.
+ */
+ class TextLengthResolver {
+ int global;
+
+ private void resolveTextLength(TextView node) {
+ /*
+
+ If node is a ‘text’ or ‘tspan’ node
+ and if the node has a valid ‘textLength’ attribute value:
+ */
+ final Class extends TextView> nodeClass = node.getClass();
+ final boolean validTextLength = node.mTextLength != null;
+ if ((nodeClass == TSpanView.class) && validTextLength) {
+ /*
+ Let a = +∞ and b = −∞.
+ */
+ double a = Double.POSITIVE_INFINITY;
+ double b = Double.NEGATIVE_INFINITY;
+ /*
+
+
+ Let i and j be the global
+ index of the first character and last characters
+ in node, respectively.
+ */
+ String content = ((TSpanView) node).mContent;
+ int i = global;
+ int j = i + (content == null ? 0 : content.length());
+ /*
+ For each index k in the range
+ [i, j] where the "addressable" flag
+ of result[k] is true:
+
+ This loop finds the left-(top-) most and
+ right-(bottom-) most extents of the typographic characters within the node and checks for
+ forced line breaks.
+ */
+ for (int k = i; k <= j; k++) {
+ if (!result[i].addressable) {
+ continue;
+ }
+ /*
+ If the character at k is a linefeed
+ or carriage return, return. No adjustments due to
+ ‘textLength’ are made to a node with
+ a forced line break.
+ */
+ switch (result[i].character) {
+ case '\n':
+ case '\r':
+ return;
+ }
+ /*
+ Let pos = the x coordinate of the position
+ in result[k], if the "horizontal"
+ flag is true, and the y coordinate otherwise.
+ */
+ double pos = horizontal ? result[k].x : result[k].y;
+ /*
+ Let advance = the advance of
+ the typographic character corresponding to
+ character k. [NOTE: This advance will be
+ negative for RTL horizontal text.]
+ */
+ double advance = result[k].advance;
+ /*
+ Set a =
+ min(a, pos, pos
+ + advance).
+
+
+ Set b =
+ max(b, pos, pos
+ + advance).
+ */
+ a = Math.min(a, Math.min(pos, pos + advance));
+ b = Math.max(b, Math.max(pos, pos + advance));
+ }
+ /*
+
+ If a ≠+∞ then:
+
+ */
+ if (a != Double.POSITIVE_INFINITY) {
+ /*
+
+ Find the distance delta = ‘textLength’
+ computed value − (b − a).
+ */
+ double delta = node.mTextLength.value - (b - a);
+ /*
+
+ User agents are required to shift the last
+ typographic character in the node by
+ delta, in the positive x direction
+ if the "horizontal" flag is true and if
+ direction is
+ lrt, in the
+ negative x direction if the "horizontal" flag
+ is true and direction is
+ rtl, or in the
+ positive y direction otherwise. User agents
+ are free to adjust intermediate
+ typographic characters for optimal
+ typography. The next steps indicate one way to
+ adjust typographic characters when
+ the value of ‘lengthAdjust’ is
+ spacing.
+
+ Find n, the total number of
+ typographic characters in this node
+ TODO including any descendant nodes that are not resolved
+ descendant nodes or within a resolved descendant
+ node.
+ */
+ int n = 0;
+ int resolvedDescendantNodes = 0;
+ for (int c = 0; c < node.getChildCount(); c++) {
+ if (((TextPathView) node.getChildAt(c)).mTextLength == null) {
+ String ccontent = ((TSpanView) node).mContent;
+ n += ccontent == null ? 0 : ccontent.length();
+ } else {
+ result[n].firstCharacterInResolvedDescendant = true;
+ resolvedDescendantNodes++;
+ }
+ }
+ /*
+ Let n = n + number of
+ resolved descendant nodes − 1.
+ */
+ n += resolvedDescendantNodes - 1;
+ /*
+ Each resolved descendant node is treated as if it
+ were a single
+ typographic character in this
+ context.
+
+ Find the per-character adjustment δ
+ = delta/n.
+
+ Let shift = 0.
+ */
+ double perCharacterAdjustment = delta / n;
+ double shift = 0;
+ /*
+ For each index k in the range [i,j]:
+ */
+ for (int k = i; k <= j; k++) {
+ /*
+ Add shift to the x coordinate of the
+ position in result[k], if the "horizontal"
+ flag is true, and to the y coordinate
+ otherwise.
+ */
+ if (horizontal) {
+ result[k].x += shift;
+ } else {
+ result[k].y += shift;
+ }
+ /*
+ If the "middle" flag for result[k]
+ is not true and k is not a character in
+ a resolved descendant node other than the first
+ character then shift = shift
+ + δ.
+ */
+ if (!result[k].middle
+ && (!result[k].resolved || result[k].firstCharacterInResolvedDescendant)) {
+ shift += perCharacterAdjustment;
+ }
+ }
+ }
+ }
+ }
+ }
+ TextLengthResolver lengthResolver = new TextLengthResolver();
+ lengthResolver.resolveTextLength(text);
+ /*
+
+ Adjust positions: x, y
+
+ This loop applies ‘x’ and ‘y’ values,
+ and ensures that text-anchor chunks do not start in
+ the middle of a typographic character.
+
+ Let shift be the current adjustment due to
+ the ‘x’ and ‘y’ attributes,
+ initialized to (0,0).
+
+ Set index = 1.
+ */
+ shift.set(0, 0);
+ int index = 1;
+ /*
+ While index < count:
+ */
+ while (index < count) {
+ /*
+ TODO If resolved_x[index] is set, then let (typo)
+ shift.x =
+ resolved_x[index] −
+ result.x[index].
+ */
+ if (resolve_x[index] != null) {
+ shift.x = (float) (Double.parseDouble(resolve_x[index]) - result[index].x);
+ }
+ /*
+ TODO If resolved_y[index] is set, then let (typo)
+ shift.y =
+ resolved_y[index] −
+ result.y[index].
+ */
+ if (resolve_y[index] != null) {
+ shift.y = (float) (Double.parseDouble(resolve_y[index]) - result[index].y);
+ }
+ /*
+ Let result.x[index] =
+ result.x[index] + shift.x
+ and result.y[index] =
+ result.y[index] + shift.y.
+ */
+ result[index].x += shift.x;
+ result[index].y += shift.y;
+ /*
+ If the "middle" and "anchored chunk" flags
+ of result[index] are both true, then:
+ */
+ if (result[index].middle && result[index].anchoredChunk) {
+ /*
+ Set the "anchored chunk" flag
+ of result[index] to false.
+ */
+ result[index].anchoredChunk = false;
+ }
+ /*
+
+ If index + 1 < count, then set
+ the "anchored chunk" flag
+ of result[index + 1] to true.
+ */
+ if (index + 1 < count) {
+ result[index + 1].anchoredChunk = true;
+ }
+ /*
+ Set index to index + 1.
+ */
+ index++;
+ }
+ /*
+
+ Apply anchoring
+
+ TODO For each slice result[i..j]
+ (inclusive of both i and j), where:
+
+ the "anchored chunk" flag of result[i]
+ is true,
+
+ the "anchored chunk" flags
+ of result[k] where i
+ < k ≤ j are false, and
+
+ j = count − 1 or the "anchored
+ chunk" flag of result[j + 1] is
+ true;
+ do:
+
+ This loops over each anchored chunk.
+
+ Let a = +∞ and b = −∞.
+
+ For each index k in the range
+ [i, j] where the "addressable" flag
+ of result[k] is true:
+
+ This loop finds the left-(top-) most and
+ right-(bottom-) most extents of the typographic character within the anchored chunk.
+ */
+ int i = 0;
+ double a = Double.POSITIVE_INFINITY;
+ double b = Double.NEGATIVE_INFINITY;
+ double prevA = Double.POSITIVE_INFINITY;
+ double prevB = Double.NEGATIVE_INFINITY;
+ for (int k = 0; k < count; k++) {
+ if (!result[k].addressable) {
+ continue;
+ }
+ if (result[k].anchoredChunk) {
+ prevA = a;
+ prevB = b;
+ a = Double.POSITIVE_INFINITY;
+ b = Double.NEGATIVE_INFINITY;
+ }
+ /*
+ Let pos = the x coordinate of the position
+ in result[k], if the "horizontal" flag
+ is true, and the y coordinate otherwise.
+
+ Let advance = the advance of
+ the typographic character corresponding to
+ character k. [NOTE: This advance will be
+ negative for RTL horizontal text.]
+
+ Set a =
+ min(a, pos, pos
+ + advance).
+
+ Set b =
+ max(b, pos, pos
+ + advance).
+ */
+ double pos = horizontal ? result[k].x : result[k].y;
+ double advance = result[k].advance;
+ a = Math.min(a, Math.min(pos, pos + advance));
+ b = Math.max(b, Math.max(pos, pos + advance));
+ /*
+ If a ≠+∞, then:
+
+ Here we perform the text anchoring.
+
+ Let shift be the x coordinate of
+ result[i], if the "horizontal" flag
+ is true, and the y coordinate otherwise.
+
+ TODO Adjust shift based on the value of text-anchor
+ TODO and direction of the element the character at
+ index i is in:
+
+ (start, ltr) or (end, rtl)
+ Set shift = shift − a.
+ (start, rtl) or (end, ltr)
+ Set shift = shift − b.
+ (middle, ltr) or (middle, rtl)
+ Set shift = shift − (a + b) / 2.
+ */
+ if ((k > 0 && result[k].anchoredChunk && prevA != Double.POSITIVE_INFINITY)
+ || k == count - 1) {
+ TextAnchor anchor = TextAnchor.start;
+ Direction direction = Direction.ltr;
+
+ if (k == count - 1) {
+ prevA = a;
+ prevB = b;
+ }
+
+ double anchorShift = horizontal ? result[i].x : result[i].y;
+ switch (anchor) {
+ case start:
+ if (direction == Direction.ltr) {
+ anchorShift = anchorShift - prevA;
+ } else {
+ anchorShift = anchorShift - prevB;
+ }
+ break;
+
+ case middle:
+ if (direction == Direction.ltr) {
+ anchorShift = anchorShift - (prevA + prevB) / 2;
+ } else {
+ anchorShift = anchorShift - (prevA + prevB) / 2;
+ }
+ break;
+
+ case end:
+ if (direction == Direction.ltr) {
+ anchorShift = anchorShift - prevB;
+ } else {
+ anchorShift = anchorShift - prevA;
+ }
+ break;
+ }
+ /*
+ For each index k in the range [i, j]:
+
+ Add shift to the x coordinate of the position
+ in result[k], if the "horizontal"
+ flag is true, and to the y coordinate otherwise.
+ */
+ int j = k == count - 1 ? k : k - 1;
+ for (int r = i; r <= j; r++) {
+ if (horizontal) {
+ result[r].x += anchorShift;
+ } else {
+ result[r].y += anchorShift;
+ }
+ }
+
+ i = k;
+ }
+ }
+ /*
+
+ Position on path
+
+ Set index = 0.
+
+ Set the "in path" flag to false.
+
+ Set the "after path" flag to false.
+
+ Let path_end be an offset for characters that follow
+ a ‘textPath’ element. Set path_end to (0,0).
+
+ While index < count:
+ */
+ index = 0;
+ boolean inPath = false;
+ boolean afterPath = false;
+ PointF path_end = new PointF(0, 0);
+ Path textPath = null;
+ PathMeasure pm = new PathMeasure();
+ while (index < count) {
+ /*
+ If the character at index i is within a
+ ‘textPath’ element and corresponds to a typographic character, then:
+
+ Set "in path" flag to true.
+ */
+ final TextPathView textPathView = inTextPath.get(index);
+ if (textPathView != null && result[index].addressable) {
+ textPath = textPathView.getTextPath(null, null);
+ inPath = true;
+ /*
+
+ If the "middle" flag of
+ result[index] is false, then:
+ */
+ if (!result[index].middle) {
+ /*
+ Here we apply ‘textPath’ positioning.
+
+ Let path be the equivalent path of
+ the basic shape element referenced by
+ the ‘textPath’ element, or an empty path if
+ the reference is invalid.
+
+ If the ‘side’ attribute of
+ the ‘textPath’ element is
+ 'right', then
+ TODO reverse path.
+ */
+ Path path = textPath;
+ if (textPathView.getSide() == TextPathSide.right) {}
+
+ /*
+ Let length be the length
+ of path.
+ */
+ pm.setPath(path, false);
+ double length = pm.getLength();
+ /*
+ Let offset be the value of the
+ ‘textPath’ element's
+ ‘startOffset’ attribute, adjusted
+ due to any ‘pathLength’ attribute on the
+ referenced element (if the referenced element is
+ a ‘path’ element).
+ */
+ double offset = textPathView.getStartOffset().value;
+ /*
+ Let advance = the advance of
+ the typographic character corresponding
+ to character TODO k. (typo) [NOTE: This advance will
+ be negative for RTL horizontal text.]
+ */
+ double advance = result[index].advance;
+ /*
+ Let (x, y)
+ and angle be the position and angle
+ in result[index].
+ */
+ double x = result[index].x;
+ double y = result[index].y;
+ double angle = result[index].rotate;
+ /*
+
+ Let mid be a coordinate value depending
+ on the value of the "horizontal" flag:
+
+ true
+ mid is x + advance / 2
+ + offset
+ false
+ mid is y + advance / 2
+ + offset
+ */
+ double mid = (horizontal ? x : y) + advance / 2 + offset;
+ /*
+
+ The user agent is free to make any additional adjustments to
+ mid necessary to ensure high quality typesetting
+ TODO due to a ‘spacing’ value of
+ 'auto' or a
+ ‘method’ value of
+ 'stretch'.
+
+ If path is not a closed subpath and
+ mid < 0 or mid > length,
+ set the "hidden" flag of result[index] to true.
+ */
+ if (!pm.isClosed() && (mid < 0 || mid > length)) {
+ result[index].hidden = true;
+ }
+ /*
+ If path is a closed subpath depending on
+ the values of text-anchor and direction of
+ the element the character at index is in:
+ */
+ if (pm.isClosed()) {
+ /*
+ This implements the special wrapping criteria for single
+ closed subpaths.
+
+ (start, ltr) or (end, rtl)
+
+ If mid−offset < 0
+ or mid−offset > length,
+ set the "hidden" flag of result[index] to true.
+
+ (middle, ltr) or (middle, rtl)
+
+ If
+ If mid−offset < −length/2
+ or mid−offset > length/2,
+ set the "hidden" flag of result[index] to true.
+
+ (start, rtl) or (end, ltr)
+
+ If mid−offset < −length
+ or mid−offset > 0,
+ set the "hidden" flag of result[index] to true.
+ */
+ TextAnchor anchor = TextAnchor.start;
+ Direction direction = Direction.ltr;
+
+ double anchorShift = horizontal ? result[i].x : result[i].y;
+ switch (anchor) {
+ case start:
+ if (direction == Direction.ltr) {
+ if (mid < 0 || mid > length) {
+ result[index].hidden = true;
+ }
+ } else {
+ if (mid < -length || mid > 0) {
+ result[index].hidden = true;
+ }
+ }
+ break;
+
+ case middle:
+ if (mid < -length / 2 || mid > length / 2) {
+ result[index].hidden = true;
+ }
+ break;
+
+ case end:
+ if (direction == Direction.ltr) {
+ if (mid < -length || mid > 0) {
+ result[index].hidden = true;
+ }
+ } else {
+ if (mid < 0 || mid > length) {
+ result[index].hidden = true;
+ }
+ }
+ break;
+ }
+ }
+ /*
+ Set mid = mid mod length.
+ */
+ mid %= length;
+ /*
+ If the hidden flag is false:
+ */
+ if (!result[index].hidden) {
+ /*
+ Let point be the position and
+ t be the unit vector tangent to
+ the point mid distance
+ along path.
+ */
+ float[] point = new float[2];
+ float[] t = new float[2];
+ pm.getPosTan((float) mid, point, t);
+ final double tau = 2 * Math.PI;
+ final double radToDeg = 360 / tau;
+ final double r = Math.atan2(t[1], t[0]) * radToDeg;
+ /*
+ If the "horizontal" flag is
+ */
+ if (horizontal) {
+ /*
+ true
+
+ Let n be the normal unit vector
+ pointing in the direction t + 90°.
+ */
+ double normAngle = r + 90;
+ double[] n = new double[] {Math.cos(normAngle), Math.sin(normAngle)};
+ /*
+ Let o be the horizontal distance from the
+ TODO vertical center line of the glyph to the alignment point.
+ */
+ double o = 0;
+ /*
+ Then set the position in
+ result[index] to
+ point -
+ o×t +
+ y×n.
+
+ Let r be the angle from
+ the positive x-axis to the tangent.
+
+ Set the angle value
+ in result[index]
+ to angle + r.
+ */
+ result[index].rotate += r;
+ } else {
+ /*
+ false
+
+ Let n be the normal unit vector
+ pointing in the direction t - 90°.
+ */
+ double normAngle = r - 90;
+ double[] n = new double[] {Math.cos(normAngle), Math.sin(normAngle)};
+ /*
+ Let o be the vertical distance from the
+ TODO horizontal center line of the glyph to the alignment point.
+ */
+ double o = 0;
+ /*
+
+ Then set the position in
+ result[index] to
+ point -
+ o×t +
+ x×n.
+
+ Let r be the angle from
+ the positive y-axis to the tangent.
+
+ Set the angle value
+ in result[index]
+ to angle + r.
+ */
+ result[index].rotate += r;
+ }
+ }
+ /*
+
+ Otherwise, the "middle" flag
+ of result[index] is true:
+
+ Set the position and angle values
+ of result[index] to those
+ in result[index − 1].
+ */
+ } else {
+ result[index].x = result[index - 1].x;
+ result[index].y = result[index - 1].y;
+ result[index].rotate = result[index - 1].rotate;
+ }
+ }
+ /*
+ If the character at index i is not within a
+ ‘textPath’ element and corresponds to a typographic character, then:
+
+ This sets the starting point for rendering any characters that
+ occur after a ‘textPath’ element to the end of the path.
+ */
+ if (textPathView == null && result[index].addressable) {
+ /*
+ If the "in path" flag is true:
+
+ Set the "in path" flag to false.
+
+ Set the "after path" flag to true.
+
+ Set path_end equal to the end point of the path
+ referenced by ‘textPath’ − the position of
+ result[index].
+ */
+ if (inPath) {
+ inPath = false;
+ afterPath = true;
+ pm.setPath(textPath, false);
+ float[] pos = new float[2];
+ pm.getPosTan(pm.getLength(), pos, null);
+ path_end.set(pos[0], pos[1]);
+ }
+ /*
+
+ If the "after path" is true.
+
+ If anchored chunk of
+ result[index] is true, set the
+ "after path" flag to false.
+
+ Else,
+ let result.x[index] =
+ result.x[index] + path_end.x
+ and result.y[index] =
+ result.y[index] + path_end.y.
+ */
+ if (afterPath) {
+ if (result[index].anchoredChunk) {
+ afterPath = false;
+ } else {
+ result[index].x += path_end.x;
+ result[index].y += path_end.y;
+ }
+ }
+ }
+ /*
+
+ Set index = index + 1.
+ */
+ index++;
+ }
+ /*
+ Return result
+ */
+ return result;
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextPathView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextPathView.java
new file mode 100644
index 0000000000000..d4eca228a3bc5
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextPathView.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import static com.horcrux.svg.TextProperties.*;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class TextPathView extends TextView {
+
+ private String mHref;
+ private TextPathSide mSide;
+ private TextPathMidLine mMidLine;
+ private @Nullable SVGLength mStartOffset;
+ private TextPathMethod mMethod = TextPathMethod.align;
+ private TextPathSpacing mSpacing = TextPathSpacing.exact;
+
+ public TextPathView(ReactContext reactContext) {
+ super(reactContext);
+ }
+
+ public void setHref(String href) {
+ mHref = href;
+ invalidate();
+ }
+
+ public void setStartOffset(Dynamic startOffset) {
+ mStartOffset = SVGLength.from(startOffset);
+ invalidate();
+ }
+
+ public void setStartOffset(String startOffset) {
+ mStartOffset = SVGLength.from(startOffset);
+ invalidate();
+ }
+
+ public void setStartOffset(Double startOffset) {
+ mStartOffset = SVGLength.from(startOffset);
+ invalidate();
+ }
+
+ public void setMethod(@Nullable String method) {
+ mMethod = TextPathMethod.valueOf(method);
+ invalidate();
+ }
+
+ public void setSpacing(@Nullable String spacing) {
+ mSpacing = TextPathSpacing.valueOf(spacing);
+ invalidate();
+ }
+
+ public void setSide(@Nullable String side) {
+ mSide = TextPathSide.valueOf(side);
+ invalidate();
+ }
+
+ public void setSharp(@Nullable String midLine) {
+ mMidLine = TextPathMidLine.valueOf(midLine);
+ invalidate();
+ }
+
+ @SuppressWarnings("unused")
+ TextPathMethod getMethod() {
+ return mMethod;
+ }
+
+ @SuppressWarnings("unused")
+ TextPathSpacing getSpacing() {
+ return mSpacing;
+ }
+
+ TextPathSide getSide() {
+ return mSide;
+ }
+
+ TextPathMidLine getMidLine() {
+ return mMidLine;
+ }
+
+ SVGLength getStartOffset() {
+ return mStartOffset;
+ }
+
+ @Override
+ void draw(Canvas canvas, Paint paint, float opacity) {
+ drawGroup(canvas, paint, opacity);
+ }
+
+ Path getTextPath(Canvas canvas, Paint paint) {
+ SvgView svg = getSvgView();
+ VirtualView template = svg.getDefinedTemplate(mHref);
+
+ if (!(template instanceof RenderableView)) {
+ // warning about this.
+ return null;
+ }
+
+ RenderableView view = (RenderableView) template;
+ return view.getPath(canvas, paint);
+ }
+
+ @Override
+ Path getPath(Canvas canvas, Paint paint) {
+ return getGroupPath(canvas, paint);
+ }
+
+ @Override
+ void pushGlyphContext() {
+ // do nothing
+ }
+
+ @Override
+ void popGlyphContext() {
+ // do nothing
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextProperties.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextProperties.java
new file mode 100644
index 0000000000000..a8581e3fe3b47
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextProperties.java
@@ -0,0 +1,219 @@
+package com.horcrux.svg;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nonnull;
+
+class TextProperties {
+
+ /*
+ https://drafts.csswg.org/css-inline/#propdef-alignment-baseline
+ 2.2.1. Alignment Point: alignment-baseline longhand
+
+ Name: alignment-baseline
+ Value: baseline | text-bottom | alphabetic | ideographic | middle | central | mathematical | text-top | bottom | center | top
+ Initial: baseline
+ Applies to: inline-level boxes, flex items, grid items, table cells
+ Inherited: no
+ Percentages: N/A
+ Media: visual
+ Computed value: as specified
+ Canonical order: per grammar
+ Animation type: discrete
+ */
+ enum AlignmentBaseline {
+ baseline("baseline"),
+ textBottom("text-bottom"),
+ alphabetic("alphabetic"),
+ ideographic("ideographic"),
+ middle("middle"),
+ central("central"),
+ mathematical("mathematical"),
+ textTop("text-top"),
+ bottom("bottom"),
+ center("center"),
+ top("top"),
+ /*
+ SVG implementations may support the following aliases in order to support legacy content:
+
+ text-before-edge = text-top
+ text-after-edge = text-bottom
+ */
+ textBeforeEdge("text-before-edge"),
+ textAfterEdge("text-after-edge"),
+ // SVG 1.1
+ beforeEdge("before-edge"),
+ afterEdge("after-edge"),
+ hanging("hanging"),
+ ;
+
+ private final String alignment;
+
+ AlignmentBaseline(String alignment) {
+ this.alignment = alignment;
+ }
+
+ static AlignmentBaseline getEnum(String strVal) {
+ if (!alignmentToEnum.containsKey(strVal)) {
+ throw new IllegalArgumentException("Unknown String Value: " + strVal);
+ }
+ return alignmentToEnum.get(strVal);
+ }
+
+ private static final Map alignmentToEnum = new HashMap<>();
+
+ static {
+ for (final AlignmentBaseline en : AlignmentBaseline.values()) {
+ alignmentToEnum.put(en.alignment, en);
+ }
+ }
+
+ @Nonnull
+ @Override
+ public String toString() {
+ return alignment;
+ }
+ }
+
+ // TODO implement rtl
+ @SuppressWarnings("unused")
+ enum Direction {
+ ltr,
+ rtl
+ }
+
+ enum FontVariantLigatures {
+ normal,
+ @SuppressWarnings("unused")
+ none
+ }
+
+ enum FontStyle {
+ normal,
+ italic,
+ @SuppressWarnings("unused")
+ oblique
+ }
+
+ enum FontWeight {
+ // Absolute
+ Normal("normal"),
+ Bold("bold"),
+ w100("100"),
+ w200("200"),
+ w300("300"),
+ w400("400"),
+ w500("500"),
+ w600("600"),
+ w700("700"),
+ w800("800"),
+ w900("900"),
+ // Relative
+ Bolder("bolder"),
+ Lighter("lighter");
+
+ private final String weight;
+
+ FontWeight(String weight) {
+ this.weight = weight;
+ }
+
+ static boolean hasEnum(String strVal) {
+ return weightToEnum.containsKey(strVal);
+ }
+
+ static FontWeight get(String strVal) {
+ return weightToEnum.get(strVal);
+ }
+
+ private static final Map weightToEnum = new HashMap<>();
+
+ static {
+ for (final FontWeight en : FontWeight.values()) {
+ weightToEnum.put(en.weight, en);
+ }
+ }
+
+ @Nonnull
+ @Override
+ public String toString() {
+ return weight;
+ }
+ }
+
+ enum TextAnchor {
+ start,
+ middle,
+ end
+ }
+
+ enum TextDecoration {
+ None("none"),
+ Underline("underline"),
+ Overline("overline"),
+ LineThrough("line-through"),
+ Blink("blink");
+
+ private final String decoration;
+
+ TextDecoration(String decoration) {
+ this.decoration = decoration;
+ }
+
+ static TextDecoration getEnum(String strVal) {
+ if (!decorationToEnum.containsKey(strVal)) {
+ throw new IllegalArgumentException("Unknown String Value: " + strVal);
+ }
+ return decorationToEnum.get(strVal);
+ }
+
+ private static final Map decorationToEnum = new HashMap<>();
+
+ static {
+ for (final TextDecoration en : TextDecoration.values()) {
+ decorationToEnum.put(en.decoration, en);
+ }
+ }
+
+ @Nonnull
+ @Override
+ public String toString() {
+ return decoration;
+ }
+ }
+
+ enum TextLengthAdjust {
+ spacing,
+ spacingAndGlyphs
+ }
+
+ enum TextPathMethod {
+ align,
+ @SuppressWarnings("unused")
+ stretch
+ }
+
+ /*
+ TODO suggest adding a compatibility mid-line rendering attribute to textPath,
+ for a chrome/firefox/opera/safari compatible sharp text path rendering,
+ which doesn't bend text smoothly along a right angle curve, (like Edge does)
+ but keeps the mid-line orthogonal to the mid-point tangent at all times instead.
+ */
+ enum TextPathMidLine {
+ sharp,
+ @SuppressWarnings("unused")
+ smooth
+ }
+
+ enum TextPathSide {
+ @SuppressWarnings("unused")
+ left,
+ right
+ }
+
+ enum TextPathSpacing {
+ @SuppressWarnings("unused")
+ auto,
+ exact
+ }
+}
diff --git a/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextView.java b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextView.java
new file mode 100644
index 0000000000000..dd73b8ee9f99a
--- /dev/null
+++ b/android/vendored/unversioned/react-native-svg/android/src/main/java/com/horcrux/svg/TextView.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2015-present, Horcrux.
+ * All rights reserved.
+ *
+ * This source code is licensed under the MIT-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+package com.horcrux.svg;
+
+import static com.horcrux.svg.TextProperties.AlignmentBaseline;
+import static com.horcrux.svg.TextProperties.TextLengthAdjust;
+
+import android.annotation.SuppressLint;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Region;
+import android.view.View;
+import android.view.ViewParent;
+import com.facebook.react.bridge.Dynamic;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.bridge.ReadableArray;
+import java.util.ArrayList;
+import javax.annotation.Nullable;
+
+@SuppressLint("ViewConstructor")
+class TextView extends GroupView {
+ SVGLength mInlineSize = null;
+ SVGLength mTextLength = null;
+ private String mBaselineShift = null;
+ TextLengthAdjust mLengthAdjust = TextLengthAdjust.spacing;
+ private AlignmentBaseline mAlignmentBaseline;
+ @Nullable private ArrayList