Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to save created image to SVG file ? #212

Closed
esutton opened this issue Jan 3, 2017 · 11 comments
Closed

How to save created image to SVG file ? #212

esutton opened this issue Jan 3, 2017 · 11 comments

Comments

@esutton
Copy link

esutton commented Jan 3, 2017

Is there a way to export a created image drawn using react-native-svg to a SVG file?

My goal is to:

  1. Use react-native-fs to write the SVG data to a SVG file.
  2. Reference the SVG image from HTML.
<html><body>
	<img src="http://phrogz.net/SVG/heart.svg" width="320" height="240">
</body></html>
  1. Convert HTML to PDF report using react-native-html-to-pdf.

I ran across a brief reference to a new method toDataURL.

While I think writing to a SVG file would be more versatile for PDF report generation,
what format does toDataURL output to? SVG? PNG?

svgRef.toDataURL(base64 => {
    console.log(base64);
});

Thank you for a great component!

@magicismight
Copy link
Collaborator

You can convert the base64 to PNG format by using other libraries.
It's not what this library do, I'm sorry I can't help you with that.
And for SVG, you can traverse through all children inside Svg component, and serialize them into a string, this is not a bad idea. I'll add this to my TODO list.

@esutton
Copy link
Author

esutton commented Jan 11, 2017

@magicismight Thank you for your tip

And for SVG, you can traverse through all children inside Svg component, and serialize them into a string, this is not a bad idea. I'll add this to my TODO list.

Not sure I understand. I could call toDataURL() on the SVG serialized result?

What would a traverse SVG children and serialize snippet look like?

@esutton
Copy link
Author

esutton commented Feb 14, 2017

Can any react native experts share suggestions on how to invisibly render a SVG for image generation purposes using toDataURL(base64) ?

The VictorChart I use supports a chart.containerRef.svgRef.toDataURL. The toDataURL(base64) works well to write image to a file using RNFS, then I add image reference to HTML content, and rendering HTML to PDF.. All good so far.

The hard part, is understanding how to create "dummy view" or a technique to invisibly render an SVG image offscreen ( with width set to match desired PDF page width ) ?

Example: Save SVG as file image using RNFS

  onWriteImageToFile(fileAttachment) {
    RNFS.writeFile(fileAttachment.path, fileAttachment.fileData, fileAttachment.encoding)
      .then(() => {
        console.log('RNFS.writeFile ', fileAttachment.path);
        // this.onWriteFileComplete(fileAttachment, sendCallback);
      })
      .catch((err) => {
        console.log(err);
        Alert.alert(`Failed to write file: ${fileAttachment.name}`, err.message);
      });
  },

In render method, call svgRef.toDataURL()

      <VictoryChart

        ref={(chart) => {
          if (chart && chart.containerRef && chart.containerRef.svgRef) {
            setTimeout(() => {
              chart.containerRef.svgRef.toDataURL((base64) => {
                const name = 'profile.png';
                const path = `${RNFS.DocumentDirectoryPath}/${name}`;
                const fileAttachment = {
                  path,
                  name,
                  fileData: base64,
                  mimeType: 'image/png',
                  encoding: 'base64',
                };
                this.onWriteImageToFile(fileAttachment);
              });
            }, 500);
          }
        }}
      >
      ... < removed code >

xxx-profile

Thanks in advance,

@petermikitsh
Copy link

Exposing the toDataURL API, without having to place the element in the on-screen component tree, would be fantastic.

@esutton
Copy link
Author

esutton commented Oct 4, 2017

This appears broken after upgrading react-native and victory-native from 0.7.0 to 0.15.0
"react": "16.0.0-alpha.12",
"react-native": "0.46.4",
"react-native-svg": "^6.0.0-rc1",
"victory-native": "^0.15.0",

It renders however chart.containerRef is undefined?

Is there another way to get chart.containerRef.svgRef ?

    const victoryChart = (
      <VictoryChart
        width={1056}

        ref={(chart) => {
          if (chart && chart.containerRef && chart.containerRef.svgRef) {
            setTimeout(() => {
              console.log('dbg: onSvgCreated timeout');
              chart.containerRef.svgRef.toDataURL((base64) => {
                const name = 'profile.png';

                // Cache Problem: New profile.png images is created but PDF still contains old, even if delete png and pdf first?
                // ToDo: Create a "tmp-<uuid>" folder each time?
                // const path = `${Utility.getFilePath()}/tmp-pdf/${name}`;
                const path = `${Utility.getFilePath()}/${name}`;

                const fileAttachment = {
                  path,
                  name,
                  fileData: base64,
                  mimeType: 'image/png',
                  encoding: 'base64',
                };
                // console.log(base64);
                console.log('fileAttachment:', fileAttachment);
                console.log('this:', this);
                this.onSvgCreated(fileAttachment);
              });
            }, 5 * 1000);
          }
        }}

I hope I do not have to roll back to victory-native 0.7.0 :-(

@esutton
Copy link
Author

esutton commented Oct 4, 2017

@boygirl explained on Gitter

  • victory no longer uses refs for anything other than its portal.
  • There isn't a built in ref, but you could create a light custom container to save a ref if you need
  • you have access to the containerComponent that VictoryChart will render. You can alter that or provide your own.

I do not understand this. I will do some research on containerComponent

@esutton
Copy link
Author

esutton commented Oct 4, 2017

I cannot figure this out how to get a svgRef from containerComponent

Rolling-back to victory-native 0.7.0

I expected this to be easy.

I expect creating a PDF containing a VictoryChart image is a common use case of any charting library.

@boygirl
Copy link
Contributor

boygirl commented Oct 4, 2017

@esutton adding an optional containerRef prop for VictoryContainer based on this feedback. Tracking here: FormidableLabs/victory#781

@esutton
Copy link
Author

esutton commented Feb 6, 2018

For capturing a VictoryChart chart react-native-view-shot worked for me:

https://github.com/gre/react-native-view-shot

  onCaptureViewShot = (uri) => {
    const base64Data = uri;
    const profileImage = {
      uri: `data:image/png;base64,${base64Data}`,
      filePath: '',
    };
    this.props.onCreateFilesAndSend(profileImage);
  }    

  render() {
    const chart = this.renderChart();
    const renderChartOffScreen = this.state.creatingPdf ? chart : null;
    return (
      <View style={{ position: 'absolute', left: 0 }}>
        <ViewShot
          onCapture={this.onCaptureViewShot}
          captureMode="mount"
          options={{
            format: 'png',
            quality: 1.0,
            result: 'base64',
            }}
        >
          {renderChartOffScreen}
        </ViewShot>
      </View>
    );
  }

@msand
Copy link
Collaborator

msand commented Dec 9, 2018

Closing as the original issues seems fixed.

@msand msand closed this as completed Dec 9, 2018
@msand
Copy link
Collaborator

msand commented Oct 19, 2019

An alternative solution if you want vector/svg rather than raster/png:

import * as React from "react";
import { Platform, StyleSheet, TouchableOpacity } from "react-native";
import { Svg, Rect } from "react-native-svg";
import ReactDOMServer from "react-dom/server";

const isWeb = Platform.OS === "web";

const childToWeb = child => {
  const { type, props } = child;
  const name = type && (type.displayName || type.name);
  const Tag = name && name[0].toLowerCase() + name.slice(1);
  return <Tag {...props}>{toWeb(props.children)}</Tag>;
};

const toWeb = children => React.Children.map(children, childToWeb);

export default class App extends React.Component {
  renderSvg() {
    return (
      <Svg height="100%" width="100%" style={{ backgroundColor: "#33AAFF" }}>
        <Rect
          x="50"
          y="50"
          width="50"
          height="50"
          fill="#3399ff"
          strokeWidth="3"
          stroke="rgb(0,0,0)"
        />
      </Svg>
    );
  }
  serialize = () => {
    const element = this.renderSvg();
    const webJsx = isWeb ? element : toWeb(element);
    const svgString = ReactDOMServer.renderToStaticMarkup(webJsx);
    console.log(svgString);
  };
  render() {
    return (
      <TouchableOpacity style={styles.container} onPress={this.serialize}>
        {this.renderSvg()}
      </TouchableOpacity>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    backgroundColor: "#ecf0f1",
    padding: 8
  }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants