How to generate PNG Image from the combination of SVG Image and CSS to share a page via Facebook
Tags: node, tutorial, javascript, face
Published: 2020-10-17 00:29:01

## Notice

You need to be able to apply what I did fundamentally even though I am using the tag `tutorial`. I added the tag because I think you can still reduce the time for `Trial and Error` in case you get a similar situation.

## Background

- `One of the clients at work` wanted to show an Avatar when sharing a page via Facebook. So I had to enable their Web Application to generate a URL for `og:image` and its related Open Graph Tags.

-  The problem was that there is no actual PNG Image stored within the App as it shows the Avatar with the combination of `SVG Image related tags` and `CSS`. So I had to come up with a way to generate the PNG Version of the Avatar before amending the logic for Open Graph Tags.

I ended up spending hours to clear out the uncertainties with my-coworkers and then made this happen as follows:

- Capture final Avatar Image at Frontend ( The web browser )
- Send the image data to the server
- Make the server generate PNG Image with the image data
- Store the PNG Image in the way the Web Application can provide the URL for it
- And then generate Open Graph Tags accordingly

Troublesome.

I could have tried to render the Avatar at server side by mucking around existing Avatar Component and then convert it to PNG Image, but I didn't really spend time to see if that way works out because the Component has a lot of mumbo jumbo with jQuery aka `the code made by someone else or me in the past`. Besides, that way may break existing logic. So I chose the path with `spaghetti code but it works` as usual.

## Solution

At first, I was trying to make this happen with Node.js Packages letting me convert SVG Image to PNG Image, and then noticed that some bits of Avatar are drawn with CSS and therefore I don’t get the same appearance with those Packages (because you cannot use CSS as parameter, well, maybe you can, I did not check that, and I don’t want to go back there and then re-factor the further code in case that worked). Anyway, I ended up using [html2canvas](https://html2canvas.hertzen.com/) to make this happen.

## Code Example

This is not the exact code for the Web Application, but if you are an experienced web developer, you should be able to copy the important bits and avoid browsing around Stack Overflow pages etc.

### Frontend

```
// This part gets thrown to Babel prior to being loaded by web browsers
// so that this ECMA Script works when the browser actually executes it

onAfterYourComponentRenderedAvatar() {

  const selector = '#container-for-avatar';
  const element = jQuery(selector);

  // Enable to revert the original size
  const width = element.width();
  const height = element.height();

  // Fix the size to make "html2canvas()" capture the Avatar properly
  // You may need to muck around more here to make that happen
  element.width(240).height(240);

  // Capture the Avatar made by the combination of SVG and CSS
  // Lucky this worked in my case
  html2canvas(
    document.querySelector(selector),
    {
      // Another part of making "html2canvas()" capture the Avatar properly
      // Skipping to think logically and/or amend other existing code
      scrollX : -9,
      // Make the background transparent, as the Avatar has the shape of circle in my case
      backgroundColor : null,
    },
  )
  .then((canvas) => {

    // Revert the Avatar Element to have the original size
    element.width(width).height(height);

    // Debug to see how it went
    // document.querySelector('#example-container').appendChild(canvas);

    // Send the image data to the server
    const opts = {
      method  : 'POST',
      headers : {
        Accept         : 'application/json',
        'Content-Type' : 'application/json',
      },
      // Not sure if I need this bit
      redirect    : 'follow',
      // I guess this is safe to put
      credentials : 'same-origin',
      // Main bit, you can buy me a cup of coffee forever
      body        : JSON.stringify({
        avatar : canvas.toDataURL('image/png'),
      }),
    };

    fetch(`/example/${id}/avatar/png`, opts);
  });
}
```

### Backend

```
/**
 * Save PNG Version of Avatar
 * in case App uses ExpressJS etc
 */
app.post(
  '/example/:id/avatar/png',
  (req, res) => {

    if (!req.body.avatar) {
      return; // or whatever
    }

    // Mumbo Jumbo Code prior to saving the PNG Version of Avatar here

    // Extract the image data from the request data
    const dataaaaaaaa = req.body.avatar.split(',')[1];
    const bufferrrrrr = Buffer.from(dataaaaaaaa, 'base64');

    // Save the image with "bufferrrrrr" or whatever you have to do
    // to enable your App to let us refer the image via URL so that Facebook can
    // I do not name variables like "bufferrrrrr" in case you wondered
  },
);

/**
 * When showing the page having "og:image" etc
 */

// Just generate the META Tags with the URL for the PNG Version of Avatar
```

## References

I don’t remember because it was almost midnight when it started working. And that is the intention of this post, helping others by sharing my experience.