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

Resize svg and output png file #729

Closed
tony-ist opened this issue Mar 7, 2017 · 8 comments
Closed

Resize svg and output png file #729

tony-ist opened this issue Mar 7, 2017 · 8 comments
Labels

Comments

@tony-ist
Copy link

tony-ist commented Mar 7, 2017

I am sorry if it is obvious or has been answered before, but this code

await sharp(new Buffer(layersWithSvg[0].icon_svg))
  .resize(100, 100)
  .png()
  .toFile('./assets/icon.png')

for icon_svg with metadata

{ format: 'svg',
  width: 16,
  height: 16,
  space: 'srgb',
  channels: 4,
  density: 72,
  hasProfile: false,
  hasAlpha: true }

outputs very poor quality png. Seems like it is scaling rasterized svg. How to scale svg and convert to png correctly?

@lovell
Copy link
Owner

lovell commented Mar 7, 2017

Hello, by default the SVG will be rendered at a density of 72DPI, which the metadata for this image suggests will be 16x16. Then resize(100, 100) will resize the 16x16 to 100x100, hence the problem you've encountered.

You can set the density parameter on the constructor to increase the size of the initial rendering.

If a density of 72 renders a 16x16 image, then a density of at least 72*100/16 = 450 will be suitable for rendering a 100x100 image.

sharp(new Buffer(layersWithSvg[0].icon_svg), { density: 450 })...

@lovell lovell added the question label Mar 7, 2017
@tony-ist
Copy link
Author

tony-ist commented Mar 7, 2017

@lovell Thanks a lot, it worked. Another question if I may: What is the fastest way to overlay png with svg (specified as xml text)? This operation is done iteratively like in #405. Right now I try

// Resizing overlay
const overlay = await sharp(new Buffer(layer.icon_svg), { density }).toBuffer()
const sharpSprite = sharp(pngBuffer).overlayWith(overlay, { top, left })
// Converting to buffer
const result = await sharpSprite.toBuffer()

It takes a lot of time:

Resizing overlay: 19.192ms
Resizing overlay: 15.852ms
Resizing overlay: 79.606ms
Resizing overlay: 41.280ms
Resizing overlay: 19.402ms
Resizing overlay: 428.578ms
Resizing overlay: 2514.534ms
Resizing overlay: 352.528ms
Resizing overlay: 138.136ms
Resizing overlay: 1132.550ms
Resizing overlay: 359.463ms
Resizing overlay: 277.739ms
Resizing overlay: 402.946ms
Resizing overlay: 406.915ms
Resizing overlay: 995.964ms
Resizing overlay: 273.016ms
Resizing overlay: 99.701ms
Resizing overlay: 2516.419ms
Converting to buffer: 564.397ms
Converting to buffer: 55.499ms
Converting to buffer: 178.630ms
Converting to buffer: 639.121ms
Converting to buffer: 310.367ms
Converting to buffer: 161.087ms
Converting to buffer: 270.496ms
Converting to buffer: 134.422ms
Converting to buffer: 2286.645ms
Converting to buffer: 302.231ms
Converting to buffer: 79.668ms
Converting to buffer: 2058.972ms
Converting to buffer: 233.220ms
Converting to buffer: 184.044ms
Converting to buffer: 2620.876ms
Converting to buffer: 211.188ms
Converting to buffer: 867.839ms
Converting to buffer: 135.458ms

All in all it takes around 22 seconds for my program to generate png sprite that consists of 18 svg images loaded from db. Also half of the time is consumed by conversion of Sharp into Buffer.

@lovell
Copy link
Owner

lovell commented Mar 7, 2017

The density parameter can be passed directly to overlayWith, something like (untested):

const result = await sharp(pngBuffer)
  .overlayWith(new Buffer(layer.icon_svg), { density, top, left })
  .toBuffer();

I notice the JSDocs for overlayWith are missing this so I'll update them, thanks for alerting me.

@tony-ist
Copy link
Author

tony-ist commented Mar 7, 2017

@lovell Thanks again, it halved sprite generation time, you are the best.

@lovell
Copy link
Owner

lovell commented Mar 10, 2017

Commit 701b1c4 adds density to the overlayWith docs.

@hongyuanlei
Copy link

Hi @ReFruity Have you solved the performance issue about overlay png with svg?

@damianobarbati
Copy link

damianobarbati commented Feb 25, 2018

@ReFruity @lovell sorry for the noise, I'm trying to resize svg=>png as well and I'm having poor quality too.
What is the correct way to deduct density given an svg image and an output size?
I tried the 72*100/width formula but it's worse.

import fs from 'mz/fs';
import sharp from 'sharp';

(async () => {
    const image = await fs.readFile('./logo.svg');

    const resolutions = [16,32,96,120,180,152,167,128,196,228]

    for(const resolution of resolutions) {
        const width = resolution;
        const height = resolution;
        const density = parseInt(72 * 100 / width);
        const result = await sharp(image, { density }).max().resize(width, height).png().toBuffer();
        await fs.writeFile(`./${width}x${height}.png`, result);
    }
})();

Test svg: https://afonsopacifer.github.io/assets/img/tech/node.svg

@lovell
Copy link
Owner

lovell commented Feb 25, 2018

@ReFruity At what dimensions does the default density of 72 DPI render? That's your divisor. What dimension do you need? That's your dividend. Multiple the result of this division by 72.

There was a worked example in #729 (comment):

"If a density of 72 renders a 16x16 image, then a density of at least 72*100/16 = 450 will be suitable for rendering a 100x100 image."

Repository owner locked and limited conversation to collaborators Feb 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants