svg / svgo

⚙️ Node.js tool for optimizing SVG files
https://svgo.dev/
MIT License
21.03k stars 1.39k forks source link

Plugin to optimize embedded images ? #971

Open Zulko opened 6 years ago

Zulko commented 6 years ago

Hi and thanks for a super-useful project. I have SVGs embedding PNG images (they are represented inline as base64 in the XML) and I want to convert them to JPG to reduce the size of my SVGs. It doesn't seem that thereis a plugin for this. Would you be interested by one ? has this been tried already ?

paperboyo commented 6 years ago

Just a user here. Images without transparency should be converted to JPGs, but the ones with transparency have to be converted by pngquant to PNG8. This may not be trivial as you would need to be able to tell one from the other and maybe provide options to control both compression levels.

Would be very useful, though!

GreLI commented 6 years ago

Yeah, it would be nice, but it's a complex topic. There a lot of optimizers with different optimizations degree and execution time, also they're a hardly depend on image type (low-color graphics compress differently from photo-like, dealing with semitransparency has its own tricks, etc).

Also, it would add extra dependencies, optimizers are binary, so you have to deal with different platforms, bloating dependencies even more, which I'd prefer to avoid, since far not everyone needs it. It more like a constructor for image optimization than a single format optimization like SVGO, so I believe it's out of SVGO's scope.

Zulko commented 6 years ago

jimp is a pure-javascript library that can do JPG. My intention is to just try some simple PNG -> JPG optimizer for my own SVG needs. Is it possible to make "external" plugins for SVGO or do plugins have to be hosted in this repository ?

GreLI commented 6 years ago

There is no special interface for external plugins, except you can pass functions within config object like

{
    "plugins": [
        /* ... list of SVGO plugin options ... */
        {
            "mySpecialPlugin": {
                "fn": function(data) { /* ... plugin body ... */ }
            }
        }
    ]
}
paperboyo commented 6 years ago

some simple PNG -> JPG

Related to know where to NOT convert (if there is transparency): https://github.com/oliver-moran/jimp/issues/271. By having a very cursory look at jimp, I can’t tell if it supports pngquant-like PNG8 with 8-bit transparency (PNG32 is too weighty, PNG8 with 1-bit transparency is useless).

julienboulay commented 6 years ago

Hi, I created a new plugin to optimise png and jpeg embedded in the svg as base64. It works well for me, decreasing the size of the svg by 50%. Let me know if it fits your needs

Zulko commented 6 years ago

I went with my own solution (upstream of SVGO) and it works well for me too, but I'd still be interested in seing your solution (and even better if it comes included in SVGO), as a SVGO plugin would be cleaner.

julienboulay commented 6 years ago

You can try it by using npm i -g git+ssh://git@github.com/julienboulay/svgo.git then svgo [OPTIONS] [ARGS] Some options can be passed to svgo to configure pngquant and jpegtran

Zulko commented 6 years ago

It seems that your solution relies on external non-JS software though, which I really want to avoid in my project. The ideal solution would be pure-js, but also synchronous so that it can be a SVGO plugin. So far I couldn't find any library that does that.

GreLI commented 6 years ago

There is no JS-based bitmap image optimizers I'm aware of. It's even doesn't make sense. It'd same as binary optimizers but with drawbacks.

julienboulay commented 6 years ago

@Zulko : why do you want to avoid non-JS solution ? imagemin, pngquant and jpegtran are very powerful projects and are multi-platforms.

Zulko commented 6 years ago

There is jimp, which is the solution I use in my project (see two messages above).

It makes plenty of sense to have a pure-JS image library, it is more accessible (it doesn't complicate the installation of your JS project) and it can run in a web application. It's fast enough.

julienboulay commented 6 years ago

There is no complication for installing imagemin, pngquant and jpegtran. You just need to use npm as any other node dependency

Zulko commented 6 years ago

@julienboulay I am making this project which, even though it is written in javascript and distributed via npm, is aimed at the general public (not just front-end specialists). I am trying to avoid any binary because these can cause platform-related problems (see for instance the imagemin issues page, but pngquant and jpegtran also have open installation issues)

julienboulay commented 6 years ago

@Zulko, I understand, now, why you don't want external libraries.

I had a look to jimp and it looks powerful. It might be hard to integrate to the svgo plugin system as the plugins doesn't work asynchronously.

I can see that you use node-sass in your project, which depends on native libraries. Did you have any users complaining about installation issues regarding node-sass ? If not, you might give a try to pngquant and jpegtran.

schmod commented 5 years ago

I wonder if we could re-envision this as a command-line argument (or JS option) that will pipe embedded images to an external program (making the management of binary dependencies "somebody else's problem").

strarsis commented 5 years ago

Also mozjpeg for JPEG files.

Isn't there a framework for CLI/tools like SVGO that offers configurable piping or callbacks to other "processors", like image file data?

Edit: After some thought it comes to mind that webpack actually solved this already: Using the input SVG as "source" and loaders for the resources that are embedded inside or referenced by the SVG, like images, fonts, styles, etc.

BrainSlugs83 commented 4 years ago

Apologies all, I know this isn't the best place for it -- but @julienboulay -- I tried to use your version today, but it fails on the minifyImages plugin. -- I'd log this as an issue on your fork, but I can't seem to find the button for it. (Is that not a thing? or maybe I'm just going blind...)

I did install imagemin, pngquant, pngquant-bin, and jpegtran globally, and it looks like it's getting back the buffer of bytes from pngquant.exe, but it's treating them as an error.

npm -g install imagemin
npm -g install pngquant
npm -g install pngquant-bin
npm -g install jpegtran
npm i -g https://github.com/julienboulay/svgo.git

svgo -i Home3.svg -o Home5.svg

It prints out a bunch of garbage characters to the console and fails with this message (garbage omitted):

at makeError (C:\[...]\npm\node_modules\svgo\node_modules\execa\index.js:172:9)
    at Function.module.exports.sync (C:\[...]\npm\node_modules\svgo\node_modules\execa\index.js:341:15)
    at minifyPng (C:\[...]\npm\node_modules\svgo\plugins\minifyImages.js:109:22)
    at Object.exports.fn (C:\[...]\npm\node_modules\svgo\plugins\minifyImages.js:53:32)
    at C:\[...]\npm\node_modules\svgo\lib\svgo\plugins.js:61:45
    at Array.filter (<anonymous>)
    at monkeys (C:\Users\[...]\npm\node_modules\svgo\lib\svgo\plugins.js:48:39)
    at C:\Users\[...]\npm\node_modules\svgo\lib\svgo\plugins.js:68:17
    at Array.filter (<anonymous>)
    at monkeys (C:\Users\[...]\npm\node_modules\svgo\lib\svgo\plugins.js:48:39) {
  code: 99,
  stdout: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 01 18 00 00 01 18 08 06 00 00 00 87 8e e9 8c 00 00 00 04 67 41 4d 41 00 00 b1 8f 0b fc 61 05 00 ... 104857 more bytes>,
  stderr: <Buffer >,
  failed: true,
  signal: null,
  cmd: 'C:\\Users\\[...]\\npm\\node_modules\\svgo\\node_modules\\pngquant-bin\\vendor\\pngquant.exe - --floyd=1 --quality 70-90 --speed 1',
  timedOut: false
julienboulay commented 4 years ago

Hi @BrainSlugs83 ,

Thank you for your post. I enabled issues on my repo, so you might be able to create new issues now.

I think your issue comes from the installation process. The version of imagemin, pngquant, pngquant-bin jpegtran might not be the right versions required for this plugin.

If you want to try with the right dependencies version :

git clone https://github.com/julienboulay/svgo.git
npm install
./bin/svgo -i Home3.svg -o Home5.svg
Christopher-Hayes commented 2 years ago

@julienboulay wow, that worked great! So hard to find an SVG minimizer that also handles embedded images. It cut my SVG size in half with no visible loss in quality.

TPS commented 1 year ago

It might even be worth converting, e.g., an embedded PNG → lossless WebP

Btw, #1246 duplicates this?

GreLI commented 1 year ago

Thinking it over, SVGO could use some already known optimizers like pngout and mozjpeg. It could search them in standard places (platform-specific) and some side plugins could add formats and binaries. However, I don't think it's a good idea to add binaries to SVGO core or add extra dependencies to compile them on installation.

TrySound commented 1 year ago

A good case for external plugin actually. No support for async plugins though I remember somebody used atomics to make async code synchronous.