Skip to content

Commit

Permalink
feat: Support local .svg files, fixes #1306
Browse files Browse the repository at this point in the history
depends on react-native-community/cli#1042

```jsx
import React from 'react';
import {LocalSvg} from 'react-native-svg';
import test from './test.svg';
export default () => <LocalSvg asset={test} />;
```
  • Loading branch information
msand committed Mar 9, 2020
1 parent 4965d6c commit 4e9e8b5
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 2 deletions.
42 changes: 42 additions & 0 deletions android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,29 @@

package com.horcrux.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);
Expand Down Expand Up @@ -228,4 +236,38 @@ public WritableMap getScreenCTM(int tag) {
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)) != 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;
}
62 changes: 62 additions & 0 deletions src/LocalSvg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState, useEffect } from 'react';
import { NativeModules, Platform } from 'react-native';
// @ts-ignore
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';

import { fetchText } from './xml';
import { SvgCss } from './css';

const { getRawResource } = NativeModules.RNSVGRenderableManager;

export function getUriFromSource(source?: string | number) {
const resolvedAssetSource = resolveAssetSource(source);
return resolvedAssetSource.uri;
}

export function loadLocalRawResourceDefault(source?: string | number) {
const uri = getUriFromSource(source);
return fetchText(uri);
}

export function isUriAnAndroidResourceIdentifier(uri?: string | number) {
return typeof uri === 'string' && uri.indexOf('/') <= -1;
}

export async function loadAndroidRawResource(uri?: string | number) {
try {
return await getRawResource(uri);
} catch (e) {
console.error(
'Error in RawResourceUtils while trying to natively load an Android raw resource: ',
e,
);
return null;
}
}

export function loadLocalRawResourceAndroid(source?: string | number) {
const uri = getUriFromSource(source);
if (isUriAnAndroidResourceIdentifier(uri)) {
return loadAndroidRawResource(uri);
} else {
return fetchText(uri);
}
}

export const loadLocalRawResource =
Platform.OS !== 'android'
? loadLocalRawResourceDefault
: loadLocalRawResourceAndroid;

export type LocalProps = { asset?: string | number; override?: Object };

export function LocalSvg(props: LocalProps) {
const { asset, ...rest } = props;
const [xml, setXml] = useState(null);
useEffect(() => {
loadLocalRawResource(asset).then(setXml);
}, [asset]);
return <SvgCss xml={xml} {...rest} />;
}

export default LocalSvg;
3 changes: 3 additions & 0 deletions src/ReactNativeSVG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
SvgWithCssUri,
inlineStyles,
} from './css';
import { LocalSvg, loadLocalRawResource } from './LocalSvg';
import {
RNSVGCircle,
RNSVGClipPath,
Expand Down Expand Up @@ -91,6 +92,8 @@ export {
SvgWithCss,
SvgWithCssUri,
inlineStyles,
LocalSvg,
loadLocalRawResource,
Shape,
RNSVGMarker,
RNSVGMask,
Expand Down
3 changes: 1 addition & 2 deletions src/elements/Shape.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
NativeMethodsMixinStatic,
} from 'react-native';
import { TransformProps } from '../lib/extract/types';

const RNSVGRenderableManager = NativeModules.RNSVGRenderableManager;
const { RNSVGRenderableManager } = NativeModules;

export interface SVGBoundingBoxOptions {
fill?: boolean;
Expand Down

0 comments on commit 4e9e8b5

Please sign in to comment.