lovell / sharp

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images. Uses the libvips library.
https://sharp.pixelplumbing.com
Apache License 2.0
29.19k stars 1.3k forks source link

Scaling SVGs results in blurry images #2398

Closed gfmio closed 4 years ago

gfmio commented 4 years ago

Are you using the latest version? Is the version currently in use as reported by npm ls sharp the same as the latest version as reported by npm view sharp dist-tags.latest?

# npm ls sharp
functions@ /Users/gfmio/projects/outfox/opti-ui-js/functions
└── sharp@0.26.1 

# npm view sharp dist-tags.latest
0.26.1

What are the steps to reproduce?

Take any SVG and try to scale it to a size that is bigger than its view box.

The output is blurry, because it seems the SVG is first rendered as a bitmap of the size of the original view box and then the bitmap is scaled.

What is the expected behaviour?

The image is as crisp as the bitmap size allows, i.e. the SVG is rendered straight onto the enlarged bitmap.

Are you able to provide a minimal, standalone code sample, without other dependencies, that demonstrates this problem?

import * as fs from "fs";
import sharp from "sharp";

const main = async () => {
  const svg = `
<svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <style type="text/css">
    .small { font: 13px "Source Sans Pro"; }
    .heavy { font: bold 30px "Source Sans Pro"; }
    .Rrrrr { font: italic 40px "Source Sans Pro"; fill: red; }
  </style>

  <rect x="120" y="10" width="50" height="50" rx="5" />
  <text x="20" y="35" class="small">My</text>
  <text x="40" y="35" class="heavy">cat</text>
  <text x="55" y="55" class="small">is</text>
  <text x="65" y="55" class="Rrrrr">Grumpy!</text>
</svg>
`;

  let image = sharp(Buffer.from(svg))
    .resize(2400, 800)
    .png();
  const outputBuffer = await image.toBuffer();
  fs.writeFileSync("./out.png", outputBuffer);
};

main();

Are you able to provide a sample image that helps explain the problem?

Input SVG:

<svg viewBox="0 0 240 80" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <style type="text/css">
    .small { font: 13px "Source Sans Pro"; }
    .heavy { font: bold 30px "Source Sans Pro"; }
    .Rrrrr { font: italic 40px "Source Sans Pro"; fill: red; }
  </style>

  <rect x="120" y="10" width="50" height="50" rx="5" />
  <text x="20" y="35" class="small">My</text>
  <text x="40" y="35" class="heavy">cat</text>
  <text x="55" y="55" class="small">is</text>
  <text x="65" y="55" class="Rrrrr">Grumpy!</text>
</svg>

Expected output (generated using Illustrator):

Artboard 1

Actual output:

out

What is the output of running npx envinfo --binaries --system?

# npx envinfo --binaries --system
npx: installed 1 in 0.824s

  System:
    OS: macOS 10.15.6
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 164.74 MB / 32.00 GB
    Shell: 5.7.1 - /bin/zsh
  Binaries:
    Node: 12.18.4 - ~/.nvm/versions/node/v12.18.4/bin/node
    Yarn: 1.22.4 - ~/.yarn/bin/yarn
    npm: 6.14.6 - ~/.nvm/versions/node/v12.18.4/bin/npm
lovell commented 4 years ago

Hi, did you see the density option of the constructor?

https://sharp.pixelplumbing.com/api-constructor#parameters

gfmio commented 4 years ago

Thanks, @lovell, that worked! But why is that configuration parameter necessary? And does that mean that whenever I'm rescaling, I need to calculate the appropriate density factor depending on the original and final resolution?

lovell commented 4 years ago

Great, please see #1421 for some previous discussion about this.

tomchen commented 3 years ago

I've just written a function for Scaling SVGs: https://github.com/lovell/sharp/issues/1421#issuecomment-813645061