├── .editorconfig
├── .github
└── workflows
│ └── CI.yml
├── .gitignore
├── .yarn
└── releases
│ └── yarn-3.5.1.cjs
├── .yarnrc.yml
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── __test__
├── index.spec.ts
├── index.spec.ts.md
└── index.spec.ts.snap
├── crates
├── binding
│ ├── Cargo.toml
│ ├── build.rs
│ └── src
│ │ └── lib.rs
└── core
│ ├── Cargo.toml
│ ├── __fixture__
│ └── plugins
│ │ ├── cleanupIds.01.svg
│ │ ├── cleanupIds.02.svg
│ │ ├── cleanupIds.03.svg
│ │ ├── cleanupIds.04.svg
│ │ ├── cleanupIds.05.svg
│ │ ├── cleanupIds.06.svg
│ │ ├── cleanupIds.07.svg
│ │ ├── cleanupIds.08.svg
│ │ ├── cleanupIds.09.svg
│ │ ├── cleanupIds.10.svg
│ │ ├── cleanupIds.11.svg
│ │ ├── cleanupIds.12.svg
│ │ ├── cleanupIds.13.svg
│ │ ├── cleanupIds.14.svg
│ │ ├── cleanupIds.15.svg
│ │ ├── cleanupIds.16.svg
│ │ ├── cleanupIds.17.svg
│ │ ├── cleanupIds.18.svg
│ │ ├── cleanupIds.19.svg
│ │ ├── cleanupIds.20.svg
│ │ ├── cleanupIds.21.svg
│ │ ├── cleanupNumericValues.01.svg
│ │ ├── cleanupNumericValues.02.svg
│ │ ├── collapseGroups.01.svg
│ │ ├── collapseGroups.02.svg
│ │ ├── collapseGroups.06.svg
│ │ ├── collapseGroups.07.svg
│ │ ├── collapseGroups.08.svg
│ │ ├── collapseGroups.09.svg
│ │ ├── collapseGroups.11.svg
│ │ ├── collapseGroups.12.svg
│ │ ├── collapseGroups.13.svg
│ │ ├── collapseGroups.14.svg
│ │ ├── collapseGroups.15.svg
│ │ ├── collapseGroups.16.svg
│ │ ├── convertColors.01.svg
│ │ ├── convertColors.02.svg
│ │ ├── convertColors.03.svg
│ │ ├── convertColors.04.svg
│ │ └── convertEllipseToCircle.01.svg
│ └── src
│ ├── collections.rs
│ ├── lib.rs
│ ├── parser.rs
│ ├── plugins
│ ├── cleanup_attrs.rs
│ ├── cleanup_enable_background.rs
│ ├── cleanup_ids.rs
│ ├── cleanup_numeric_values.rs
│ ├── collapse_groups.rs
│ ├── convert_colors.rs
│ ├── convert_ellipse_to_circle.rs
│ └── mod.rs
│ ├── stringifier.rs
│ └── testing.rs
├── index.d.ts
├── index.js
├── npm
├── darwin-arm64
│ ├── README.md
│ └── package.json
├── darwin-universal
│ ├── README.md
│ └── package.json
├── darwin-x64
│ ├── README.md
│ └── package.json
├── linux-x64-gnu
│ ├── README.md
│ └── package.json
└── win32-x64-msvc
│ ├── README.md
│ └── package.json
├── package.json
├── rustfmt.toml
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 |
6 | [*.{js,ts}]
7 | indent_style = tab
8 | indent_size = 2
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 | max_line_length = 80
13 |
14 | [*.{yml,yaml,json}]
15 | indent_style = space
16 | indent_size = 2
17 |
18 | [test/cases/parsing/bom/bomfile.{css,js}]
19 | charset = utf-8-bom
20 |
21 | [*.md]
22 | trim_trailing_whitespace = false
23 |
--------------------------------------------------------------------------------
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | env:
3 | DEBUG: napi:*
4 | APP_NAME: svgo-rs
5 | MACOSX_DEPLOYMENT_TARGET: '10.13'
6 | 'on':
7 | push:
8 | branches:
9 | - main
10 | tags-ignore:
11 | - '**'
12 | paths-ignore:
13 | - '**/*.md'
14 | - LICENSE
15 | - '**/*.gitignore'
16 | - .editorconfig
17 | - docs/**
18 | pull_request: null
19 | jobs:
20 | cargo-test:
21 | name: Test cargo
22 | runs-on: ubuntu-latest
23 | steps:
24 | - uses: actions/checkout@v3
25 | - name: Install Rust
26 | uses: dtolnay/rust-toolchain@stable
27 | with:
28 | toolchain: nightly-2023-06-27
29 | target: x86_64-unknown-linux-gnu
30 | - name: Run cargo test
31 | run: cargo test
32 | build:
33 | strategy:
34 | fail-fast: false
35 | matrix:
36 | settings:
37 | - host: macos-latest
38 | target: x86_64-apple-darwin
39 | build: |
40 | yarn build
41 | strip -x *.node
42 | - host: windows-latest
43 | build: yarn build
44 | target: x86_64-pc-windows-msvc
45 | - host: ubuntu-latest
46 | target: x86_64-unknown-linux-gnu
47 | docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
48 | build: |-
49 | set -e &&
50 | rustup default nightly-2023-06-27 &&
51 | yarn build --target x86_64-unknown-linux-gnu &&
52 | strip *.node
53 | - host: macos-latest
54 | target: aarch64-apple-darwin
55 | build: |
56 | yarn build --target aarch64-apple-darwin
57 | strip -x *.node
58 | name: stable - ${{ matrix.settings.target }} - node@18
59 | needs:
60 | - cargo-test
61 | runs-on: ${{ matrix.settings.host }}
62 | steps:
63 | - uses: actions/checkout@v3
64 | - name: Setup node
65 | uses: actions/setup-node@v3
66 | if: ${{ !matrix.settings.docker }}
67 | with:
68 | node-version: 18
69 | check-latest: true
70 | cache: yarn
71 | - name: Install
72 | uses: dtolnay/rust-toolchain@stable
73 | if: ${{ !matrix.settings.docker }}
74 | with:
75 | toolchain: nightly-2023-06-27
76 | targets: ${{ matrix.settings.target }}
77 | - name: Cache cargo
78 | uses: actions/cache@v3
79 | with:
80 | path: |
81 | ~/.cargo/registry/index/
82 | ~/.cargo/registry/cache/
83 | ~/.cargo/git/db/
84 | .cargo-cache
85 | target/
86 | key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }}
87 | - uses: goto-bus-stop/setup-zig@v2
88 | if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' }}
89 | with:
90 | version: 0.10.1
91 | - name: Setup toolchain
92 | run: ${{ matrix.settings.setup }}
93 | if: ${{ matrix.settings.setup }}
94 | shell: bash
95 | - name: Setup node x86
96 | if: matrix.settings.target == 'i686-pc-windows-msvc'
97 | run: yarn config set supportedArchitectures.cpu "ia32"
98 | shell: bash
99 | - name: Install dependencies
100 | run: yarn install
101 | - name: Setup node x86
102 | uses: actions/setup-node@v3
103 | if: matrix.settings.target == 'i686-pc-windows-msvc'
104 | with:
105 | node-version: 18
106 | check-latest: true
107 | cache: yarn
108 | architecture: x86
109 | - name: Build in docker
110 | uses: addnab/docker-run-action@v3
111 | if: ${{ matrix.settings.docker }}
112 | with:
113 | image: ${{ matrix.settings.docker }}
114 | options: '--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build'
115 | run: ${{ matrix.settings.build }}
116 | - name: Build
117 | run: ${{ matrix.settings.build }}
118 | if: ${{ !matrix.settings.docker }}
119 | shell: bash
120 | - name: Upload artifact
121 | uses: actions/upload-artifact@v3
122 | with:
123 | name: bindings-${{ matrix.settings.target }}
124 | path: ${{ env.APP_NAME }}.*.node
125 | if-no-files-found: error
126 | test-macOS-windows-binding:
127 | name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }}
128 | needs:
129 | - build
130 | strategy:
131 | fail-fast: false
132 | matrix:
133 | settings:
134 | - host: windows-latest
135 | target: x86_64-pc-windows-msvc
136 | node:
137 | - '14'
138 | - '16'
139 | - '18'
140 | runs-on: ${{ matrix.settings.host }}
141 | steps:
142 | - uses: actions/checkout@v3
143 | - name: Setup node
144 | uses: actions/setup-node@v3
145 | with:
146 | node-version: ${{ matrix.node }}
147 | check-latest: true
148 | cache: yarn
149 | - name: Install dependencies
150 | run: yarn install
151 | - name: Download artifacts
152 | uses: actions/download-artifact@v3
153 | with:
154 | name: bindings-${{ matrix.settings.target }}
155 | path: .
156 | - name: List packages
157 | run: ls -R .
158 | shell: bash
159 | - name: Test bindings
160 | run: yarn test
161 | test-linux-x64-gnu-binding:
162 | name: Test bindings on Linux-x64-gnu - node@${{ matrix.node }}
163 | needs:
164 | - build
165 | strategy:
166 | fail-fast: false
167 | matrix:
168 | node:
169 | - '14'
170 | - '16'
171 | - '18'
172 | runs-on: ubuntu-latest
173 | steps:
174 | - uses: actions/checkout@v3
175 | - name: Setup node
176 | uses: actions/setup-node@v3
177 | with:
178 | node-version: ${{ matrix.node }}
179 | check-latest: true
180 | cache: yarn
181 | - name: Install dependencies
182 | run: yarn install
183 | - name: Download artifacts
184 | uses: actions/download-artifact@v3
185 | with:
186 | name: bindings-x86_64-unknown-linux-gnu
187 | path: .
188 | - name: List packages
189 | run: ls -R .
190 | shell: bash
191 | - name: Test bindings
192 | run: docker run --rm -v $(pwd):/build -w /build node:${{ matrix.node }}-slim yarn test
193 | universal-macOS:
194 | name: Build universal macOS binary
195 | needs:
196 | - build
197 | runs-on: macos-latest
198 | steps:
199 | - uses: actions/checkout@v3
200 | - name: Setup node
201 | uses: actions/setup-node@v3
202 | with:
203 | node-version: 18
204 | check-latest: true
205 | cache: yarn
206 | - name: Install dependencies
207 | run: yarn install
208 | - name: Download macOS x64 artifact
209 | uses: actions/download-artifact@v3
210 | with:
211 | name: bindings-x86_64-apple-darwin
212 | path: artifacts
213 | - name: Download macOS arm64 artifact
214 | uses: actions/download-artifact@v3
215 | with:
216 | name: bindings-aarch64-apple-darwin
217 | path: artifacts
218 | - name: Combine binaries
219 | run: yarn universal
220 | - name: Upload artifact
221 | uses: actions/upload-artifact@v3
222 | with:
223 | name: bindings-universal-apple-darwin
224 | path: ${{ env.APP_NAME }}.*.node
225 | if-no-files-found: error
226 | publish:
227 | name: Publish
228 | runs-on: ubuntu-latest
229 | needs:
230 | - test-macOS-windows-binding
231 | - universal-macOS
232 | steps:
233 | - uses: actions/checkout@v3
234 | - name: Setup node
235 | uses: actions/setup-node@v3
236 | with:
237 | node-version: 18
238 | check-latest: true
239 | cache: yarn
240 | - name: Install dependencies
241 | run: yarn install
242 | - name: Download all artifacts
243 | uses: actions/download-artifact@v3
244 | with:
245 | path: artifacts
246 | - name: Move artifacts
247 | run: yarn artifacts
248 | - name: List packages
249 | run: ls -R ./npm
250 | shell: bash
251 | - name: Publish
252 | run: |
253 | if git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+$";
254 | then
255 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
256 | npm publish --access public
257 | elif git log -1 --pretty=%B | grep "^[0-9]\+\.[0-9]\+\.[0-9]\+";
258 | then
259 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
260 | npm publish --tag next --access public
261 | else
262 | echo "Not a release, skipping publish"
263 | fi
264 | env:
265 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
266 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
267 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/node
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node
3 |
4 | ### Node ###
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # Diagnostic reports (https://nodejs.org/api/report.html)
14 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
15 |
16 | # Runtime data
17 | pids
18 | *.pid
19 | *.seed
20 | *.pid.lock
21 |
22 | # Directory for instrumented libs generated by jscoverage/JSCover
23 | lib-cov
24 |
25 | # Coverage directory used by tools like istanbul
26 | coverage
27 | *.lcov
28 |
29 | # nyc test coverage
30 | .nyc_output
31 |
32 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
33 | .grunt
34 |
35 | # Bower dependency directory (https://bower.io/)
36 | bower_components
37 |
38 | # node-waf configuration
39 | .lock-wscript
40 |
41 | # Compiled binary addons (https://nodejs.org/api/addons.html)
42 | build/Release
43 |
44 | # Dependency directories
45 | node_modules/
46 | jspm_packages/
47 |
48 | # TypeScript v1 declaration files
49 | typings/
50 |
51 | # TypeScript cache
52 | *.tsbuildinfo
53 |
54 | # Optional npm cache directory
55 | .npm
56 |
57 | # Optional eslint cache
58 | .eslintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variables file
76 | .env
77 | .env.test
78 |
79 | # parcel-bundler cache (https://parceljs.org/)
80 | .cache
81 |
82 | # Next.js build output
83 | .next
84 |
85 | # Nuxt.js build / generate output
86 | .nuxt
87 | dist
88 |
89 | # Gatsby files
90 | .cache/
91 | # Comment in the public line in if your project uses Gatsby and not Next.js
92 | # https://nextjs.org/blog/next-9-1#public-directory-support
93 | # public
94 |
95 | # vuepress build output
96 | .vuepress/dist
97 |
98 | # Serverless directories
99 | .serverless/
100 |
101 | # FuseBox cache
102 | .fusebox/
103 |
104 | # DynamoDB Local files
105 | .dynamodb/
106 |
107 | # TernJS port file
108 | .tern-port
109 |
110 | # Stores VSCode versions used for testing VSCode extensions
111 | .vscode-test
112 |
113 | # End of https://www.toptal.com/developers/gitignore/api/node
114 |
115 | # Created by https://www.toptal.com/developers/gitignore/api/macos
116 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos
117 |
118 | ### macOS ###
119 | # General
120 | .DS_Store
121 | .AppleDouble
122 | .LSOverride
123 |
124 | # Icon must end with two
125 | Icon
126 |
127 |
128 | # Thumbnails
129 | ._*
130 |
131 | # Files that might appear in the root of a volume
132 | .DocumentRevisions-V100
133 | .fseventsd
134 | .Spotlight-V100
135 | .TemporaryItems
136 | .Trashes
137 | .VolumeIcon.icns
138 | .com.apple.timemachine.donotpresent
139 |
140 | # Directories potentially created on remote AFP share
141 | .AppleDB
142 | .AppleDesktop
143 | Network Trash Folder
144 | Temporary Items
145 | .apdisk
146 |
147 | ### macOS Patch ###
148 | # iCloud generated files
149 | *.icloud
150 |
151 | # End of https://www.toptal.com/developers/gitignore/api/macos
152 |
153 | # Created by https://www.toptal.com/developers/gitignore/api/windows
154 | # Edit at https://www.toptal.com/developers/gitignore?templates=windows
155 |
156 | ### Windows ###
157 | # Windows thumbnail cache files
158 | Thumbs.db
159 | Thumbs.db:encryptable
160 | ehthumbs.db
161 | ehthumbs_vista.db
162 |
163 | # Dump file
164 | *.stackdump
165 |
166 | # Folder config file
167 | [Dd]esktop.ini
168 |
169 | # Recycle Bin used on file shares
170 | $RECYCLE.BIN/
171 |
172 | # Windows Installer files
173 | *.cab
174 | *.msi
175 | *.msix
176 | *.msm
177 | *.msp
178 |
179 | # Windows shortcuts
180 | *.lnk
181 |
182 | # End of https://www.toptal.com/developers/gitignore/api/windows
183 |
184 | #Added by cargo
185 |
186 | /target
187 |
188 | .pnp.*
189 | .yarn/*
190 | !.yarn/patches
191 | !.yarn/plugins
192 | !.yarn/releases
193 | !.yarn/sdks
194 | !.yarn/versions
195 |
196 | *.node
197 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | yarnPath: .yarn/releases/yarn-3.5.1.cjs
4 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "crates/core",
4 | "crates/binding"
5 | ]
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 svg-rust
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
SVGO RS
2 |
3 | Speedy SVGO rewritten in Rust 🦀
4 |
5 | > ⚠️ RVGO RS is in early development and should not be used in production, expect bugs! 🐛
6 |
--------------------------------------------------------------------------------
/__test__/index.spec.ts:
--------------------------------------------------------------------------------
1 | import test from 'ava'
2 | import { optimize } from '..'
3 |
4 | test('basic', (t) => {
5 | const svg = `
6 |
9 | `
10 | const output = optimize(svg)
11 | t.snapshot(output)
12 | })
13 |
--------------------------------------------------------------------------------
/__test__/index.spec.ts.md:
--------------------------------------------------------------------------------
1 | # Snapshot report for `__test__/index.spec.ts`
2 |
3 | The actual snapshot is saved in `index.spec.ts.snap`.
4 |
5 | Generated by [AVA](https://avajs.dev).
6 |
7 | ## basic
8 |
9 | > Snapshot 1
10 |
11 | {
12 | data: `␊
15 | `,
16 | }
17 |
--------------------------------------------------------------------------------
/__test__/index.spec.ts.snap:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/svg-rust/svgo-rs/0062a5fd746e55e1a78f3361edf3b43512fdac44/__test__/index.spec.ts.snap
--------------------------------------------------------------------------------
/crates/binding/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | authors = ["SyMind "]
3 | name = "binding"
4 | version = "0.0.0"
5 | edition = "2021"
6 | publish = false
7 | license = "MIT"
8 |
9 | [lib]
10 | bench = false
11 | crate-type = ["cdylib"]
12 |
13 | [dependencies]
14 | napi = { version = "2.12.0", default-features = false, features = ["napi4"] }
15 | napi-derive = "2.12.2"
16 | svgo-rs = { path = "../core", features = ["node"] }
17 |
18 | [build-dependencies]
19 | napi-build = "2.0.1"
20 |
21 | [profile.release]
22 | lto = true
23 |
--------------------------------------------------------------------------------
/crates/binding/build.rs:
--------------------------------------------------------------------------------
1 | extern crate napi_build;
2 |
3 | fn main() {
4 | napi_build::setup();
5 | }
6 |
--------------------------------------------------------------------------------
/crates/binding/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![deny(clippy::all)]
2 |
3 | #[macro_use]
4 | extern crate napi_derive;
5 |
6 | use svgo_rs::{optimize as optimize_core, Output};
7 |
8 | /// The core of SVGO RS
9 | #[napi]
10 | pub fn optimize(input: String) -> Output {
11 | optimize_core(input)
12 | }
13 |
--------------------------------------------------------------------------------
/crates/core/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | authors = ["SyMind "]
3 | description = "A tool for optimizing SVG vector graphics files"
4 | name = "svgo-rs"
5 | edition = "2021"
6 | license = "MIT"
7 | repository = "https://github.com/svg-rust/svgo-rs.git"
8 | version = "0.0.0"
9 |
10 | [features]
11 | node = ["dep:napi", "dep:napi-derive"]
12 |
13 | [dependencies]
14 | # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
15 | napi = { version = "2.12.0", default-features = false, features = ["napi4"], optional = true }
16 | napi-derive = { version = "2.12.2", optional = true }
17 |
18 | regex = "1.8.1"
19 | serde = { version = "1", features = ["derive"] }
20 | serde_json = "1"
21 | swc_core = { version = "0.78.28", features = [
22 | "ecma_ast",
23 | "ecma_ast_serde",
24 | "common_concurrent",
25 | "bundler",
26 | "ecma_loader",
27 | "ecma_transforms",
28 | "ecma_visit",
29 | "ecma_codegen",
30 | "base_node",
31 | "__parser",
32 | ] }
33 | swc_xml_ast = "0.10.17"
34 | swc_xml_codegen = "0.11.22"
35 | swc_xml_parser = "0.11.20"
36 | swc_xml_visit = "0.10.17"
37 | linked-hash-map = "0.5.6"
38 |
39 | [dev-dependencies]
40 | pretty_assertions = "1.3.0"
41 | testing = "0.33.19"
42 |
43 | [profile.release]
44 | lto = true
45 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.01.svg:
--------------------------------------------------------------------------------
1 |
26 |
27 | @@@
28 |
29 |
54 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.02.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.03.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.04.svg:
--------------------------------------------------------------------------------
1 |
219 |
220 | @@@
221 |
222 |
440 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.05.svg:
--------------------------------------------------------------------------------
1 |
18 |
19 | @@@
20 |
21 |
38 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.06.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
17 | @@@
18 |
19 | {"force": true}
20 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.07.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
17 | @@@
18 |
19 | {"force": true}
20 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.08.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
17 | @@@
18 |
19 | {"preserve": ["circle", "rect"]}
20 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.09.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 | @@@
11 |
12 |
20 |
21 | @@@
22 |
23 | {"force": true, "preserve": ["circle", "rect"]}
24 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.10.svg:
--------------------------------------------------------------------------------
1 |
15 |
16 | @@@
17 |
18 |
32 |
33 | @@@
34 |
35 | {"force": true, "preserve": ["figure"]}
36 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.11.svg:
--------------------------------------------------------------------------------
1 |
8 |
9 | @@@
10 |
11 |
18 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.12.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
17 | @@@
18 |
19 | {"preservePrefixes": ["xyz"]}
20 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.13.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 | @@@
11 |
12 |
20 |
21 | @@@
22 |
23 | {"force": true, "preservePrefixes": ["pre1_", "pre2_"]}
24 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.14.svg:
--------------------------------------------------------------------------------
1 |
15 |
16 | @@@
17 |
18 |
32 |
33 | @@@
34 |
35 | {"force": true, "preservePrefixes": ["pre1_"]}
36 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.15.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
17 | @@@
18 |
19 | {"preserve": ["circle"], "preservePrefixes": ["suffix", "rect"]}
20 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.16.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 | @@@
11 |
12 |
20 |
21 | @@@
22 |
23 | {"preserve": ["a"]}
24 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.17.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 | @@@
11 |
12 |
20 |
21 | @@@
22 |
23 | {"preservePrefixes": ["a"]}
24 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.18.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 | @@@
11 |
12 |
20 |
21 | @@@
22 |
23 | {"preservePrefixes": ["a"]}
24 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.19.svg:
--------------------------------------------------------------------------------
1 |
11 |
12 | @@@
13 |
14 |
24 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.20.svg:
--------------------------------------------------------------------------------
1 |
18 |
19 | @@@
20 |
21 |
38 |
39 | @@@
40 |
41 | {"remove": false}
42 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupIds.21.svg:
--------------------------------------------------------------------------------
1 |
21 |
22 | @@@
23 |
24 |
44 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupNumericValues.01.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 | @@@
6 |
7 |
10 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/cleanupNumericValues.02.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 | @@@
6 |
7 |
10 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.01.svg:
--------------------------------------------------------------------------------
1 | Collapse groups without attributes
2 |
3 | ===
4 |
5 |
12 |
13 | @@@
14 |
15 |
18 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.02.svg:
--------------------------------------------------------------------------------
1 | Inherit attributes to single child
2 |
3 | ===
4 |
5 |
29 |
30 | @@@
31 |
32 |
44 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.06.svg:
--------------------------------------------------------------------------------
1 | Remove inheritable overriden groups attributes
2 |
3 | ===
4 |
5 |
13 |
14 | @@@
15 |
16 |
22 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.07.svg:
--------------------------------------------------------------------------------
1 | Remove equal overriden groups attributes
2 |
3 | ===
4 |
5 |
15 |
16 | @@@
17 |
18 |
26 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.08.svg:
--------------------------------------------------------------------------------
1 | Combine own child transform and inherited
2 |
3 | ===
4 |
5 |
13 |
14 | @@@
15 |
16 |
22 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.09.svg:
--------------------------------------------------------------------------------
1 | Preserve transform when group has clip-path
2 |
3 | ===
4 |
5 |
29 |
30 | @@@
31 |
32 |
52 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.11.svg:
--------------------------------------------------------------------------------
1 | Preserve groups when clip-path and mask are used without any other attributes
2 |
3 | ===
4 |
5 |
17 |
18 | @@@
19 |
20 |
32 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.12.svg:
--------------------------------------------------------------------------------
1 | Preserve groups with id attribute or animation elements inside
2 |
3 | ===
4 |
5 |
24 |
25 | @@@
26 |
27 |
46 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.13.svg:
--------------------------------------------------------------------------------
1 | Preserve groups with classes
2 |
3 | ===
4 |
5 |
17 |
18 | @@@
19 |
20 |
30 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.14.svg:
--------------------------------------------------------------------------------
1 |
15 |
16 | @@@
17 |
18 |
19 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.15.svg:
--------------------------------------------------------------------------------
1 |
9 |
10 | @@@
11 |
12 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/collapseGroups.16.svg:
--------------------------------------------------------------------------------
1 |
8 |
9 | @@@
10 |
11 |
16 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/convertColors.01.svg:
--------------------------------------------------------------------------------
1 |
8 |
9 | @@@
10 |
11 |
18 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/convertColors.02.svg:
--------------------------------------------------------------------------------
1 |
4 |
5 | @@@
6 |
7 |
10 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/convertColors.03.svg:
--------------------------------------------------------------------------------
1 |
5 |
6 | @@@
7 |
8 |
12 |
13 | @@@
14 |
15 | { "shorthex": false }
16 |
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/convertColors.04.svg:
--------------------------------------------------------------------------------
1 |
10 |
11 | @@@
12 |
13 |
22 |
23 | @@@
24 |
25 | { "currentColor": true }
--------------------------------------------------------------------------------
/crates/core/__fixture__/plugins/convertEllipseToCircle.01.svg:
--------------------------------------------------------------------------------
1 |
7 |
8 | @@@
9 |
10 |
16 |
--------------------------------------------------------------------------------
/crates/core/src/collections.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | // https://www.w3.org/TR/SVG11/intro.html#Definitions
4 | pub fn get_elems_groups() -> HashMap<&'static str, Vec<&'static str>> {
5 | HashMap::from([
6 | ("animation", vec![
7 | "animate",
8 | "animateColor",
9 | "animateMotion",
10 | "animateTransform",
11 | "set",
12 | ]),
13 | ("descriptive", vec!["desc", "metadata", "title"]),
14 | ("shape", vec!["circle", "ellipse", "line", "path", "polygon", "polyline", "rect"]),
15 | ("structural", vec!["defs", "g", "svg", "symbol", "use"]),
16 | ("paintServer", vec![
17 | "solidColor",
18 | "linearGradient",
19 | "radialGradient",
20 | "meshGradient",
21 | "pattern",
22 | "hatch",
23 | ]),
24 | ("nonRendering", vec![
25 | "linearGradient",
26 | "radialGradient",
27 | "pattern",
28 | "clipPath",
29 | "mask",
30 | "marker",
31 | "symbol",
32 | "filter",
33 | "solidColor",
34 | ]),
35 | ("container", vec![
36 | "a",
37 | "defs",
38 | "g",
39 | "marker",
40 | "mask",
41 | "missing-glyph",
42 | "pattern",
43 | "svg",
44 | "switch",
45 | "symbol",
46 | "foreignObject",
47 | ]),
48 | ("textContent", vec![
49 | "altGlyph",
50 | "altGlyphDef",
51 | "altGlyphItem",
52 | "glyph",
53 | "glyphRef",
54 | "textPath",
55 | "text",
56 | "tref",
57 | "tspan",
58 | ]),
59 | ("textContentChild", vec!["altGlyph", "textPath", "tref", "tspan"]),
60 | ("lightSource", vec![
61 | "feDiffuseLighting",
62 | "feSpecularLighting",
63 | "feDistantLight",
64 | "fePointLight",
65 | "feSpotLight",
66 | ]),
67 | ("filterPrimitive", vec![
68 | "feBlend",
69 | "feColorMatrix",
70 | "feComponentTransfer",
71 | "feComposite",
72 | "feConvolveMatrix",
73 | "feDiffuseLighting",
74 | "feDisplacementMap",
75 | "feDropShadow",
76 | "feFlood",
77 | "feFuncA",
78 | "feFuncB",
79 | "feFuncG",
80 | "feFuncR",
81 | "feGaussianBlur",
82 | "feImage",
83 | "feMerge",
84 | "feMergeNode",
85 | "feMorphology",
86 | "feOffset",
87 | "feSpecularLighting",
88 | "feTile",
89 | "feTurbulence",
90 | ]),
91 | ])
92 | }
93 |
94 | pub fn get_text_elems() -> Vec<&'static str> {
95 | let mut elems_groups = get_elems_groups();
96 | let text_elems = elems_groups.get_mut("textContent").unwrap();
97 | text_elems.push("title");
98 | text_elems.to_vec()
99 | }
100 |
101 | // https://www.w3.org/TR/SVG11/propidx.html
102 | pub fn get_inheritable_attrs() -> Vec<&'static str> {
103 | vec![
104 | "clip-rule",
105 | "color",
106 | "color-interpolation",
107 | "color-interpolation-filters",
108 | "color-profile",
109 | "color-rendering",
110 | "cursor",
111 | "direction",
112 | "dominant-baseline",
113 | "fill",
114 | "fill-opacity",
115 | "fill-rule",
116 | "font",
117 | "font-family",
118 | "font-size",
119 | "font-size-adjust",
120 | "font-stretch",
121 | "font-style",
122 | "font-variant",
123 | "font-weight",
124 | "glyph-orientation-horizontal",
125 | "glyph-orientation-vertical",
126 | "image-rendering",
127 | "letter-spacing",
128 | "marker",
129 | "marker-end",
130 | "marker-mid",
131 | "marker-start",
132 | "paint-order",
133 | "pointer-events",
134 | "shape-rendering",
135 | "stroke",
136 | "stroke-dasharray",
137 | "stroke-dashoffset",
138 | "stroke-linecap",
139 | "stroke-linejoin",
140 | "stroke-miterlimit",
141 | "stroke-opacity",
142 | "stroke-width",
143 | "text-anchor",
144 | "text-rendering",
145 | "transform",
146 | "visibility",
147 | "word-spacing",
148 | "writing-mode",
149 | ]
150 | }
151 |
152 | // https://www.w3.org/TR/SVG11/linking.html#processingIRI
153 | pub fn get_references_props() -> Vec<&'static str> {
154 | vec![
155 | "clip-path",
156 | "color-profile",
157 | "fill",
158 | "filter",
159 | "marker-start",
160 | "marker-mid",
161 | "marker-end",
162 | "mask",
163 | "stroke",
164 | "style",
165 | ]
166 | }
167 |
168 | pub fn get_colors_names() -> HashMap<&'static str, &'static str> {
169 | HashMap::from([
170 | ("aliceblue", "#f0f8ff"),
171 | ("antiquewhite", "#faebd7"),
172 | ("aqua", "#0ff"),
173 | ("aquamarine", "#7fffd4"),
174 | ("azure", "#f0ffff"),
175 | ("beige", "#f5f5dc"),
176 | ("bisque", "#ffe4c4"),
177 | ("black", "#000"),
178 | ("blanchedalmond", "#ffebcd"),
179 | ("blue", "#00f"),
180 | ("blueviolet", "#8a2be2"),
181 | ("brown", "#a52a2a"),
182 | ("burlywood", "#deb887"),
183 | ("cadetblue", "#5f9ea0"),
184 | ("chartreuse", "#7fff00"),
185 | ("chocolate", "#d2691e"),
186 | ("coral", "#ff7f50"),
187 | ("cornflowerblue", "#6495ed"),
188 | ("cornsilk", "#fff8dc"),
189 | ("crimson", "#dc143c"),
190 | ("cyan", "#0ff"),
191 | ("darkblue", "#00008b"),
192 | ("darkcyan", "#008b8b"),
193 | ("darkgoldenrod", "#b8860b"),
194 | ("darkgray", "#a9a9a9"),
195 | ("darkgreen", "#006400"),
196 | ("darkgrey", "#a9a9a9"),
197 | ("darkkhaki", "#bdb76b"),
198 | ("darkmagenta", "#8b008b"),
199 | ("darkolivegreen", "#556b2f"),
200 | ("darkorange", "#ff8c00"),
201 | ("darkorchid", "#9932cc"),
202 | ("darkred", "#8b0000"),
203 | ("darksalmon", "#e9967a"),
204 | ("darkseagreen", "#8fbc8f"),
205 | ("darkslateblue", "#483d8b"),
206 | ("darkslategray", "#2f4f4f"),
207 | ("darkslategrey", "#2f4f4f"),
208 | ("darkturquoise", "#00ced1"),
209 | ("darkviolet", "#9400d3"),
210 | ("deeppink", "#ff1493"),
211 | ("deepskyblue", "#00bfff"),
212 | ("dimgray", "#696969"),
213 | ("dimgrey", "#696969"),
214 | ("dodgerblue", "#1e90ff"),
215 | ("firebrick", "#b22222"),
216 | ("floralwhite", "#fffaf0"),
217 | ("forestgreen", "#228b22"),
218 | ("fuchsia", "#f0f"),
219 | ("gainsboro", "#dcdcdc"),
220 | ("ghostwhite", "#f8f8ff"),
221 | ("gold", "#ffd700"),
222 | ("goldenrod", "#daa520"),
223 | ("gray", "#808080"),
224 | ("green", "#008000"),
225 | ("greenyellow", "#adff2f"),
226 | ("grey", "#808080"),
227 | ("honeydew", "#f0fff0"),
228 | ("hotpink", "#ff69b4"),
229 | ("indianred", "#cd5c5c"),
230 | ("indigo", "#4b0082"),
231 | ("ivory", "#fffff0"),
232 | ("khaki", "#f0e68c"),
233 | ("lavender", "#e6e6fa"),
234 | ("lavenderblush", "#fff0f5"),
235 | ("lawngreen", "#7cfc00"),
236 | ("lemonchiffon", "#fffacd"),
237 | ("lightblue", "#add8e6"),
238 | ("lightcoral", "#f08080"),
239 | ("lightcyan", "#e0ffff"),
240 | ("lightgoldenrodyellow", "#fafad2"),
241 | ("lightgray", "#d3d3d3"),
242 | ("lightgreen", "#90ee90"),
243 | ("lightgrey", "#d3d3d3"),
244 | ("lightpink", "#ffb6c1"),
245 | ("lightsalmon", "#ffa07a"),
246 | ("lightseagreen", "#20b2aa"),
247 | ("lightskyblue", "#87cefa"),
248 | ("lightslategray", "#789"),
249 | ("lightslategrey", "#789"),
250 | ("lightsteelblue", "#b0c4de"),
251 | ("lightyellow", "#ffffe0"),
252 | ("lime", "#0f0"),
253 | ("limegreen", "#32cd32"),
254 | ("linen", "#faf0e6"),
255 | ("magenta", "#f0f"),
256 | ("maroon", "#800000"),
257 | ("mediumaquamarine", "#66cdaa"),
258 | ("mediumblue", "#0000cd"),
259 | ("mediumorchid", "#ba55d3"),
260 | ("mediumpurple", "#9370db"),
261 | ("mediumseagreen", "#3cb371"),
262 | ("mediumslateblue", "#7b68ee"),
263 | ("mediumspringgreen", "#00fa9a"),
264 | ("mediumturquoise", "#48d1cc"),
265 | ("mediumvioletred", "#c71585"),
266 | ("midnightblue", "#191970"),
267 | ("mintcream", "#f5fffa"),
268 | ("mistyrose", "#ffe4e1"),
269 | ("moccasin", "#ffe4b5"),
270 | ("navajowhite", "#ffdead"),
271 | ("navy", "#000080"),
272 | ("oldlace", "#fdf5e6"),
273 | ("olive", "#808000"),
274 | ("olivedrab", "#6b8e23"),
275 | ("orange", "#ffa500"),
276 | ("orangered", "#ff4500"),
277 | ("orchid", "#da70d6"),
278 | ("palegoldenrod", "#eee8aa"),
279 | ("palegreen", "#98fb98"),
280 | ("paleturquoise", "#afeeee"),
281 | ("palevioletred", "#db7093"),
282 | ("papayawhip", "#ffefd5"),
283 | ("peachpuff", "#ffdab9"),
284 | ("peru", "#cd853f"),
285 | ("pink", "#ffc0cb"),
286 | ("plum", "#dda0dd"),
287 | ("powderblue", "#b0e0e6"),
288 | ("purple", "#800080"),
289 | ("rebeccapurple", "#639"),
290 | ("red", "#f00"),
291 | ("rosybrown", "#bc8f8f"),
292 | ("royalblue", "#4169e1"),
293 | ("saddlebrown", "#8b4513"),
294 | ("salmon", "#fa8072"),
295 | ("sandybrown", "#f4a460"),
296 | ("seagreen", "#2e8b57"),
297 | ("seashell", "#fff5ee"),
298 | ("sienna", "#a0522d"),
299 | ("silver", "#c0c0c0"),
300 | ("skyblue", "#87ceeb"),
301 | ("slateblue", "#6a5acd"),
302 | ("slategray", "#708090"),
303 | ("slategrey", "#708090"),
304 | ("snow", "#fffafa"),
305 | ("springgreen", "#00ff7f"),
306 | ("steelblue", "#4682b4"),
307 | ("tan", "#d2b48c"),
308 | ("teal", "#008080"),
309 | ("thistle", "#d8bfd8"),
310 | ("tomato", "#ff6347"),
311 | ("turquoise", "#40e0d0"),
312 | ("violet", "#ee82ee"),
313 | ("wheat", "#f5deb3"),
314 | ("white", "#fff"),
315 | ("whitesmoke", "#f5f5f5"),
316 | ("yellow", "#ff0"),
317 | ("yellowgreen", "#9acd32"),
318 | ])
319 | }
320 |
321 | pub fn get_colors_short_names() -> HashMap<&'static str, &'static str> {
322 | HashMap::from([
323 | ("#f0ffff", "azure"),
324 | ("#f5f5dc", "beige"),
325 | ("#ffe4c4", "bisque"),
326 | ("#a52a2a", "brown"),
327 | ("#ff7f50", "coral"),
328 | ("#ffd700", "gold"),
329 | ("#808080", "gray"),
330 | ("#008000", "green"),
331 | ("#4b0082", "indigo"),
332 | ("#fffff0", "ivory"),
333 | ("#f0e68c", "khaki"),
334 | ("#faf0e6", "linen"),
335 | ("#800000", "maroon"),
336 | ("#000080", "navy"),
337 | ("#808000", "olive"),
338 | ("#ffa500", "orange"),
339 | ("#da70d6", "orchid"),
340 | ("#cd853f", "peru"),
341 | ("#ffc0cb", "pink"),
342 | ("#dda0dd", "plum"),
343 | ("#800080", "purple"),
344 | ("#f00", "red"),
345 | ("#ff0000", "red"),
346 | ("#fa8072", "salmon"),
347 | ("#a0522d", "sienna"),
348 | ("#c0c0c0", "silver"),
349 | ("#fffafa", "snow"),
350 | ("#d2b48c", "tan"),
351 | ("#008080", "teal"),
352 | ("#ff6347", "tomato"),
353 | ("#ee82ee", "violet"),
354 | ("#f5deb3", "wheat"),
355 | ])
356 | }
357 |
358 | // https://www.w3.org/TR/SVG11/single-page.html#types-DataTypeColor
359 | pub fn get_colors_props() -> Vec<&'static str> {
360 | vec![
361 | "color",
362 | "fill",
363 | "stroke",
364 | "stop-color",
365 | "flood-color",
366 | "lighting-color",
367 | ]
368 | }
369 |
--------------------------------------------------------------------------------
/crates/core/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![deny(clippy::all)]
2 |
3 | #[cfg(feature = "node")]
4 | #[macro_use]
5 | extern crate napi_derive;
6 |
7 | mod collections;
8 | mod parser;
9 | mod plugins;
10 | mod stringifier;
11 |
12 | #[cfg(test)]
13 | mod testing;
14 |
15 | use stringifier::{stringify_svg, StringifyOptions};
16 |
17 | #[cfg(feature = "node")]
18 | #[napi(object)]
19 | pub struct Output {
20 | pub data: String,
21 | }
22 |
23 | #[cfg(not(feature = "node"))]
24 | pub struct Output {
25 | pub data: String,
26 | }
27 |
28 | /// The core of SVGO
29 | pub fn optimize(input: String) -> Output {
30 | let mut doc = parser::parse_svg(input).unwrap();
31 |
32 | plugins::cleanup_attrs::apply(&mut doc);
33 | plugins::cleanup_enable_background::apply(&mut doc);
34 | plugins::cleanup_ids::apply(&mut doc, &Default::default());
35 | plugins::cleanup_numeric_values::apply(&mut doc, &Default::default());
36 |
37 | let data = stringify_svg(&doc, StringifyOptions {
38 | pretty: true,
39 | ..Default::default()
40 | });
41 | Output {
42 | data,
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/crates/core/src/parser.rs:
--------------------------------------------------------------------------------
1 | use std::sync::Arc;
2 |
3 | use regex::Regex;
4 | use swc_xml_ast::*;
5 | use swc_xml_visit::{VisitMut, VisitMutWith};
6 | use swc_xml_parser::{parse_file_as_document, parser, error::Error};
7 | use swc_core::common::{SourceMap, FileName};
8 |
9 | use crate::collections::get_text_elems;
10 |
11 | struct Visitor {
12 | text_elems: Vec<&'static str>,
13 | }
14 |
15 | impl VisitMut for Visitor {
16 | fn visit_mut_comment(&mut self, n: &mut Comment) {
17 | n.data = n.data.to_string().trim().into();
18 | }
19 |
20 | fn visit_mut_element(&mut self, n: &mut Element) {
21 | let mut children: Vec = vec![];
22 | n.children.iter_mut().for_each(|child| {
23 | if let Child::Text(text) = child {
24 | if self.text_elems.contains(&n.tag_name.to_string().as_str()) {
25 | children.push(child.clone())
26 | } else {
27 | let re = Regex::new(r"\S").unwrap();
28 | if re.is_match(&text.data.to_string()) {
29 | text.data = text.data.to_string().trim().into();
30 | children.push(child.clone())
31 | }
32 | }
33 | } else {
34 | children.push(child.clone())
35 | }
36 | });
37 | n.children = children;
38 |
39 | n.visit_mut_children_with(self)
40 | }
41 | }
42 |
43 | impl Visitor {
44 | fn new() -> Self {
45 | Self {
46 | text_elems: get_text_elems(),
47 | }
48 | }
49 | }
50 |
51 | pub fn parse_svg(input: String) -> Result {
52 | let cm = Arc::::default();
53 | let fm = cm.new_source_file(FileName::Anon, input);
54 |
55 | let mut errors = vec![];
56 | let mut r = parse_file_as_document(
57 | &fm,
58 | parser::ParserConfig::default(),
59 | &mut errors
60 | );
61 |
62 | match &mut r {
63 | Ok(doc) => {
64 | let mut v = Visitor::new();
65 | doc.visit_mut_with(&mut v);
66 | r
67 | },
68 | Err(_) => r
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/crates/core/src/plugins/cleanup_attrs.rs:
--------------------------------------------------------------------------------
1 | // cleanups attributes from newlines, trailing and repeating spaces
2 |
3 | use swc_xml_ast::*;
4 | use swc_xml_visit::{VisitMut, VisitMutWith};
5 | use regex::{Regex, Captures};
6 |
7 | struct Visitor {
8 | newlines: bool,
9 | trim: bool,
10 | spaces: bool,
11 | }
12 |
13 | impl Default for Visitor {
14 | fn default() -> Self {
15 | Self {
16 | newlines: true,
17 | trim: true,
18 | spaces: true,
19 | }
20 | }
21 | }
22 |
23 | impl VisitMut for Visitor {
24 | fn visit_mut_element(&mut self, n: &mut Element) {
25 | n.visit_mut_children_with(self);
26 |
27 | for attr in n.attributes.iter_mut() {
28 | if attr.value.is_none() {
29 | break;
30 | }
31 |
32 | let mut value = attr.value.clone().unwrap().to_string();
33 |
34 | if self.newlines {
35 | // new line which requires a space instead of themselve
36 | let reg_newlines_need_space = Regex::new(r#"(\S)\r?\n(\S)"#).unwrap();
37 | value = reg_newlines_need_space.replace_all(&value, |caps: &Captures| format!("{} {}", &caps[1], &caps[2])).to_string();
38 |
39 | // simple new line
40 | let reg_new_lines = Regex::new(r#"\r?\n"#).unwrap();
41 | value = reg_new_lines.replace_all(&value, |_: &Captures| "").to_string()
42 | }
43 |
44 | if self.trim {
45 | value = value.trim().to_string();
46 | }
47 |
48 | if self.spaces {
49 | let reg_spaces = Regex::new(r#"\s{2,}"#).unwrap();
50 | value = reg_spaces.replace_all(&value, |_: &Captures| " ").to_string()
51 | }
52 |
53 | attr.value = Some(value.into());
54 | }
55 | }
56 | }
57 |
58 | pub fn apply(doc: &mut Document) {
59 | let mut v: Visitor = Default::default();
60 | doc.visit_mut_with(&mut v);
61 | }
62 |
63 | #[cfg(test)]
64 | mod tests {
65 | #[cfg(test)]
66 | use pretty_assertions::assert_eq;
67 |
68 | use crate::parser::parse_svg;
69 | use crate::stringifier::{stringify_svg, StringifyOptions};
70 | use super::*;
71 |
72 | fn code_test(input: String, expected: String) {
73 | let mut doc = parse_svg(input).unwrap();
74 | apply(&mut doc);
75 | let result = stringify_svg(&doc, StringifyOptions {
76 | pretty: true,
77 | ..Default::default()
78 | });
79 | assert_eq!(result.trim_end(), expected);
80 | }
81 |
82 | #[test]
83 | fn test_1() {
84 | code_test(
85 | r#""#.to_string(),
90 | r#""#.to_string(),
93 | );
94 | }
95 |
96 | #[test]
97 | fn test_2() {
98 | code_test(
99 | r#""#.to_string(),
103 | r#""#.to_string(),
106 | );
107 | }
108 |
109 | #[test]
110 | fn test_3() {
111 | code_test(
112 | r#""#.to_string(),
120 | r#""#.to_string(),
125 | );
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/crates/core/src/plugins/cleanup_enable_background.rs:
--------------------------------------------------------------------------------
1 | // remove or cleanup enable-background attribute when possible
2 | //
3 | // @see https://www.w3.org/TR/SVG11/filters.html#EnableBackgroundProperty
4 | //
5 | // @example
6 | //