Search code examples
javascriptsvgcanvasserverlessvercel

Convert a SVG to a PNG/JPEG without using canvas in a serverless function


I have a serverless function which returns a SVG based on a text sent as a query parameter.

module.exports = (req, res) => {
  const { yourName } = req.query;

  res.setHeader("Content-Type", "image/svg+xml");

  const svg = `
    <svg fill="none" viewBox="0 0 600 400" width="600" height="400" xmlns="http://www.w3.org/2000/svg">
      <foreignObject width="100%" height="100%">
        <div xmlns="http://www.w3.org/1999/xhtml">
          <style>
            .title {
              font-size: 10vh;
              font-weight: regular;
              background-color: #ffddff;
            }
          </style>
            
          <p class="title">Hello ${yourName}</p> 
          
        </div>
      </foreignObject>
    </svg>
  `;

  res.send(svg);

};

As an example, when I call /api?yourName=world, it returns me the following SVG;

hello world by a serveless api

(Deployed URL: https://vercel-test-ruddy.vercel.app/api?world)

What I need?

I need to return the SVG as an image (PNG/JPEG). So, the my function would look like,

module.exports = (req, res) => {
  const { yourName } = req.query;

  res.setHeader("Content-Type", "image/png");

  const svg = `
    <svg fill="none" viewBox="0 0 600 400" width="600" height="400" xmlns="http://www.w3.org/2000/svg">
      <foreignObject width="100%" height="100%">
        <div xmlns="http://www.w3.org/1999/xhtml">
          <style>
            .title {
              font-size: 10vh;
              font-weight: regular;
              background-color: #ffddff;
            }
          </style>
            
          <p class="title">Hello ${yourName}</p> 
          
        </div>
      </foreignObject>
    </svg>
  `;

  res.send( svgToPng(svg) );

};

svgToPng = (svg) => {
   // not defined
}

Did I refer other SO questions?

Yes. There are few questions regarding converting SVG to PNG:

Convert SVG to image (JPEG, PNG, etc.) in the browser
How to Exporting SVG as PNG format

Almost every answer talks about converting SVG to canvas and then canvas to PNG. And also answers are much older too. Can we do it without using a canvas?

In my project I actually don't have a html document, I have only one single .js file (As I know we need HTML5 to use canvas).

Notes

I have used using Vercel to deploy my serverless function which is;

Deployed at: https://vercel-test-ruddy.vercel.app/api?yourName=YOUR_NAME_HERE
GitRepo: https://github.com/tharindusathis/vercel-test


Solution

  • The best way to deal with this that I have found so far is by using a headless chrome and getting a screenshot. As an example by using puppeteer I could get a screenshot of the viewport like follows,

    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    
    let yourName = 'world'
    
    const svg = `
        <svg fill="none" viewBox="0 0 600 400" width="600" height="400" xmlns="http://www.w3.org/2000/svg">
          <foreignObject width="100%" height="100%">
            <div xmlns="http://www.w3.org/1999/xhtml">
              <style>
                .title {
                  font-size: 10vh;
                  font-weight: regular;
                  background-color: #ffddff;
                }
              </style>
                
              <p class="title">Hello ${yourName}</p> 
              
            </div>
          </foreignObject>
        </svg>
      `;
    
    await page.setViewport({ width: 2048, height: 1170 });
    await page.setContent(svg);
    
    console.log(await page.content());
    await page.screenshot({path: 'screenshot.png'});
    await browser.close();
    
    

    Then it will be easy to send the png as the response.