Do you know about these SVG techniques?
Tags: showdev, svg, javascript
Published: 2018-07-02 21:59:55

---
title: Do you know about these SVG techniques?
published: true
description: 
tags: showdev, svg, javascript
---

I recently discovered 3 really exciting [SVG] Techniques:

1. Convert [SVG] to image with [canvas]
2. [HiDPI Canvas]
3. Prefetching SVG: *Verrrrrryyyy cool*

I can't wait to tell you about them!

## Background Story

So I created [Faviator] a few months back and so far I have received some really good feed back. Check it out and star it if you haven't already done so.

 [Faviator] relied on a library called [convert-svg] which basically takes a screenshot of the SVG with [puppeteer] (headless chrome). The library was chosen because of its intuitive API and the fact that it uses [puppeteer], which guarantees that the image output would look exactly the same as what we see on Chrome. 

However, there are two main issues this library:

1. Image quality is not excellent; blurry image is generated
2. CSS `@import()` or `url()` are not always being loaded when the screenshot is taken

> Don't get me wrong. [convert-svg] is still an amazing project!

The second issue is very crucial for [Faviator] which fetches font files from [Google Fonts]. I did find out how to fix it and submitted a [PR](https://github.com/NotNinja/convert-svg/pull/45), but the owner seems to be too busy to spend time on the project. 

So I decided to create my own: [@ycm.jason/svg-to-img]; aiming to solve these problems. While working on this, I have discovered some really cool techniques to be used with SVG, so I thought I could make a blog post about my discoveries.

## Convert [SVG] to image with [canvas]
As I mentioned, one way to convert [SVG] to image is by doing a screenshot with [puppeteer]. It is actually a pretty nice approach as we won't have to worry about any error that could occur during the rendering of the [SVG].

Another approach is with HTML5 [canvas]. We can draw an `<img>` on a [canvas] easily with the [drawImage] method. As a combo, [canvas] also provides a [toDataURL](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL) method which exports the drawing to either PNG or JPEG format.

Okay.. So what?

This mean, we can:

1. Point an `<img>` to a SVG
2. Draw the `<img>` on a `<canvas>`
3. Export the `<canvas>` as a PNG or JPEG

Here is a quick demo:
```js
const img = document.createElement('img');
img.src = 'some/path/to/the/awesome.svg';
img.onload = () => {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  // draw img to (0, 0) on the canvas
  context.drawImage(img, 0, 0);
  // export the PNG or JPEG
  const pngDataURL = canvas.toDataURL('image/png');
  const jpegDataURL = canvas.toDataURL('image/jpeg');
  // ... do something with them ...
};
```

Notice all this happens in the browser, which enable [@ycm.jason/svg-to-img] to support both the browser and Node.js (with [puppeteer]).

## [HiDPI Canvas]

HiDPI Canvas is a technique introduced by Paul Lewis. It addresses the problem with the High DPI devices and the drawing of [canvas]. Please see his [article](https://www.html5rocks.com/en/tutorials/canvas/hidpi/) for detailed explanation. 

Summary:

1. Canvas drawing are drawn with 2x the pixels in High DPI devices to keep the width and height
2. This is basically upscaling the image which leads to blurry image
3. To solve this, we can draw the canvas 2x the intended size and use CSS to shrink it back to intended size.

Quick example (intended to draw 200x500):

```html
<canvas width="400" height="1000" style="width: 200; height: 500">
</canvas>
```
This technique enable me to convert [SVG] to sharp images on high DPI screens. However, it requires some manual resizing of the image. The current implementation uses [jimp] but I intend to write a smaller one just for this purpose. (Since now I include the whole freaking [jimp] inside the bundle. This is sinful...)

You can see how different they are:
**Original SVG**
![](https://svgshare.com/i/7Sp.svg)
https://svgshare.com/i/7Sp.svg

(the image showing is not an SVG... The image host convert it to png...)

**Without HiDPI technique**
![](https://i.imgur.com/vzps1C7.png)

**With HiDPI technique**
![](https://i.imgur.com/UsQSmgN.png)

Can you see the difference? If you focus on tips of the "F", you will notice how the last one matches the original SVG more.

Although this definitely improves the image detail, it still appears to bit a little bit blurry. This could be caused by the resizing of the image. I am not an expert in image processing, would be nice if you can tell me some useful techniques that I could use here.

## Prefetching SVG
CSS could be embedded in SVG to control the styles. With the introduction of CSS3 [@import], we could now include css inside css definition! How amazing!

However, if you are displaying your SVG in `<img>`, you might find out that the styles are not imported. The browser (or just Chrome) seems to ignore any external resources if SVG is used in `<img>`.

I invented a technique called Prefetching SVG which can solve the above problem and make your SVG looks the same even when you are offline!

The idea is to replace [@import] with the content that it is importing. Replace all `url()` with a data url. I have created a library to do this: [prefetch-svg].

**Without prefetching**
>  Since you might have the font-family installed locally, I explicitly removed the `@import` to demonstrate.

![](https://svgshare.com/i/7RH.svg)

**With prefetching**

![](http://svgur.com/i/7Sp.svg)

## That's it
And that's it. Here is my little sharing about SVGs. Tell me what you think! Have I missed anything?

[canvas]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas
[SVG]: https://developer.mozilla.org/en-US/docs/Web/SVG
[HiDPI Canvas]: https://www.html5rocks.com/en/tutorials/canvas/hidpi/
[Faviator]: https://github.com/faviator/faviator
[@ycm.jason/svg-to-img]: https://www.npmjs.com/package/@ycm.jason/svg-to-img
[puppeteer]: https://github.com/GoogleChrome/puppeteer
[Google Fonts]: https://fonts.google.com/
[convert-svg]: https://github.com/NotNinja/convert-svg
[drawImage]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
[jimp]: https://github.com/oliver-moran/jimp
[@import]: https://developer.mozilla.org/en-US/docs/Web/CSS/@import
[prefetch-svg]: https://github.com/ycmjason/prefetch-svg