localtools-svg-optimizer
v1.0.4
Published
Optimize, sanitize and rasterize SVG files offline with SVGO and resvg (localtools)
Readme
SVG Optimizer (localtools-svg-optimizer)
Optimize SVG files with real SVGO 4, one file or a whole folder at a time, entirely on your machine. Shows before/after size and % saved with side-by-side previews, downloads one file or a zip of all, can sanitize untrusted SVGs (XSS-safe for user uploads) and export PNG at any width.
Install
| OS / registry | command |
|---|---|
| Windows (PowerShell) | irm https://raw.githubusercontent.com/chayprabs/localtools/main/svg-optimizer/install.ps1 \| iex |
| macOS / Linux | curl -fsSL https://raw.githubusercontent.com/chayprabs/localtools/main/svg-optimizer/install.sh \| bash |
| npm (Node >= 20) | not published yet (localtools-svg-optimizer is prepared; publishing is blocked on npm 2FA) |
| Homebrew | brew install chayprabs/localtools/localtools-svg-optimizer |
| Scoop | scoop bucket add localtools https://github.com/chayprabs/scoop-localtools; scoop install localtools-svg-optimizer |
The zip install is one self-contained executable (Bun runtime, SVGO and the resvg wasm module embedded). No network use.
Use
UI: run localtools-svg-optimizer (or the launcher); it opens http://127.0.0.1:47127. Drop SVGs or paste markup,
pick options, press Optimize. Click a row to compare that file. Previews are <img> elements, so an uploaded SVG never runs in the page.
Options: Safe preset (SVGO preset-default, but every id, <symbol> and <defs> entry is kept, so sprite sheets,
file.svg#id links and outside CSS/JS keep working) or Aggressive (full preset-default: drops unreferenced ids,
symbols and defs and shortens ids; also drops <title>/<desc> and off-canvas paths, reuses identical paths, turns
styles into attributes, 2 decimals), decimal places 0-8, keep viewBox, remove width/height
(responsive), prefix IDs with the file name (safe to inline several SVGs on one page), multipass, sanitize, PNG width.
CLI:
localtools-svg-optimizer optimize icons/ -o out/ --sanitize --png 512
localtools-svg-optimizer optimize logo.svg --aggressive --multipass # writes logo.min.svgFlags: -o DIR, --aggressive, --precision N, --multipass, --no-viewbox, --remove-dimensions, --prefix-ids,
--sanitize, --png WIDTH. Exit code 1 if any file failed.
API: POST /api/optimize, multipart with one or more file parts and/or an svg text field. Fields: preset
(safe|aggressive), keepViewBox, removeDimensions, prefixIds, multipass, sanitize (1/0), precision, png
(width px), format=zip for a zip instead of JSON. JSON: {files:[{name,before,after,saved,svg,png?}], errors, before, after, saved}.
curl -F [email protected] -F sanitize=1 http://127.0.0.1:47127/api/optimizeEngine and why
- SVGO 4.1 is the standard SVG optimizer, used by every major build tool; it runs unmodified with its own plugins.
- Sanitizer: a strict allowlist SVGO plugin, not a regex pass. Only drawing elements survive (no
script,foreignObject,iframe/embed/object,handler, HTML or unknown namespaces). Everyon*attribute,xml:base, non-localhrefandjavascript:/data:text/htmlvalues are removed. CSS (<style>,style=""and presentation attributes) is first normalized like a browser reads it (comments dropped, escapes such asu\72l(decoded) and then checked; any externalurl(),@import,@font-face,@namespace,image-set(),cross-fade(),element()orsrc()drops thatstyle=""declaration, or the whole<style>sheet (never re-assembled from pieces). Only thexlink:/xml:prefixes are allowed, and SMIL may not animatehref,style,on*orbase. The final SVGO output is sanitized again. Links are unwrapped;<image>keeps only inline PNG/JPEG/GIF/WebP/AVIF data. Tested against a 30-payload XSS corpus and a 59-payload CSS/namespace/SMIL corpus, both by pattern checks and by inlining every sanitized output into a live Chromium page that must fire no dialog and make no request. DOMPurify was not used because it needs a full server-side DOM (jsdom), a large dependency for no extra safety here. - resvg (wasm build,
@resvg/resvg-wasm2.6) renders PNGs without a browser and runs on every OS/arch from one artifact, so no per-OS sidecar is needed. It is embedded in the executable and the npm bundle. Text uses common system fonts (Arial, Helvetica, DejaVu, Liberation) read from the OS font folder.
Tests render every sample before and after optimization with resvg and require at most 0.5% of pixels to differ.
Cut / limits
- PNG size is set by width in px (height follows the aspect ratio). No separate DPI field: width is what a DPI setting would compute.
- SVGs over 32 MB, nested more than 256 levels deep or with more than 30,000 elements are rejected with a clear error
(SVGO's run time grows faster than linearly with those). Files with over 2,000 shapes skip SVGO's
mergePaths(quadratic). Each file has 30 s; past that it is stopped (HTTP 422). Files run in a separate worker process, so the UI and API stay responsive meanwhile. - PNG output is limited to 16384 px per side and 64 megapixels.
- PNG text only uses the system fonts listed above; other font families render with a fallback (the SVG output is unaffected).
Update / uninstall
localtools-svg-optimizer update / localtools-svg-optimizer uninstall. Package-manager installs print the matching brew/scoop/npm command.
License
MIT. Bundled third-party licenses: THIRD_PARTY_NOTICES.md.
