SVGY
Generate icons from SVG
Turn one SVG into all the icons a project needs: resized and optimized SVGs, PNGs, Windows .ico macOS .icns, or all of them in a single pass.
Install
Prebuilt binary
Install in seconds using signed binary, for Linux, macOS, and Windows, on x64 and arm64:
cargo binstall svgy
Source
Install from source using crates.io:
cargo install svgy
Local
Install from a clone of this repository:
cargo install --path .
Signatures
Every release archive is signed with minisign. The public
key is in Cargo.toml.
To check an archive downloaded from the releases page:
minisign -V -m <source archive> -P <public key>
Synopsis
svgy <input.svg> [targets] [options]
With one input file, preferrably a square SVG, svgy can generate many icons formats in one pass.
Defaults
The command svgy file.svg without parameter:
- Resizes the SVG so its longest side is
1024, by rewriting theviewBox. - Saves the SVG with a default suffix as
file.svgy.svg.
This is equivalent to:
svgy file.svg --svg --suffix=svgy --size=1024
--svg: Create an SVG.--suffix=svgy: Add the suffixsvgyto the output file name.--size=1024: Resize so the longest side is 1024.
Asking for any target turns the implicit --svg off. Add --svg explicitly to keep it.
Examples
Create two icons for macOS and Windows in their respective subfolders, and a tiny SVG for the favicon in the same directory:
svgy example.svg --icns=macos/example.icns --ico=windows/example.ico --svg=favicon.svg --size=32
example.svgmacos
example.icns16x16 to 1024x1024windows
example.ico16x16 to 256x256favicon.svg32x32
Note: --size never applies to icons, they keep their fixed size sets (see Sizing).
Create a PNG and keep the resized SVG alongside it:
svgy example.svg --png --svg
example.svgexample.svgy.svg1024x1024, --svg always uses thesvgysuffix to prevent overwriting the source SVG.example.svgy.png1024x1024, default size
Fit the artwork inside a circle, for a round contact avatar:
svgy example.svg --round
example.svgexample.round.svg1024x1024, --round uses theroundsuffix.
Output targets
--svg[=<path>]: Convert to an optimized and minified SVG.--png[=<path>]: Convert to an optimized PNG.--ico[=<path>]: Convert to an optimized Windows icon.ico.--icns[=<path>]: Convert to an optimized macOS icon.icns.--round[=<path>]: Convert to an SVG whose artwork is centered and fitted inside a circle.
The output images are by default resized to fit 1024 x 1024, except icons which have specific sets of sizes per platform.
Sizing
All sizing parameters are optional, and they all preserve the aspect ratio.
--size=<pixels>: Resize to a square of<pixels>by<pixels>.--width=<pixels>: Resize to the specified width.--height=<pixels>: Resize to the specified height.--no-resize: Keep the original dimensions.
Notes:
- Passing
--widthand--heighttogether fits the artwork inside that box. - The sizing parameters
--size,--width,--heightare mutually exclusive. - For icons, non-square source SVGs are resized and centered.
- Sizing parameters do not apply to
.icoand.icnssince they have fixed size sets.
Rounding
--padding[=<0.0..1.0>]: Fraction of the inscribed circle's radius to leave empty around the artwork.0.0by default, or0.1when the source has a full-canvas background; bare--paddingmeans0.1. svgy prints which default it applied. The upper bound is exclusive.
--padding applies to --round only.
Output naming
- If the destination is not specified, the output is written beside the source as
<name>.<suffix>.<extension>, withsvgyas the default suffix.svgy example.svg --png->example.svgy.pngsvgy example.svg --icns->example.svgy.icns
--suffix=<suffix>: Use<suffix>instead ofsvgy.svgy example.svg --png --suffix=v2->example.v2.png
--roundis the one exception: its default suffix isround, so that--svgand--roundcan be asked for together without both claimingexample.svgy.svg.svgy example.svg --svg --round->example.svgy.svgandexample.round.svg
Behaviour
- Order of operations: read -> strip text -> resize -> round -> write each target. Rounding runs
last, inside whatever canvas
--sizeset, and the raster targets are rendered from the resized SVG. --roundis a targetsvgy logo.svg --round --icnswrites a round SVG and a normal.icns, the icon is not round-fitted. For round icons, run svgy twice and feed it the round SVG.- Text is removed.
<text>and its children are stripped from the source before anything else runs, so every target agrees on what the artwork is: it is absent from--svgjust as it is from--png,--icoand--icns, and--roundfits the circle to what remains. svgy prints a warning when it removes text. Convert text to paths before converting the icon. In Inkscape, select the text and use Path > Object to Path (Shift+Ctrl+C). - One input at a time. Globs and multiple inputs are not supported; use a shell loop.
- Missing directories are created. A destination may name a subdirectory that does not exist yet, as the icons example above does.
- Existing files are overwritten without asking.
- Exit codes:
0on success,1on any error, with a message on stderr.
Other options
--no-optimize: Skip PNG optimization. Optimization dominates the runtime of an.icnsor.ico, so this is the flag to reach for when iterating.--zopfli: Compress every PNG with Zopfli. Saves a 1~5% more, but takes 100x more time (minutes instead of seconds).--no-legacy-ico: Skip the 256-color entries in the Windows icon, keeping only the PNG ones. Saves about 3.6 KiB, at the cost of dropping support for 256-color sessions and pre-Vista shells.
Planned
Not implemented yet. Documented here so the intended surface is on record.
Targets
--all-app[=<app>]: Convert to all native app icons, using the source SVG location as the root directory.- Main icon:
app/<app>.svg - Windows:
windows/<app>.ico - macOS:
macos/<app>.icns - Linux:
linux/<app>_size.pngandlinux/<app>.svg
- Main icon:
--svgz[=<path>]: Convert to an optimized and GZIP-compressed SVGZ.--avif[=<path>]: Convert to an optimized lossless AVIF.--liquid[=<icon>]: Convert to an optimized macOS Liquid Glass icon.icondirectory.--linux[=<app>]: Convert to a set of optimized PNGs (<size>.png) and an SVG (scalable.svg)in theappdirectory.
Actions
--set-folder-icon[=<dir>]: Set the folder icon, macOS only. Without a value, the icon is for the directory containing the source SVG.
Parameters
--in-place: Overwrite the source.svg. SVG output only. Warning: this is a destructive parameter.--keep-ids: Keep SVG IDs, for example<path id="this-is-kept">. Depends on SVG optimization, below, since nothing strips IDs until that ships.--round-anchor=<shape|canvas>: Whether--roundrecenters the artwork on the canvas (canvas, the default and current behaviour) or scales it in place, leaving its center where it is (shape).--if-exists=<replace|keep|if-smaller|suffix>: What to do when the destination exists.replaceby default.keepto leave existing files alone,if-smallerto replace only when the new file is smaller,suffixto keep existing files and write to the suffixed name instead, replacing an existing suffixed file.--quiet: No output. Mutually exclusive with--verbose.--verbose: Print out all operations. The default prints one line per file written.--all-app-dir=<dir>: See--all-app.--app_id=<app_id>: See--all-app.
Optimization
SVG optimization. Until it ships, an SVG output is resized but not optimized. When it lands,
--no-optimize will skip it too, so the flag keeps its meaning.
IDs referenced from the document (url(#gradient), href="#clip", and the like) will never be
stripped, whether or not --keep-ids is passed; the flag governs the unreferenced ones.
References
Sizes
| Type | Extension | Icons in set | Size |
|---|---|---|---|
| Scalable Vector Graphics | .svg |
1 | 1024 x 1024 |
| Compressed Scalable Vector Graphics (gzip) | .svgz |
1 | 1024 x 1024 |
| Portable Network Graphics | .png |
1 | 1024 x 1024 |
| macOS icon | .icns |
11 | 16 x 16 to 1024 x 1024 |
| Windows icon | .ico |
8 | 16 x 16 to 256 x 256 |
| AV1 Image File Format | .avif |
1 | 1024 x 1024 |
.icns and .ico are multi-resolution containers: they hold every entry listed below, and the
OS picks one. Their size sets are fixed and cannot be overridden, on purpose: the default is
meant to be the good, opinionated answer.
macOS icon
File extension: .icns
| Size | Pixels | Type | Format |
|---|---|---|---|
| 16 x 16 | 16 x 16 | ic04 |
ARGB-RLE |
| 16 x 16 @2 | 32 x 32 | ic11 |
PNG |
| 32 x 32 | 32 x 32 | ic05 |
ARGB-RLE |
| 32 x 32 @2 | 64 x 64 | ic12 |
PNG |
| 48 x 48 | 48 x 48 | icp6 |
PNG |
| 128 x 128 | 128 x 128 | ic07 |
PNG |
| 128 x 128 @2 | 256 x 256 | ic13 |
PNG |
| 256 x 256 | 256 x 256 | ic08 |
PNG |
| 256 x 256 @2 | 512 x 512 | ic14 |
PNG |
| 512 x 512 | 512 x 512 | ic09 |
PNG |
| 512 x 512 @2 | 1024 x 1024 | ic10 |
PNG |
Notes:
- The 16 x 16 and 32 x 32 entries store straight-alpha ARGB with each channel PackBits-RLE encoded; every larger entry embeds its PNG bytes verbatim.
- Eleven entries cover eight distinct pixel sizes, and each size is rendered once. The 256 and 512 renders are also encoded once and their PNG bytes reused across both OSTypes. The 32 render is encoded twice, because its two entries want different formats: ARGB-RLE for
ic05and PNG foric11.
Windows icon
File extension: .ico
Eight entries for six sizes: the two smallest are stored twice, once in full color and once for
256-color consumers. Pass --no-legacy-ico for the six PNG entries alone.
| Size | Format | Declared depth | Alpha |
|---|---|---|---|
| 16 x 16 | PNG | 32 bpp | 8-bit |
| 16 x 16 | BMP | 8 bpp | 1-bit mask |
| 32 x 32 | PNG | 32 bpp | 8-bit |
| 32 x 32 | BMP | 8 bpp | 1-bit mask |
| 48 x 48 | PNG | 32 bpp | 8-bit |
| 64 x 64 | PNG | 32 bpp | 8-bit |
| 128 x 128 | PNG | 32 bpp | 8-bit |
| 256 x 256 | PNG | 32 bpp | 8-bit |
Notes:
- 256 x 256 is the largest size an
.icocan address, as the width and height fields are single bytes (0 meaning 256).- PNG entries are optimized by oxipng. Their declared depth is what Windows compares against the display when choosing between two entries of the same size, so it stays at the conventional 32 even where oxipng has reduced the PNG to an 8-bit palette; readers take the real depth from the IHDR.
- The BMP entries are the classic bottom-up 8bpp DIB: a 256-entry color table, then the color data, then the 1-bit AND mask. Colors come from a median-cut palette of the source render.
- They exist for sessions capped at 256 colors, such as an old
mstscor the "Limit maximum color depth" policy, where Windows prefers an entry declaring 8 bpp. They also keep those two sizes readable by pre-Vista shells, which cannot decode PNG entries at all.- They cost about 3.6 KiB: the color table alone is a fixed 1 KiB per entry, written in full rather than trimmed with
biClrUsed, since the point of these entries is to be boring for old readers.--no-legacy-icodrops them.- A 1-bit mask has no partial transparency. Pixels below 50% alpha vanish and the rest become fully opaque, so anti-aliased edges turn hard. This only affects the 256-color entries.
Linux icons
See:
- Icon Theme Specification
- APPImage: The filesystem image
Installation path:
- SVG:
/usr/share/icons/hicolor/scalable/apps/<id.app>.svge.g. com.ystorian.svgy.svg
- PNG:
/usr/share/icons/hicolor/256x256/apps/<id.app>.pnge.g. com.ystorian.svgy.png
Pipelines
- SVG: rewrite the root
viewBox-> multiply the scale into every coordinate in place -> round coordinates to 6 decimals - ROUND: measure the artwork's minimum enclosing circle (background fills excluded) -> bake the scale and translation that map it onto the inscribed circle into every coordinate
- PNG: render with resvg -> optimize with oxipng
- ICO: render each size with resvg -> encode PNG -> optimize with oxipng -> embed verbatim, plus a median-cut 256-color BMP for 16 x 16 and 32 x 32
- ICNS: render each size with resvg -> 16 and 32 to straight-alpha ARGB with PackBits RLE, larger sizes to PNG -> optimize with oxipng -> embed verbatim
Resizing adds no transform: a uniform scale is origin-independent and commutes with rotation, so
multiplying it into each coordinate renders identically to the source while preserving the original
element structure. Existing transforms are rewritten in place rather than flattened.
Every PNG goes through oxipng at preset 4, with alpha optimization on. Pass --zopfli to use
the Zopfli deflater, this saves 1~5% more, 2% on average, but this requires minutes instead of
seconds. Pass --no-optimize to skip optimization entirely.
entirely.
Requirements
Rust, edition 2024.
--set-folder-icon (planned) will be OS-specific. Everything else is cross-platform: an .icns can
be produced on Windows and an .ico on macOS.
Licence
Dual-licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.
Unless you state otherwise, any contribution intentionally submitted for inclusion in svgy, as defined in the Apache-2.0 licence, shall be dual-licensed as above, without any additional terms or conditions.