diegomura / react-pdf

📄 Create PDF files using React
https://react-pdf.org
MIT License
14.46k stars 1.14k forks source link

Passing SVG to <Image> component #1735

Open palegz opened 2 years ago

palegz commented 2 years ago

Hello!

Is your feature request related to a problem? Please describe. I have a project in which I use d3.js to create many different charts of data. My d3.js functions return JSX elements which I then use to render in React.

My problem is that at the moment I have to render that JSX to static markup, then create a blob url from that <svg> markup, then load that blob url to an image in a <canvas> and finally from there export the chart as a png file.

I have to do all of this since React pdf renderer requires me to use only png or jpeg format in the <Image> component. Using the <Svg> component doesn't feel really useful in my case since I am already creating html <svg>'s but I can only use the React-pdf <Svg> components in the pdf itself.

So I am doing something like this in code:

 //Generate a blob url for d3 svg charts
function generateImageBlob(element) {
    const chart = ReactDOMServer.renderToStaticMarkup(element);
    const blob = new Blob([chart ], { type: 'image/svg+xml' });
    return URL.createObjectURL(blob);
  }
//Custom canvas component 
//used to get the toDataURL to a local state in the PDF renderer component
const Canvas = props => {
  const canvasRef = useRef(null);

  const draw = (ctx, img) => {
    img.onload = () => {
      ctx.drawImage(img, 0, 0);
    };
    URL.revokeObjectURL(img.src);
  };

  useEffect(() => {
    const canvas = canvasRef.current;
    const context = canvas.getContext('2d');
    const img = new Image();

    if (props.imgUrl) {
      draw(context, img);
      img.src = props.imgUrl;
      console.log(img.src);
    }

//this is passed to <Image src=> in the pdf renderer
    props.setCanvas(canvas.toDataURL('image/jpg'));
  }, [draw, props.imgUrl]);

  return <canvas ref={canvasRef} {...props} />;
};

While this works the images need to be drawn first in the browser itself, and then again in the PDF file. Seems counter productive to me. Error handling is troublesome with this method + the image quality is not as good as with vector graphics.

If anyone else has had similar use cases I would be interested to hear how you have resolved this problem.

Describe the solution you'd like Make the <Image> component accept SVG as an image format, like the html <img> equivelant.

Describe alternatives you've considered Create a wrapper to directly use <svg> instead of the library's own <Svg> elements directly inside the pdf renderer?

nowtryz commented 2 years ago

You could generate Svg's tags instead, or you can use a transformer to transform your tags to JSX tags that react-pdf handles. There are issues on this repo explaining how to do that.

cheald commented 2 years ago

Svg tags and transformers are not sufficient, because the Svg implementation doesn't support everything that pdfkit is capable of handling, such as text on paths. A feature like https://github.com/diegomura/react-pdf/issues/973 would provide an escape hatch to be able to go directly to pdfkit for arbitrary operations, allowing the use of something like svg-to-pdfkit to accomplish this.

Alynva commented 1 year ago

Also needing this. I have implemented an transformer, but not all the options are available in the react-pdf' Svg components.

Expected Result
image image

(had to remove the <tspan /> due to #2000

edemen commented 1 year ago

This would be so useful. Actually a deal breaker!