├── .codeclimate.yml ├── .editorconfig ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING ├── FUNDING.yml ├── ISSUE_TEMPLATE ├── PULL_REQUEST_TEMPLATE └── workflows │ └── test.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin └── svgicons2svgfont.js ├── eslint.config.js ├── fixtures ├── expected │ ├── calcbounds.svg │ ├── cleanicons-ascent.svg │ ├── cleanicons-high.svg │ ├── cleanicons-lig.svg │ ├── cleanicons-multi.svg │ ├── cleanicons-stw.svg │ ├── cleanicons.svg │ ├── hiddenpathesicons.svg │ ├── lotoficons-cli.svg │ ├── lotoficons.svg │ ├── multipathicons.svg │ ├── nestedicons-cli.svg │ ├── nestedicons.svg │ ├── onlywithrx.svg │ ├── onlywithry.svg │ ├── originalicons-cli.svg │ ├── originalicons.svg │ ├── originalicons2.svg │ ├── originalicons3.svg │ ├── originalicons4.svg │ ├── originalicons5.svg │ ├── originalicons6.svg │ ├── originalicons7.svg │ ├── originalicons8.svg │ ├── originaliconsn.svg │ ├── paesku.svg │ ├── pathfillnone.svg │ ├── prefixedicons.svg │ ├── realicons.svg │ ├── realworld.svg │ ├── rendricons.svg │ ├── rotatedrectangle.svg │ ├── roundedcorners.svg │ ├── scaledicons.svg │ ├── shapeicons.svg │ ├── singleicon.svg │ ├── skew.svg │ ├── test-codepoint.json │ ├── tocentericons.svg │ ├── toverticalcentericons.svg │ ├── transformedicons.svg │ ├── translatex.svg │ ├── variableheighticons.svg │ ├── variableheighticonsn.svg │ ├── variableheighticonsnp.svg │ ├── variablewidthicons.svg │ ├── variablewidthiconsid.svg │ └── variablewidthiconsn.svg └── icons │ ├── badicons │ └── pathdata.svg │ ├── calcbounds │ ├── arrow-left.svg │ └── arrow-right.svg │ ├── cleanicons │ ├── account.svg │ ├── arrow-down.svg │ ├── arrow-left.svg │ ├── arrow-right.svg │ ├── arrow-up.svg │ ├── basket.svg │ ├── close.svg │ ├── minus.svg │ ├── plus.svg │ └── search.svg │ ├── hiddenpathesicons │ ├── sound--off.svg │ └── sound--on.svg │ ├── multipathicons │ ├── basket.svg │ └── kikoolol.svg │ ├── nestedicons │ ├── arrow-down.svg │ ├── uEA01-arrow-left.svg │ ├── uEA01arrow-up.svg │ └── uEA03-arrow-right.svg │ ├── onlywithrx │ └── rounded.svg │ ├── onlywithry │ └── rounded.svg │ ├── originalicons │ ├── mute.svg │ ├── sound.svg │ └── speaker.svg │ ├── paesku │ └── chip.svg │ ├── pathfillnone │ └── account.svg │ ├── prefixedicons │ ├── uE008-arrow-down.svg │ ├── uE00F-arrow-left.svg │ ├── uE013-arrow-right.svg │ └── uE014-arrow-up.svg │ ├── realicons │ ├── diegoliv.svg │ ├── hannesjohansson.svg │ ├── roelvanhitum.svg │ ├── safety-icon.svg │ ├── sb-icon.svg │ ├── settings-icon.svg │ ├── track-icon.svg │ └── web-icon.svg │ ├── realworld │ └── omridevk.svg │ ├── rendricons │ ├── account.svg │ ├── basket.svg │ ├── close.svg │ ├── minus.svg │ ├── plus.svg │ └── search.svg │ ├── rotatedrectangle │ └── rotatedrectangle.svg │ ├── roundedcorners │ └── roundedrect.svg │ ├── scaledicons │ └── ic_search_black_48px.svg │ ├── shapeicons │ ├── circle.svg │ ├── ellipse.svg │ ├── lines.svg │ ├── polygon.svg │ ├── polyline.svg │ └── rect.svg │ ├── singleicon │ └── account.svg │ ├── skew │ ├── skew-x.svg │ └── skew-y.svg │ ├── tocentericons │ ├── bottomleft.svg │ ├── center.svg │ └── topright.svg │ ├── toverticalcentericons │ ├── bottomleft.svg │ ├── center.svg │ └── topright.svg │ ├── transformedicons │ ├── arrow-down.svg │ ├── arrow-left.svg │ ├── arrow-right.svg │ └── arrow-up.svg │ ├── translatex │ └── translatex.svg │ ├── variableheighticons │ ├── arrow-down.svg │ ├── arrow-left.svg │ ├── arrow-right.svg │ └── arrow-up.svg │ └── variablewidthicons │ ├── arrow-left.svg │ └── arrow-right.svg ├── package-lock.json ├── package.json ├── src ├── filesorter.ts ├── iconsdir.ts ├── index.ts ├── metadata.ts ├── svgshapes2svgpath.ts └── tests │ ├── cli.test.ts │ ├── filesorter.test.ts │ ├── index.test.ts │ └── metadata.test.ts └── tsconfig.json /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by a `metapak` 2 | # module. Do not change it here, your changes would 3 | # be overridden. 4 | 5 | engines: 6 | eslint: 7 | enabled: true 8 | 9 | ratings: 10 | paths: 11 | - "'src/**/*.ts'" 12 | ## Exclude test files. 13 | exclude_patterns: 14 | - "dist/" 15 | - "**/node_modules/" 16 | - "src/**/*.test.ts" 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by a `metapak` 2 | # module. Do not change it elsewhere, changes would 3 | # be overridden. 4 | 5 | # EditorConfig is awesome: http://EditorConfig.org 6 | 7 | # top-most EditorConfig file 8 | root = true 9 | 10 | # Unix-style newlines with a newline ending every file 11 | [*] 12 | end_of_line = lf 13 | insert_final_newline = true 14 | 15 | # Matches multiple files with brace expansion notation 16 | # Set default charset 17 | # 2 space indentation 18 | [*.{js,css}] 19 | charset = utf-8 20 | indent_style = space 21 | trim_trailing_whitespace = true 22 | indent_size = 2 23 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Be kind, except if I behave like an asshole, if so, tell me by linking to this 4 | file. 5 | 6 | I try hard to document and automate things so that you cannot create noises 7 | without really willing to do so. 8 | 9 | This is why I'll just delete issues/comments making be sad. 10 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Contributing to this project requires you to be 2 | a gentleman. 3 | 4 | By contributing you must agree with publishing your 5 | changes into the same license that apply to the current code. 6 | 7 | You will find the license in the LICENSE file at 8 | the root of this repository. 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [nfroidure] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | ## Issue 2 | 16 | 17 | I'm a gentledev I: 18 | - [ ] fully read the README recently 19 | - [ ] searched for existing issues 20 | - [ ] checked I'm up to date with the latest version of the project 21 | 22 | ### Expected behavior 23 | 24 | ### Actual behavior 25 | 26 | ### Steps to reproduce the behavior 27 | 28 | ### Debugging informations 29 | - `node -v` result: 30 | ``` 31 | 32 | ``` 33 | 34 | - `npm -v` result: 35 | ``` 36 | 37 | ``` 38 | If the result is lower than 20.11.1, there is 39 | poor chances I even have a look to it. Please, 40 | use the last [NodeJS LTS version](https://nodejs.org/en/). 41 | 42 | ## Feature request 43 | 54 | 55 | ### Feature description 56 | 57 | ### Use cases 58 | 59 | - [ ] I will/did implement the feature 60 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 10 | 11 | Fixes # 12 | 13 | ### Proposed changes 14 | - 15 | - 16 | 17 | 18 | 19 | ### Code quality 20 | - [ ] I made some tests for my changes 21 | - [ ] I added my name in the 22 | [contributors](https://docs.npmjs.com/files/package.json#people-fields-author-contributors) 23 | field of the `package.json` file. Beware to use the same format than for the author field 24 | for the entries so that you'll get a mention in the `README.md` with a link to your website. 25 | 26 | ### License 27 | To get your contribution merged, you must check the following. 28 | 29 | - [ ] I read the project license in the LICENSE file 30 | - [ ] I agree with publishing under this project license 31 | 32 | 46 | ### Join 47 | - [ ] I wish to join the core team 48 | - [ ] I agree that with great powers comes responsibilities 49 | - [ ] I'm a nice person 50 | 51 | My NPM username: 52 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by a `metapak` 2 | # module. Do not change it here, your changes would 3 | # be overridden. 4 | 5 | name: Node.js CI 6 | 7 | on: 8 | push: 9 | branches: [main] 10 | pull_request: 11 | branches: [main] 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [20.x] 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Use Node.js ${{ matrix.node-version }} 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | cache: 'npm' 28 | - name: Install dependencies 29 | run: npm ci 30 | - name: Run pre-commit tests 31 | run: npm run precz 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by a `metapak` 2 | # module. Do not change it elsewhere, changes would 3 | # be overridden. 4 | 5 | # Created by https://www.gitignore.io/api/osx,node,linux 6 | 7 | ### Linux ### 8 | *~ 9 | 10 | # temporary files which can be created if a process still has a handle open of a deleted file 11 | .fuse_hidden* 12 | 13 | # KDE directory preferences 14 | .directory 15 | 16 | # Linux trash folder which might appear on any partition or disk 17 | .Trash-* 18 | 19 | # .nfs files are created when an open file is removed but is still being accessed 20 | .nfs* 21 | 22 | ### Node ### 23 | # Logs 24 | logs 25 | *.log 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # Runtime data 31 | pids 32 | *.pid 33 | *.seed 34 | *.pid.lock 35 | 36 | # Directory for instrumented libs generated by jscoverage/JSCover 37 | lib-cov 38 | 39 | # Coverage directory used by tools like istanbul 40 | coverage 41 | 42 | # nyc test coverage 43 | .nyc_output 44 | 45 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 46 | .grunt 47 | 48 | # Bower dependency directory (https://bower.io/) 49 | bower_components 50 | 51 | # node-waf configuration 52 | .lock-wscript 53 | 54 | # Compiled binary addons (https://nodejs.org/api/addons.html) 55 | build/Release 56 | 57 | # Dependency directories 58 | node_modules/ 59 | jspm_packages/ 60 | 61 | # TypeScript v1 declaration files 62 | typings/ 63 | 64 | # Optional npm cache directory 65 | .npm 66 | 67 | # Optional eslint cache 68 | .eslintcache 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variables file 80 | .env 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | 85 | # next.js build output 86 | .next 87 | 88 | # nuxt.js build output 89 | .nuxt 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless 96 | 97 | ### OSX ### 98 | # General 99 | .DS_Store 100 | .AppleDouble 101 | .LSOverride 102 | 103 | # Icon must end with two \r 104 | Icon 105 | 106 | # Thumbnails 107 | ._* 108 | 109 | # Files that might appear in the root of a volume 110 | .DocumentRevisions-V100 111 | .fseventsd 112 | .Spotlight-V100 113 | .TemporaryItems 114 | .Trashes 115 | .VolumeIcon.icns 116 | .com.apple.timemachine.donotpresent 117 | 118 | # Directories potentially created on remote AFP share 119 | .AppleDB 120 | .AppleDesktop 121 | Network Trash Folder 122 | Temporary Items 123 | .apdisk 124 | 125 | 126 | # End of https://www.gitignore.io/api/osx,node,linux 127 | 128 | # Coveralls key 129 | .coveralls.yml 130 | 131 | # Project custom ignored file 132 | dist 133 | fixtures/results 134 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "gruntfuggly.todo-tree" 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [15.0.1](https://github.com/nfroidure/svgicons2svgfont/compare/v15.0.0...v15.0.1) (2025-02-26) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * replace deprecated transformation-matrix-js ([118572c](https://github.com/nfroidure/svgicons2svgfont/commit/118572c009f3e3e06da5eea0dac48e3cc296913b)) 7 | 8 | 9 | 10 | # [15.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v14.0.2...v15.0.0) (2024-11-07) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * **tests:** fix results folder creation ([6fcbc1e](https://github.com/nfroidure/svgicons2svgfont/commit/6fcbc1e9c7cf157e783527a1861c762733b3cfbb)) 16 | 17 | 18 | ### Code Refactoring 19 | 20 | * **cli:** uniformize CLI args style ([04e54a8](https://github.com/nfroidure/svgicons2svgfont/commit/04e54a8824d83575c7d4d17537e31c7c7425f83f)), closes [#167](https://github.com/nfroidure/svgicons2svgfont/issues/167) 21 | 22 | 23 | ### BREAKING CHANGES 24 | 25 | * **cli:** CLI usages will need an update 26 | 27 | 28 | 29 | ## [14.0.2](https://github.com/nfroidure/svgicons2svgfont/compare/v14.0.1...v14.0.2) (2024-09-08) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * fix circles convertion when no cx/cy ([13d9caf](https://github.com/nfroidure/svgicons2svgfont/commit/13d9caf999242490289c2ef0c22780120cdc030b)), closes [#162](https://github.com/nfroidure/svgicons2svgfont/issues/162) 35 | 36 | 37 | 38 | ## [14.0.1](https://github.com/nfroidure/svgicons2svgfont/compare/v14.0.0...v14.0.1) (2024-08-01) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * **bin:** fix script mod eoutput to avoid logging unecessary data ([2ef67d3](https://github.com/nfroidure/svgicons2svgfont/commit/2ef67d320ef31e01d4ad4eab996adf9eb1386358)) 44 | 45 | 46 | 47 | # [14.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v13.0.0...v14.0.0) (2024-07-30) 48 | 49 | 50 | 51 | # [13.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v12.0.0...v13.0.0) (2024-07-30) 52 | 53 | 54 | ### Bug Fixes 55 | 56 | * **tests:** fix ci tests ([9c4152f](https://github.com/nfroidure/svgicons2svgfont/commit/9c4152f42c465db06841fd46ba02c55d4c2d6cdc)) 57 | 58 | 59 | ### Code Refactoring 60 | 61 | * **core:** complete rewrite with Typescript and modern node APIs ([e84a5ce](https://github.com/nfroidure/svgicons2svgfont/commit/e84a5ceca5c3b73662d986ffbfcadabbe8b766d8)), closes [#169](https://github.com/nfroidure/svgicons2svgfont/issues/169) [#129](https://github.com/nfroidure/svgicons2svgfont/issues/129) [#91](https://github.com/nfroidure/svgicons2svgfont/issues/91) 62 | 63 | 64 | ### BREAKING CHANGES 65 | 66 | * **core:** Requires Node 20.11+ and ESM 67 | 68 | 69 | 70 | # [12.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v11.0.0...v12.0.0) (2022-05-28) 71 | 72 | 73 | 74 | # [11.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v10.0.6...v11.0.0) (2022-05-28) 75 | 76 | 77 | ### Bug Fixes 78 | 79 | * **core:** fix the metapak setup ([66a6274](https://github.com/nfroidure/svgicons2svgfont/commit/66a6274d2917979298a96fc7597ceb68d1689b83)) 80 | * **deps:** remove unused dependencies, move devDependencies where they belong to ([ecc3d87](https://github.com/nfroidure/svgicons2svgfont/commit/ecc3d8736df9d7dbb4116475c8bc1b062bd70565)) 81 | 82 | 83 | 84 | 85 | # [10.0.6](https://github.com/nfroidure/svgicons2svgfont/compare/v10.0.5...v10.0.6) (2022-03-12) 86 | 87 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v10.0.6 88 | 89 | 90 | # [10.0.5](https://github.com/nfroidure/svgicons2svgfont/compare/v10.0.4...v10.0.5) (2021-09-10) 91 | 92 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v10.0.5 93 | 94 | 95 | # [10.0.4](https://github.com/nfroidure/svgicons2svgfont/compare/v10.0.4...v10.0.4) (2021-06-02) 96 | 97 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v10.0.4 98 | 99 | 100 | # [10.0.3](https://github.com/nfroidure/svgicons2svgfont/compare/v10.0.2...v10.0.3) (2021-06-02) 101 | 102 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v10.0.3 103 | 104 | 105 | # [10.0.2](https://github.com/nfroidure/svgicons2svgfont/compare/v10.0.1...v10.0.2) (2021-05-20) 106 | 107 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v10.0.2 108 | 109 | 110 | # [10.0.1](https://github.com/nfroidure/svgicons2svgfont/compare/v9.2.0...v10.0.1) (2021-05-13) 111 | 112 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v10.0.1 113 | 114 | 115 | # [10.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v9.2.0...v10.0.0) (2021-05-12) 116 | 117 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v10.0.0 118 | 119 | 120 | # [9.2.0](https://github.com/nfroidure/svgicons2svgfont/compare/v9.1.0...v9.2.0) (2021-04-24) 121 | 122 | https://github.com/nfroidure/svgicons2svgfont/releases/tag/v9.2.0 123 | 124 | 125 | # [9.1.0](https://github.com/nfroidure/svgicons2svgfont/compare/v9.0.3...v9.1.0) (2019-06-01) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * NaN in d attribute when shape has rx, but ry attribute doesn't present ([72c5520](https://github.com/nfroidure/svgicons2svgfont/commit/72c5520)) 131 | 132 | 133 | ### Features 134 | 135 | * Add options.metadataProvider option + API doc ([3343d33](https://github.com/nfroidure/svgicons2svgfont/commit/3343d33)) 136 | 137 | 138 | 139 | 140 | ## [9.0.3](https://github.com/nfroidure/svgicons2svgfont/compare/v9.0.2...v9.0.3) (2018-06-02) 141 | 142 | 143 | 144 | 145 | ## [9.0.2](https://github.com/nfroidure/svgicons2svgfont/compare/v9.0.1...v9.0.2) (2018-02-26) 146 | 147 | 148 | 149 | 150 | ## [9.0.1](https://github.com/nfroidure/svgicons2svgfont/compare/v9.0.0...v9.0.1) (2018-02-26) 151 | 152 | 153 | 154 | 155 | # [9.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v8.0.0...v9.0.0) (2018-02-15) 156 | 157 | 158 | 159 | 160 | # [8.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v7.0.0...v8.0.0) (2017-12-03) 161 | 162 | 163 | 164 | 165 | ## [7.0.2](https://github.com/nfroidure/svgicons2svgfont/compare/v7.0.0...v7.0.2) (2017-09-22) 166 | 167 | 168 | 169 | 170 | ## [7.0.1](https://github.com/nfroidure/svgicons2svgfont/compare/v7.0.0...v7.0.1) (2017-08-24) 171 | 172 | 173 | 174 | 175 | # [7.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v6.0.0...v7.0.0) (2017-08-24) 176 | 177 | 178 | 179 | 180 | # [6.1.0](https://github.com/nfroidure/svgicons2svgfont/compare/v6.0.0...v6.1.0) (2017-08-22) 181 | 182 | 183 | 184 | 185 | # [6.0.0](https://github.com/nfroidure/svgicons2svgfont/compare/v5.0.2...v6.0.0) (2017-07-15) 186 | 187 | 188 | ### Bug Fixes 189 | 190 | * **ScaledIcons:** Icons using a scaled viewbow weren't rendering as expected ([44e3dd6](https://github.com/nfroidure/svgicons2svgfont/commit/44e3dd6)), closes [#73](https://github.com/nfroidure/svgicons2svgfont/issues/73) 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2017 Nicolas Froidure 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the “Software”), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [//]: # ( ) 2 | [//]: # (This file is automatically generated by a `metapak`) 3 | [//]: # (module. Do not change it except between the) 4 | [//]: # (`content:start/end` flags, your changes would) 5 | [//]: # (be overridden.) 6 | [//]: # ( ) 7 | # svgicons2svgfont 8 | > Read a set of SVG icons and output a SVG font 9 | 10 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/nfroidure/svgicons2svgfont/blob/main/LICENSE) 11 | 12 | 13 | [//]: # (::contents:start) 14 | 15 | `svgicons2svgfont` is a simple tool to merge multiple icons to an SVG font. 16 | 17 | 'rect', 'line', 'circle', 'ellipsis', 'polyline' and 'polygon' shapes will be 18 | converted to pathes. Multiple pathes will be merged. 19 | 20 | Transform attributes support is currenly experimental, 21 | [report issues if any](https://github.com/nfroidure/svgicons2svgfont/issues/6). 22 | 23 | You can test this library with the 24 | [frontend generator](http://nfroidure.github.io/svgiconfont/). 25 | 26 | You may want to convert fonts to icons, if so use 27 | [svgfont2svgicons](https://github.com/nfroidure/svgfont2svgicons). 28 | 29 | ## Usage 30 | 31 | ### In your scripts 32 | 33 | ```js 34 | import { SVGIcons2SVGFontStream } from 'svgicons2svgfont'; 35 | import { createReadStream, createWriteStream } from 'node:fs'; 36 | 37 | const fontStream = new SVGIcons2SVGFontStream({ 38 | fontName: 'hello', 39 | }); 40 | 41 | // Setting the font destination 42 | fontStream 43 | .pipe(createWriteStream('fonts/hello.svg')) 44 | .on('finish', function () { 45 | console.log('Font successfully created!'); 46 | }) 47 | .on('error', function (err) { 48 | console.log(err); 49 | }); 50 | 51 | // Writing glyphs 52 | const glyph1 = createReadStream('icons/icon1.svg'); 53 | glyph1.metadata = { 54 | unicode: ['\uE001\uE002'], 55 | name: 'icon1', 56 | }; 57 | fontStream.write(glyph1); 58 | 59 | // Multiple unicode values are possible 60 | const glyph2 = createReadStream('icons/icon1.svg'); 61 | glyph2.metadata = { 62 | unicode: ['\uE002', '\uEA02'], 63 | name: 'icon2', 64 | }; 65 | fontStream.write(glyph2); 66 | 67 | // Either ligatures are available 68 | const glyph3 = createReadStream('icons/icon1.svg'); 69 | glyph3.metadata = { 70 | unicode: ['\uE001\uE002'], 71 | name: 'icon1-icon2', 72 | }; 73 | fontStream.write(glyph3); 74 | 75 | // Do not forget to end the stream 76 | fontStream.end(); 77 | ``` 78 | 79 | ## Debugging 80 | 81 | To debug, simply use the following env var: `DEBUG=svgicons2svgfont`. 82 | 83 | ## CLI interface 84 | 85 | All options are available except the `log` one by using this pattern: 86 | `--{LOWER_CASE(optionName)}={optionValue}`. 87 | 88 | ```sh 89 | svgicons2svgfont --fontName=hello -o font/destination/file.svg icons/directory/*.svg 90 | ``` 91 | 92 | Note that you won't be able to customize icon names or icons unicodes by passing 93 | options but by using the following convention to name your icons files: 94 | `${icon.unicode}-${icon.name}.svg` where `icon.unicode` is a comma separated 95 | list of unicode strings (ex: 'uEA01,uE001,uE001uE002', note that the last string 96 | is in fact a ligature). 97 | 98 | There is a few more options for the CLI interface, you can list all of them: 99 | 100 | ```sh 101 | npx svgicons2svgfont --help 102 | ``` 103 | 104 | Gives: 105 | 106 | ```txt 107 | Usage: svgicons2svgfont [options] 108 | 109 | Options: 110 | -V, --version output the version number 111 | -v, --verbose tell me everything! 112 | -o, --output [/dev/stdout] file to write output to 113 | -f, --fontName [value] the font family name you want [iconfont] 114 | -i, --fontId [value] the font id you want [fontName] 115 | -st, --style [value] the font style you want 116 | -we, --weight [value] the font weight you want 117 | -w, --fixedWidth creates a monospace font of the width of the largest input icon 118 | -c, --centerHorizontally calculate the bounds of a glyph and center it horizontally 119 | -y, --centerVertically centers the glyphs vertically in the generated font. 120 | -n, --normalize normalize icons by scaling them to the height of the highest icon 121 | -p, --preserveAspectRatio used with normalize to scale down glyph if the SVG width is greater than the height 122 | -h, --height [value] the output font height [MAX(icons.height)] (icons will be scaled so the highest has 123 | this height) 124 | -r, --round [value] setup the SVG path rounding [10e12] 125 | -d, --descent [value] the font descent [0] 126 | -a, --ascent [value] the font ascent [height - descent] 127 | -s, --startUnicode [value] the start unicode code point for unprefixed files [0xEA01] 128 | -u, --prependUnicode prefix files with their automatically allocated unicode code point 129 | -m, --metadata content of the metadata tag 130 | --help display help for command 131 | ``` 132 | 133 | ## API 134 | 135 | ### new SVGIcons2SVGFontStream(options) 136 | 137 | #### options.fontName 138 | 139 | Type: `String` Default value: `'iconfont'` 140 | 141 | The font family name you want. 142 | 143 | #### options.fontId 144 | 145 | Type: `String` Default value: the options.fontName value 146 | 147 | The font id you want. 148 | 149 | #### options.fontStyle 150 | 151 | Type: `String` Default value: `''` 152 | 153 | The font style you want. 154 | 155 | #### options.fontWeight 156 | 157 | Type: `String` Default value: `''` 158 | 159 | The font weight you want. 160 | 161 | #### options.fixedWidth 162 | 163 | Type: `Boolean` Default value: `false` 164 | 165 | Creates a monospace font of the width of the largest input icon. 166 | 167 | #### options.centerHorizontally 168 | 169 | Type: `Boolean` Default value: `false` 170 | 171 | Calculate the bounds of a glyph and center it horizontally. 172 | 173 | #### options.centerVertically 174 | 175 | Type: `Boolean` Default value: `false` 176 | 177 | Centers the glyphs vertically in the generated font. 178 | 179 | #### options.normalize 180 | 181 | Type: `Boolean` Default value: `false` 182 | 183 | Normalize icons by scaling them to the height of the highest icon. 184 | 185 | #### options.preserveAspectRatio 186 | 187 | Type: `Boolean` Default value: `false` 188 | 189 | Used with normalize to scale down glyph if the SVG width is greater than the 190 | height. 191 | 192 | #### options.fontHeight 193 | 194 | Type: `Number` Default value: `MAX(icons.height)` The outputted font height 195 | (defaults to the height of the highest input icon). 196 | 197 | #### options.round 198 | 199 | Type: `Number` Default value: `10e12` Setup SVG path rounding. 200 | 201 | #### options.descent 202 | 203 | Type: `Number` Default value: `0` 204 | 205 | The font descent. It is usefull to fix the font baseline yourself. 206 | 207 | **Warning:** The descent is a positive value! 208 | 209 | #### options.ascent 210 | 211 | Type: `Number` Default value: `fontHeight - descent` 212 | 213 | The font ascent. Use this options only if you know what you're doing. A suitable 214 | value for this is computed for you. 215 | 216 | #### options.metadata 217 | 218 | Type: `String` Default value: `undefined` 219 | 220 | The font [metadata](http://www.w3.org/TR/SVG/metadata.html). You can set any 221 | character data in but it is the be suited place for a copyright mention. 222 | 223 | #### options.metadataProvider 224 | 225 | Type: 226 | `(file: string, cb: (err: any, metadata: {file: string, name: string, unicode: string[], renamed: boolean}) => void` 227 | 228 | Default value: `require('svgicons2svgfont/src/metadata')(options)` 229 | 230 | A function which determines the metadata for an icon. It takes a parameter 231 | `file` with an icon svg and should return icon metadata (asynchronously) via the 232 | callback function. You can use this function to provide custom logic for svg to 233 | codepoint mapping. 234 | 235 | | | | 236 | | ------------------ | ---------------------------------------------------------------------------------------- | 237 | | `metadata.path` | The path to the icon file. (The original `file` param is the file was not moved.) | 238 | | `metadata.name` | The name of the icon | 239 | | `metadata.unicode` | The unicode codepoints corresponding to this glyph. Each should be a 1-codepoint string. | 240 | | `metadata.renamed` | Wether the original file was moved (e.g. to prefix it with its unicode codepoint) | 241 | 242 | #### options.log 243 | 244 | Type: `Function` Default value: `console.log` 245 | 246 | Allows you to provide your own logging function. Set to `function(){}` to 247 | disable logging. 248 | 249 | ## Build systems 250 | 251 | ### Grunt plugins 252 | 253 | [grunt-svgicons2svgfont](https://github.com/nfroidure/grunt-svgicons2svgfont) 254 | and [grunt-webfont](https://github.com/sapegin/grunt-webfont). 255 | 256 | ### Gulp plugins 257 | 258 | Try [gulp-iconfont](https://github.com/nfroidure/gulp-iconfont) and 259 | [gulp-svgicons2svgfont](https://github.com/nfroidure/gulp-svgicons2svgfont). 260 | 261 | ### Stylus plugin 262 | 263 | Use [stylus-iconfont](https://www.npmjs.org/package/stylus-iconfont). 264 | 265 | ### Mimosa plugin 266 | 267 | Use 268 | [mimosa-svgs-to-iconfonts](https://www.npmjs.org/package/mimosa-svgs-to-iconfonts). 269 | 270 | ## CLI alternatives 271 | 272 | You can combine this plugin's CLI interface with 273 | [svg2ttf](https://www.npmjs.com/package/svg2ttf), 274 | [ttf2eot](https://www.npmjs.com/package/ttf2eot), 275 | [ttf2woff](https://www.npmjs.com/package/ttf2woff) and 276 | [ttf2woff2](https://www.npmjs.com/package/ttf2woff2). You can also use 277 | [webfonts-generator](https://www.npmjs.com/package/webfonts-generator). 278 | 279 | ## Stats 280 | 281 | [![NPM](https://nodei.co/npm/svgicons2svgfont.png?downloads=true&stars=true)](https://nodei.co/npm/svgicons2svgfont/) 282 | [![NPM](https://nodei.co/npm-dl/svgicons2svgfont.png)](https://nodei.co/npm/svgicons2svgfont/) 283 | 284 | ## Contributing 285 | 286 | Feel free to push your code if you agree with publishing under the MIT license. 287 | 288 | # License 289 | 290 | [MIT](https://github.com/nfroidure/svgicons2svgfont/blob/master/LICENSE) 291 | 292 | [//]: # (::contents:end) 293 | 294 | # Authors 295 | - [Nicolas Froidure](http://insertafter.com/en/index.html) 296 | - [Adrian Leonhard](https://github.com/NaridaL) 297 | - [Vinicius Teixeira](https://github.com/vinicius0026) 298 | 299 | # License 300 | [MIT](https://github.com/nfroidure/svgicons2svgfont/blob/main/LICENSE) 301 | -------------------------------------------------------------------------------- /bin/svgicons2svgfont.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | import { program } from 'commander'; 4 | import { createWriteStream } from 'node:fs'; 5 | import { join } from 'node:path'; 6 | import { argv, exit, stdout } from 'node:process'; 7 | import { error } from 'node:console'; 8 | import { readFile } from 'node:fs/promises'; 9 | import { glob } from 'glob'; 10 | import { SVGIcons2SVGFontStream } from '../dist/index.js'; 11 | import { SVGIconsDirStream } from '../dist/iconsdir.js'; 12 | 13 | const { version } = JSON.parse((await readFile(join(import.meta.dirname, '..', 'package.json'))).toString()); 14 | 15 | program 16 | .storeOptionsAsProperties(true) 17 | .version(version) 18 | .usage('[options] ') 19 | .option('-v, --verbose', 'tell me everything!') 20 | .option('-o, --output [/dev/stdout]', 'file to write output to') 21 | .option('-f, --fontName [value]', 'the font family name you want [iconfont]') 22 | .option('-i, --fontId [value]', 'the font id you want [fontName]') 23 | .option('-st, --style [value]', 'the font style you want') 24 | .option('-we, --weight [value]', 'the font weight you want') 25 | .option( 26 | '-w, --fixedWidth', 27 | 'creates a monospace font of the width of the largest input icon' 28 | ) 29 | .option( 30 | '-c, --centerHorizontally', 31 | 'calculate the bounds of a glyph and center it horizontally' 32 | ) 33 | .option( 34 | '-y, --centerVertically', 35 | 'centers the glyphs vertically in the generated font.' 36 | ) 37 | .option( 38 | '-n, --normalize', 39 | 'normalize icons by scaling them to the height of the highest icon' 40 | ) 41 | .option( 42 | '-p, --preserveAspectRatio', 43 | 'used with normalize to scale down glyph if the SVG width is greater than the height' 44 | ) 45 | .option( 46 | '-h, --height [value]', 47 | 'the output font height [MAX(icons.height)] (icons will be scaled so the highest has this height)', 48 | parseInt 49 | ) 50 | .option( 51 | '-r, --round [value]', 52 | 'setup the SVG path rounding [10e12]', 53 | parseFloat 54 | ) 55 | .option('-d, --descent [value]', 'the font descent [0]', parseInt) 56 | .option( 57 | '-a, --ascent [value]', 58 | 'the font ascent [height - descent]', 59 | parseInt 60 | ) 61 | .option( 62 | '-s, --startUnicode [value]', 63 | 'the start unicode code point for' + ' unprefixed files [0xEA01]', 64 | parseInt 65 | ) 66 | .option( 67 | '-u, --prependUnicode', 68 | 'prefix files with their automatically' + ' allocated unicode code point', 69 | parseInt 70 | ) 71 | .option('-m, --metadata', 'content of the metadata tag') 72 | .parse(argv); 73 | 74 | if (!program.args.length) { 75 | error('No icons specified!'); 76 | exit(1); 77 | } 78 | 79 | const files = program.args.flatMap((file) => glob.sync(file)); 80 | const options = program.opts(); 81 | 82 | new SVGIconsDirStream(files, { 83 | startUnicode: options.startUnicode, 84 | prependUnicode: options.prependUnicode, 85 | }) 86 | .pipe( 87 | new SVGIcons2SVGFontStream({ 88 | fontName: options.fontName, 89 | fontId: options.fontId, 90 | fixedWidth: options.fixedwidth, 91 | centerHorizontally: options.centerHorizontally, 92 | centerVertically: options.centerVertically, 93 | normalize: options.normalize, 94 | preserveAspectRatio: options.preserveAspectRatio, 95 | fontHeight: options.height, 96 | round: options.round, 97 | descent: options.descent, 98 | ascent: options.ascent, 99 | metadata: options.metadata, 100 | }) 101 | ) 102 | .pipe(options.output ? createWriteStream(options.output) : stdout); 103 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // This file is automatically generated by a `metapak` 3 | // module. Do not change it elsewhere, changes would 4 | // be overridden. 5 | 6 | import eslint from '@eslint/js'; 7 | import tseslint from 'typescript-eslint'; 8 | import eslintConfigPrettier from 'eslint-config-prettier'; 9 | import eslintPluginJest from 'eslint-plugin-jest'; 10 | 11 | export default tseslint.config( 12 | eslint.configs.recommended, 13 | ...tseslint.configs.recommended, 14 | { 15 | files: ['*.test.ts'], 16 | ...eslintPluginJest.configs['flat/recommended'], 17 | }, 18 | eslintConfigPrettier, 19 | { 20 | name: 'Project config', 21 | languageOptions: { 22 | ecmaVersion: 2018, 23 | sourceType: 'module', 24 | }, 25 | ignores: ['*.d.ts'], 26 | }, 27 | ); 28 | 29 | -------------------------------------------------------------------------------- /fixtures/expected/calcbounds.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/expected/cleanicons-ascent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 37 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /fixtures/expected/cleanicons-high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/cleanicons-lig.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/cleanicons-multi.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/expected/cleanicons-stw.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /fixtures/expected/cleanicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 37 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /fixtures/expected/hiddenpathesicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/expected/multipathicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/expected/nestedicons-cli.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fixtures/expected/nestedicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fixtures/expected/onlywithrx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/onlywithry.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons-cli.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons3.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons4.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons5.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons6.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons7.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originalicons8.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/originaliconsn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/paesku.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/pathfillnone.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/prefixedicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fixtures/expected/realicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /fixtures/expected/realworld.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/rendricons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 25 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /fixtures/expected/rotatedrectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/roundedcorners.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/scaledicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/shapeicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 25 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /fixtures/expected/singleicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/skew.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/expected/test-codepoint.json: -------------------------------------------------------------------------------- 1 | { 2 | "cleanicons": [{ 3 | "name": "account", 4 | "unicode": ["\uE001"], 5 | "width": 150, 6 | "height": 150 7 | }, { 8 | "name": "arrow-down", 9 | "unicode": ["\uE002"], 10 | "width": 150, 11 | "height": 150, 12 | "color": "#0F9925" 13 | }, { 14 | "name": "arrow-left", 15 | "unicode": ["\uE003"], 16 | "width": 150, 17 | "height": 150, 18 | "color": "black" 19 | }, { 20 | "name": "arrow-right", 21 | "unicode": ["\uE004"], 22 | "width": 150, 23 | "height": 150, 24 | "color": "red" 25 | }, { 26 | "name": "arrow-up", 27 | "unicode": ["\uE005"], 28 | "width": 150, 29 | "height": 150, 30 | "color": "blue" 31 | }, { 32 | "name": "basket", 33 | "unicode": ["\uE006"], 34 | "width": 150, 35 | "height": 150, 36 | "color": "#F9CE42" 37 | }, { 38 | "name": "close", 39 | "unicode": ["\uE007"], 40 | "width": 150, 41 | "height": 150, 42 | "color": "purple" 43 | }, { 44 | "name": "minus", 45 | "unicode": ["\uE008"], 46 | "width": 150, 47 | "height": 150 48 | }, { 49 | "name": "plus", 50 | "unicode": ["\uE009"], 51 | "width": 150, 52 | "height": 150, 53 | "color": "#F0F" 54 | }, { 55 | "name": "search", 56 | "unicode": ["\uE00A"], 57 | "width": 150, 58 | "height": 150, 59 | "color": "black" 60 | }], 61 | "multipathicons": [{ 62 | "name": "basket", 63 | "unicode": ["\uE001"], 64 | "width": 150, 65 | "height": 150, 66 | "color": "yellow" 67 | }, { 68 | "name": "kikoolol", 69 | "unicode": ["\uE002"], 70 | "width": 150, 71 | "height": 150, 72 | "color": "black" 73 | }], 74 | "originalicons": [{ 75 | "name": "mute", 76 | "unicode": ["\uE001"], 77 | "width": 439, 78 | "height": 1024, 79 | "color": "black" 80 | }, { 81 | "name": "sound", 82 | "unicode": ["\uE002"], 83 | "width": 439, 84 | "height": 1024 85 | }, { 86 | "name": "speaker", 87 | "unicode": ["\uE003"], 88 | "width": 511.9057, 89 | "height": 1024, 90 | "color": "#0F0" 91 | }], 92 | "prefixedicons": [{ 93 | "name": "arrow-down", 94 | "unicode": ["\uE008"], 95 | "width": 150, 96 | "height": 150, 97 | "color": "green" 98 | }, { 99 | "name": "arrow-left", 100 | "unicode": ["\uE00F"], 101 | "width": 150, 102 | "height": 150, 103 | "color": "black" 104 | }, { 105 | "name": "arrow-right", 106 | "unicode": ["\uE013"], 107 | "width": 150, 108 | "height": 150, 109 | "color": "#0F0F0F" 110 | }, { 111 | "name": "arrow-up", 112 | "unicode": ["\uE014"], 113 | "width": 150, 114 | "height": 150, 115 | "color": "#F4F4F4" 116 | }], 117 | "shapeicons": [{ 118 | "name": "circle", 119 | "unicode": ["\uE001"], 120 | "width": 150, 121 | "height": 150, 122 | "color": "red" 123 | }, { 124 | "name": "ellipse", 125 | "unicode": ["\uE002"], 126 | "width": 150, 127 | "height": 150, 128 | "color": "#1F1F1F" 129 | }, { 130 | "name": "lines", 131 | "unicode": ["\uE003"], 132 | "width": 150, 133 | "height": 150, 134 | "color": "#8F8F8F" 135 | }, { 136 | "name": "polygon", 137 | "unicode": ["\uE004"], 138 | "width": 150, 139 | "height": 150 140 | }, { 141 | "name": "polyline", 142 | "unicode": ["\uE005"], 143 | "width": 150, 144 | "height": 150 145 | }, { 146 | "name": "rect", 147 | "unicode": ["\uE006"], 148 | "width": 150, 149 | "height": 150 150 | }] 151 | } 152 | -------------------------------------------------------------------------------- /fixtures/expected/tocentericons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/toverticalcentericons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /fixtures/expected/transformedicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fixtures/expected/translatex.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/expected/variableheighticons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fixtures/expected/variableheighticonsn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fixtures/expected/variableheighticonsnp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /fixtures/expected/variablewidthicons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/expected/variablewidthiconsid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/expected/variablewidthiconsn.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 13 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/icons/badicons/pathdata.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/calcbounds/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /fixtures/icons/calcbounds/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/account.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/basket.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/cleanicons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/hiddenpathesicons/sound--off.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 12 | 20 | 21 | -------------------------------------------------------------------------------- /fixtures/icons/hiddenpathesicons/sound--on.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 12 | 20 | 21 | -------------------------------------------------------------------------------- /fixtures/icons/multipathicons/basket.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 30 | 50 | 53 | 56 | 59 | 60 | -------------------------------------------------------------------------------- /fixtures/icons/multipathicons/kikoolol.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 30 | 50 | 55 | 59 | 60 | -------------------------------------------------------------------------------- /fixtures/icons/nestedicons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/nestedicons/uEA01-arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/nestedicons/uEA01arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/nestedicons/uEA03-arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/icons/onlywithrx/rounded.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon-call-window-front 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /fixtures/icons/onlywithry/rounded.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon-call-window-front 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /fixtures/icons/originalicons/mute.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/originalicons/sound.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/originalicons/speaker.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/paesku/chip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | cpu 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /fixtures/icons/pathfillnone/account.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /fixtures/icons/prefixedicons/uE008-arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/prefixedicons/uE00F-arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/prefixedicons/uE013-arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/icons/prefixedicons/uE014-arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/diegoliv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 13 | 14 | 15 | 21 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/hannesjohansson.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/roelvanhitum.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/safety-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/sb-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/settings-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 57 | 58 | 59 | 60 | 61 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/track-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /fixtures/icons/realicons/web-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /fixtures/icons/realworld/omridevk.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /fixtures/icons/rendricons/account.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /fixtures/icons/rendricons/basket.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /fixtures/icons/rendricons/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/icons/rendricons/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/rendricons/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/rendricons/search.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /fixtures/icons/rotatedrectangle/rotatedrectangle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /fixtures/icons/roundedcorners/roundedrect.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/scaledicons/ic_search_black_48px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /fixtures/icons/shapeicons/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /fixtures/icons/shapeicons/ellipse.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /fixtures/icons/shapeicons/lines.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 30 | 50 | 56 | 57 | -------------------------------------------------------------------------------- /fixtures/icons/shapeicons/polygon.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/shapeicons/polyline.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/shapeicons/rect.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 30 | 50 | 57 | 58 | -------------------------------------------------------------------------------- /fixtures/icons/singleicon/account.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/skew/skew-x.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /fixtures/icons/skew/skew-y.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /fixtures/icons/tocentericons/bottomleft.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/tocentericons/center.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/tocentericons/topright.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/toverticalcentericons/bottomleft.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/toverticalcentericons/center.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/toverticalcentericons/topright.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /fixtures/icons/transformedicons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /fixtures/icons/transformedicons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /fixtures/icons/transformedicons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/icons/transformedicons/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 12 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /fixtures/icons/translatex/translatex.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /fixtures/icons/variableheighticons/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 30 | 50 | 54 | 55 | -------------------------------------------------------------------------------- /fixtures/icons/variableheighticons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /fixtures/icons/variableheighticons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /fixtures/icons/variableheighticons/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 22 | image/svg+xml 23 | 25 | 26 | 27 | 28 | 30 | 50 | 54 | 55 | -------------------------------------------------------------------------------- /fixtures/icons/variablewidthicons/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /fixtures/icons/variablewidthicons/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "metapak": { 3 | "configs": [ 4 | "main", 5 | "readme", 6 | "tsesm", 7 | "jest", 8 | "eslint", 9 | "codeclimate", 10 | "ghactions" 11 | ], 12 | "data": { 13 | "files": "'src/**/*.ts'", 14 | "testsFiles": "'src/**/*.test.ts'", 15 | "distFiles": "'dist/**/*.js'", 16 | "ignore": [ 17 | "dist", 18 | "fixtures/results" 19 | ], 20 | "bundleFiles": [ 21 | "dist", 22 | "src", 23 | "bin" 24 | ] 25 | } 26 | }, 27 | "name": "svgicons2svgfont", 28 | "version": "15.0.1", 29 | "description": "Read a set of SVG icons and output a SVG font", 30 | "homepage": "https://github.com/nfroidure/svgicons2svgfont", 31 | "main": "dist/index.js", 32 | "type": "module", 33 | "types": "dist/index.d.ts", 34 | "scripts": { 35 | "build": "rimraf 'dist' && tsc --outDir dist", 36 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md", 37 | "cli": "env NODE_ENV=${NODE_ENV:-cli}", 38 | "cover": "npm run jest -- --coverage", 39 | "cz": "env NODE_ENV=${NODE_ENV:-cli} git cz", 40 | "format": "npm run prettier", 41 | "jest": "NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest", 42 | "lint": "eslint 'src/**/*.ts'", 43 | "metapak": "metapak", 44 | "precz": "npm run build && npm t && npm run lint && npm run metapak -- -s", 45 | "prettier": "prettier --write 'src/**/*.ts'", 46 | "preversion": "npm run build && npm t && npm run lint && npm run metapak -- -s", 47 | "rebuild": "swc ./src -s -d dist -C jsc.target=es2022", 48 | "test": "npm run jest", 49 | "type-check": "tsc --pretty --noEmit", 50 | "version": "npm run changelog" 51 | }, 52 | "repository": { 53 | "type": "git", 54 | "url": "git://github.com/nfroidure/svgicons2svgfont" 55 | }, 56 | "keywords": [ 57 | "SVG", 58 | "icons", 59 | "font", 60 | "creation", 61 | "fonts" 62 | ], 63 | "engines": { 64 | "node": ">=20.11.1" 65 | }, 66 | "dependencies": { 67 | "@types/sax": "^1.2.7", 68 | "commander": "^12.1.0", 69 | "debug": "^4.3.6", 70 | "glob": "^11.0.0", 71 | "sax": "^1.4.1", 72 | "svg-pathdata": "^7.0.0", 73 | "transformation-matrix": "^3.0.0", 74 | "yerror": "^8.0.0" 75 | }, 76 | "devDependencies": { 77 | "@eslint/js": "^9.7.0", 78 | "@swc/cli": "^0.4.0", 79 | "@swc/core": "^1.6.13", 80 | "@swc/helpers": "^0.5.12", 81 | "@swc/jest": "^0.2.36", 82 | "bufferstreams": "^4.0.0", 83 | "commitizen": "^4.3.0", 84 | "conventional-changelog-cli": "^5.0.0", 85 | "cz-conventional-changelog": "^3.3.0", 86 | "eslint": "^9.7.0", 87 | "eslint-config-prettier": "^9.1.0", 88 | "eslint-plugin-jest": "^28.6.0", 89 | "eslint-plugin-prettier": "^5.1.3", 90 | "jest": "^29.7.0", 91 | "metapak": "^6.0.1", 92 | "metapak-nfroidure": "^18.2.0", 93 | "prettier": "^3.3.3", 94 | "rimraf": "^6.0.1", 95 | "streamtest": "^3.0.1", 96 | "typescript": "^5.5.3", 97 | "typescript-eslint": "^7.16.0" 98 | }, 99 | "author": { 100 | "name": "Nicolas Froidure", 101 | "email": "nicolas.froidure@insertafter.com", 102 | "url": "http://insertafter.com/en/index.html" 103 | }, 104 | "contributors": [ 105 | { 106 | "name": "Adrian Leonhard", 107 | "email": "adrianleonhard@gmail.com", 108 | "url": "https://github.com/NaridaL" 109 | }, 110 | { 111 | "name": "Vinicius Teixeira", 112 | "email": "vinicius0026@gmail.com", 113 | "url": "https://github.com/vinicius0026" 114 | } 115 | ], 116 | "license": "MIT", 117 | "bugs": { 118 | "url": "https://github.com/nfroidure/svgicons2svgfont/issues" 119 | }, 120 | "bin": { 121 | "svgicons2svgfont": "bin/svgicons2svgfont.js" 122 | }, 123 | "files": [ 124 | "dist", 125 | "src", 126 | "bin", 127 | "LICENSE", 128 | "README.md", 129 | "CHANGELOG.md" 130 | ], 131 | "config": { 132 | "commitizen": { 133 | "path": "./node_modules/cz-conventional-changelog" 134 | } 135 | }, 136 | "greenkeeper": { 137 | "ignore": [ 138 | "commitizen", 139 | "cz-conventional-changelog", 140 | "conventional-changelog-cli", 141 | "typescript", 142 | "rimraf", 143 | "@swc/cli", 144 | "@swc/core", 145 | "@swc/helpers", 146 | "jest", 147 | "@swc/jest", 148 | "eslint", 149 | "prettier", 150 | "eslint-config-prettier", 151 | "eslint-plugin-prettier", 152 | "typescript-eslint" 153 | ] 154 | }, 155 | "jest": { 156 | "coverageReporters": [ 157 | "lcov" 158 | ], 159 | "testPathIgnorePatterns": [ 160 | "/node_modules/" 161 | ], 162 | "roots": [ 163 | "/src" 164 | ], 165 | "transform": { 166 | "^.+\\.tsx?$": [ 167 | "@swc/jest", 168 | {} 169 | ] 170 | }, 171 | "testEnvironment": "node", 172 | "moduleNameMapper": { 173 | "(.+)\\.js": "$1" 174 | }, 175 | "extensionsToTreatAsEsm": [ 176 | ".ts" 177 | ], 178 | "prettierPath": null 179 | }, 180 | "prettier": { 181 | "semi": true, 182 | "printWidth": 80, 183 | "singleQuote": true, 184 | "trailingComma": "all", 185 | "proseWrap": "always" 186 | }, 187 | "overrides": { 188 | "eslint": "^9.7.0" 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/filesorter.ts: -------------------------------------------------------------------------------- 1 | const testExpression = /(^|\/|\\)(?:((?:u[0-9a-f]{4,6},?)+)-)(.+)\.svg$/i; 2 | 3 | export function fileSorter(fileA, fileB) { 4 | const hasUnicodeA = testExpression.test(fileA); 5 | const hasUnicodeB = testExpression.test(fileB); 6 | 7 | if (hasUnicodeA == hasUnicodeB) { 8 | // just compare alphabetically 9 | const fileA_ = fileA.substr(0, fileA.lastIndexOf('.')); 10 | const fileB_ = fileB.substr(0, fileB.lastIndexOf('.')); 11 | return fileA_ < fileB_ ? -1 : 1; 12 | } else { 13 | // map true to 0, because we want it to be first 14 | return (hasUnicodeA ? 0 : 1) - (hasUnicodeB ? 0 : 1); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/iconsdir.ts: -------------------------------------------------------------------------------- 1 | import { Readable } from 'node:stream'; 2 | import { createReadStream, readdir } from 'node:fs'; 3 | import { fileSorter } from './filesorter.js'; 4 | import { 5 | getMetadataService, 6 | FileMetadata, 7 | MetadataServiceOptions, 8 | } from './metadata.js'; 9 | import debug from 'debug'; 10 | 11 | const warn = debug('svgicons2svgfont'); 12 | 13 | export type SVGIconsDirStreamOptions = { 14 | metadataProvider: ReturnType; 15 | }; 16 | export type SVGIconStream = Readable & { 17 | metadata: Pick; 18 | }; 19 | 20 | class SVGIconsDirStream extends Readable { 21 | private _options: SVGIconsDirStreamOptions & Partial; 22 | gotFilesInfos: boolean = false; 23 | fileInfos: FileMetadata[] = []; 24 | dir: string; 25 | 26 | constructor( 27 | dir: string[], 28 | options: Partial, 29 | ) { 30 | super({ objectMode: true }); 31 | this._options = { 32 | metadataProvider: options.metadataProvider || getMetadataService(options), 33 | }; 34 | 35 | if (dir instanceof Array) { 36 | this.dir = ''; 37 | this._getFilesInfos(dir); 38 | } else { 39 | this.dir = dir; 40 | } 41 | } 42 | _getFilesInfos(files) { 43 | let filesProcessed = 0; 44 | 45 | this.fileInfos = []; 46 | // Ensure prefixed files come first 47 | files = files.slice(0).sort(fileSorter); 48 | files.forEach((file) => { 49 | this._options.metadataProvider( 50 | (this.dir ? this.dir + '/' : '') + file, 51 | (err, metadata) => { 52 | filesProcessed++; 53 | if (err) { 54 | this.emit('error', err); 55 | } 56 | if (metadata) { 57 | if (metadata.renamed) { 58 | warn( 59 | '➕ - Saved codepoint: ' + 60 | 'u' + 61 | metadata.unicode[0] 62 | .codePointAt(0) 63 | ?.toString(16) 64 | .toUpperCase() + 65 | ' for the glyph "' + 66 | metadata.name + 67 | '"', 68 | ); 69 | } 70 | this.fileInfos.push(metadata); 71 | } 72 | if (files.length === filesProcessed) { 73 | // Reorder files 74 | this.fileInfos.sort((infosA, infosB) => 75 | infosA.unicode[0] > infosB.unicode[0] ? 1 : -1, 76 | ); 77 | // Mark directory as processed 78 | this.gotFilesInfos = true; 79 | // Start processing 80 | this._pushSVGIcons(); 81 | } 82 | }, 83 | ); 84 | }); 85 | } 86 | 87 | _pushSVGIcons() { 88 | let fileInfo: FileMetadata; 89 | let svgIconStream: SVGIconStream; 90 | 91 | while (this.fileInfos.length) { 92 | fileInfo = this.fileInfos.shift() as FileMetadata; 93 | svgIconStream = createReadStream( 94 | fileInfo.path, 95 | ) as unknown as SVGIconStream; 96 | svgIconStream.metadata = { 97 | name: fileInfo.name, 98 | unicode: fileInfo.unicode, 99 | }; 100 | if (!this.push(svgIconStream)) { 101 | return; 102 | } 103 | } 104 | this.push(null); 105 | } 106 | _read() { 107 | if (this.dir) { 108 | readdir(this.dir, (err, files) => { 109 | if (err) { 110 | this.emit('error', err); 111 | } 112 | this._getFilesInfos(files); 113 | }); 114 | return; 115 | } 116 | if (this.gotFilesInfos) { 117 | this._pushSVGIcons(); 118 | } 119 | } 120 | } 121 | 122 | export { SVGIconsDirStream }; 123 | -------------------------------------------------------------------------------- /src/metadata.ts: -------------------------------------------------------------------------------- 1 | import { join, dirname, basename } from 'node:path'; 2 | import { rename } from 'node:fs'; 3 | 4 | export type MetadataServiceOptions = { 5 | prependUnicode: boolean; 6 | startUnicode: number; 7 | }; 8 | export type FileMetadata = { 9 | path: string; 10 | name: string; 11 | unicode: string[] | string; 12 | renamed: boolean; 13 | }; 14 | 15 | function getMetadataService(options: Partial = {}) { 16 | const usedUnicodes = [] as string[]; 17 | 18 | // Default options 19 | const _options: MetadataServiceOptions = { 20 | prependUnicode: !!options.prependUnicode, 21 | startUnicode: 22 | 'number' === typeof options.startUnicode ? options.startUnicode : 0xea01, 23 | }; 24 | 25 | return function getMetadataFromFile( 26 | file: string, 27 | cb: (error: Error | null, metadata?: FileMetadata) => void, 28 | ) { 29 | const fileBasename = basename(file); 30 | const metadata: FileMetadata = { 31 | path: file, 32 | name: '', 33 | unicode: [], 34 | renamed: false, 35 | }; 36 | const matches = fileBasename.match( 37 | /^(?:((?:u[0-9a-f]{4,6},?)+)-)?(.+)\.svg$/i, 38 | ); 39 | 40 | metadata.name = 41 | matches && matches[2] ? matches[2] : 'icon' + _options.startUnicode; 42 | 43 | if (matches && matches[1]) { 44 | metadata.unicode = matches[1].split(',').map((match) => { 45 | match = match.substring(1); 46 | return match 47 | .split('u') 48 | .map((code) => String.fromCodePoint(parseInt(code, 16))) 49 | .join(''); 50 | }); 51 | if (-1 !== usedUnicodes.indexOf(metadata.unicode[0])) { 52 | cb( 53 | new Error( 54 | 'The unicode codepoint of the glyph ' + 55 | metadata.name + 56 | ' seems to be already used by another glyph.', 57 | ), 58 | ); 59 | return; 60 | } 61 | usedUnicodes.push(...metadata.unicode); 62 | } else { 63 | do { 64 | (metadata.unicode as string[])[0] = String.fromCodePoint( 65 | _options.startUnicode++, 66 | ); 67 | } while (usedUnicodes.includes(metadata.unicode[0])); 68 | usedUnicodes.push(metadata.unicode[0]); 69 | if (_options.prependUnicode) { 70 | metadata.renamed = true; 71 | metadata.path = join( 72 | dirname(file), 73 | 'u' + 74 | metadata.unicode[0].codePointAt(0)?.toString(16).toUpperCase() + 75 | '-' + 76 | fileBasename, 77 | ); 78 | rename(file, metadata.path, (err) => { 79 | if (err) { 80 | cb( 81 | new Error( 82 | 'Could not save codepoint: ' + 83 | 'u' + 84 | metadata.unicode[0] 85 | .codePointAt(0) 86 | ?.toString(16) 87 | .toUpperCase() + 88 | ' for ' + 89 | fileBasename, 90 | ), 91 | ); 92 | return; 93 | } 94 | cb(null, metadata); 95 | }); 96 | } 97 | } 98 | if (!metadata.renamed) { 99 | setImmediate(() => cb(null, metadata)); 100 | } 101 | }; 102 | } 103 | 104 | export { getMetadataService }; 105 | -------------------------------------------------------------------------------- /src/svgshapes2svgpath.ts: -------------------------------------------------------------------------------- 1 | const svgShapesToPath = { 2 | rectToPath: svgShapesToPathRectToPath, 3 | polylineToPath: svgShapesToPathPolylineToPath, 4 | lineToPath: svgShapesToPathLineToPath, 5 | circleToPath: svgShapesToPathCircleToPath, 6 | polygonToPath: svgShapesToPathPolygonToPath, 7 | }; 8 | 9 | export default svgShapesToPath; 10 | 11 | // Shapes helpers (should also move elsewhere) 12 | function svgShapesToPathRectToPath(attributes) { 13 | const x = 'undefined' !== typeof attributes.x ? parseFloat(attributes.x) : 0; 14 | const y = 'undefined' !== typeof attributes.y ? parseFloat(attributes.y) : 0; 15 | const width = 16 | 'undefined' !== typeof attributes.width ? parseFloat(attributes.width) : 0; 17 | const height = 18 | 'undefined' !== typeof attributes.height 19 | ? parseFloat(attributes.height) 20 | : 0; 21 | const rx = 22 | 'undefined' !== typeof attributes.rx 23 | ? parseFloat(attributes.rx) 24 | : 'undefined' !== typeof attributes.ry 25 | ? parseFloat(attributes.ry) 26 | : 0; 27 | const ry = 28 | 'undefined' !== typeof attributes.ry ? parseFloat(attributes.ry) : rx; 29 | 30 | return ( 31 | '' + 32 | // start at the left corner 33 | 'M' + 34 | (x + rx) + 35 | ' ' + 36 | y + 37 | // top line 38 | 'h' + 39 | (width - rx * 2) + 40 | // upper right corner 41 | (rx || ry ? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx + ' ' + ry : '') + 42 | // Draw right side 43 | 'v' + 44 | (height - ry * 2) + 45 | // Draw bottom right corner 46 | (rx || ry ? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx * -1 + ' ' + ry : '') + 47 | // Down the down side 48 | 'h' + 49 | (width - rx * 2) * -1 + 50 | // Draw bottom right corner 51 | (rx || ry 52 | ? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx * -1 + ' ' + ry * -1 53 | : '') + 54 | // Down the left side 55 | 'v' + 56 | (height - ry * 2) * -1 + 57 | // Draw bottom right corner 58 | (rx || ry ? 'a ' + rx + ' ' + ry + ' 0 0 1 ' + rx + ' ' + ry * -1 : '') + 59 | // Close path 60 | 'z' 61 | ); 62 | } 63 | 64 | function svgShapesToPathPolylineToPath(attributes) { 65 | return 'M' + attributes.points; 66 | } 67 | 68 | function svgShapesToPathLineToPath(attributes) { 69 | // Move to the line start 70 | return ( 71 | '' + 72 | 'M' + 73 | (parseFloat(attributes.x1) || 0).toString(10) + 74 | ' ' + 75 | (parseFloat(attributes.y1) || 0).toString(10) + 76 | ' ' + 77 | ((parseFloat(attributes.x1) || 0) + 1).toString(10) + 78 | ' ' + 79 | ((parseFloat(attributes.y1) || 0) + 1).toString(10) + 80 | ' ' + 81 | ((parseFloat(attributes.x2) || 0) + 1).toString(10) + 82 | ' ' + 83 | ((parseFloat(attributes.y2) || 0) + 1).toString(10) + 84 | ' ' + 85 | (parseFloat(attributes.x2) || 0).toString(10) + 86 | ' ' + 87 | (parseFloat(attributes.y2) || 0).toString(10) + 88 | 'Z' 89 | ); 90 | } 91 | 92 | function svgShapesToPathCircleToPath(attributes) { 93 | const cx = parseFloat(attributes.cx || 0); 94 | const cy = parseFloat(attributes.cy || 0); 95 | const rx = 96 | 'undefined' !== typeof attributes.rx 97 | ? parseFloat(attributes.rx) 98 | : parseFloat(attributes.r); 99 | const ry = 100 | 'undefined' !== typeof attributes.ry 101 | ? parseFloat(attributes.ry) 102 | : parseFloat(attributes.r); 103 | 104 | // use two A commands because one command which returns to origin is invalid 105 | return ( 106 | '' + 107 | 'M' + 108 | (cx - rx) + 109 | ',' + 110 | cy + 111 | 'A' + 112 | rx + 113 | ',' + 114 | ry + 115 | ' 0,0,0 ' + 116 | (cx + rx) + 117 | ',' + 118 | cy + 119 | 'A' + 120 | rx + 121 | ',' + 122 | ry + 123 | ' 0,0,0 ' + 124 | (cx - rx) + 125 | ',' + 126 | cy 127 | ); 128 | } 129 | 130 | function svgShapesToPathPolygonToPath(attributes) { 131 | return 'M' + attributes.points + 'Z'; 132 | } 133 | -------------------------------------------------------------------------------- /src/tests/cli.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect } from '@jest/globals'; 2 | import { readFile, mkdir } from 'node:fs/promises'; 3 | import { join } from 'node:path'; 4 | import { exec } from 'node:child_process'; 5 | import { promisify } from 'node:util'; 6 | 7 | try { 8 | await mkdir(join('fixtures', 'results')); 9 | } catch (err) { 10 | // empty 11 | } 12 | 13 | describe('Testing CLI', () => { 14 | test('should work for simple SVG', async () => { 15 | const command = 16 | `${'node' + ' '}${join('bin', 'svgicons2svgfont.js')} -o ${join( 17 | 'fixtures', 18 | 'results', 19 | 'originalicons-cli.svg', 20 | )} -s 0xE001` + ` ${join('fixtures', 'icons', 'originalicons', '*.svg')}`; 21 | 22 | await promisify(exec)(command); 23 | 24 | expect( 25 | await readFile(join('fixtures', 'results', 'originalicons-cli.svg'), { 26 | encoding: 'utf8', 27 | }), 28 | ).toEqual( 29 | await readFile(join('fixtures', 'expected', 'originalicons-cli.svg'), { 30 | encoding: 'utf8', 31 | }), 32 | ); 33 | }); 34 | 35 | test('should work for more than 32 SVG icons', async () => { 36 | const command = 37 | 'node' + 38 | ' ' + 39 | join('bin', 'svgicons2svgfont.js') + 40 | ' -o ' + 41 | join('fixtures', 'results', 'lotoficons-cli.svg') + 42 | ' -s 0xE001' + 43 | ' -r 1e4' + 44 | ' ' + 45 | join('fixtures', 'icons', 'cleanicons', '*.svg') + 46 | ' ' + 47 | join('fixtures', 'icons', 'hiddenpathesicons', '*.svg') + 48 | ' ' + 49 | join('fixtures', 'icons', 'multipathicons', 'kikoolol.svg') + 50 | ' ' + 51 | join('fixtures', 'icons', 'originalicons', '*.svg') + 52 | ' ' + 53 | join('fixtures', 'icons', 'realicons', '*.svg') + 54 | ' ' + 55 | join('fixtures', 'icons', 'roundedcorners', '*.svg') + 56 | ' ' + 57 | join('fixtures', 'icons', 'shapeicons', '*.svg') + 58 | ' ' + 59 | join('fixtures', 'icons', 'tocentericons', '*.svg'); 60 | 61 | await promisify(exec)(command); 62 | 63 | expect( 64 | await readFile(join('fixtures', 'results', 'lotoficons-cli.svg'), { 65 | encoding: 'utf8', 66 | }), 67 | ).toEqual( 68 | await readFile(join('fixtures', 'expected', 'lotoficons-cli.svg'), { 69 | encoding: 'utf8', 70 | }), 71 | ); 72 | }); 73 | 74 | describe('with nested icons', () => { 75 | test('should work', async () => { 76 | const command = `${'node' + ' '}${join( 77 | 'bin', 78 | 'svgicons2svgfont.js', 79 | )} -o ${join( 80 | 'fixtures', 81 | 'results', 82 | 'nestedicons-cli.svg', 83 | )} ${join('fixtures', 'icons', 'nestedicons', '*.svg')}`; 84 | 85 | await promisify(exec)(command); 86 | 87 | expect( 88 | await readFile(join('fixtures', 'results', 'nestedicons-cli.svg'), { 89 | encoding: 'utf8', 90 | }), 91 | ).toEqual( 92 | await readFile(join('fixtures', 'expected', 'nestedicons-cli.svg'), { 93 | encoding: 'utf8', 94 | }), 95 | ); 96 | }); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /src/tests/filesorter.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect } from '@jest/globals'; 2 | import { fileSorter } from '../filesorter.js'; 3 | 4 | describe('fileSorter', () => { 5 | test('should sort files per filename', () => { 6 | expect( 7 | [ 8 | '/var/plop/c.svg', 9 | '/var/plop/a.svg', 10 | '/var/plop/A.svg', 11 | '/var/plop/C.svg', 12 | '/var/plop/B.svg', 13 | '/var/plop/b.svg', 14 | ].sort(fileSorter), 15 | ).toEqual([ 16 | '/var/plop/A.svg', 17 | '/var/plop/B.svg', 18 | '/var/plop/C.svg', 19 | '/var/plop/a.svg', 20 | '/var/plop/b.svg', 21 | '/var/plop/c.svg', 22 | ]); 23 | }); 24 | 25 | test('should sort files per codepoints', () => { 26 | expect( 27 | [ 28 | '/var/plop/uAE01-c.svg', 29 | '/var/plop/uAE03-a.svg', 30 | '/var/plop/uAE02-A.svg', 31 | '/var/plop/uAE06-C.svg', 32 | '/var/plop/uAE04-B.svg', 33 | '/var/plop/uAE05-b.svg', 34 | ].sort(fileSorter), 35 | ).toEqual([ 36 | '/var/plop/uAE01-c.svg', 37 | '/var/plop/uAE02-A.svg', 38 | '/var/plop/uAE03-a.svg', 39 | '/var/plop/uAE04-B.svg', 40 | '/var/plop/uAE05-b.svg', 41 | '/var/plop/uAE06-C.svg', 42 | ]); 43 | }); 44 | 45 | test('should put codepoints first', () => { 46 | expect( 47 | [ 48 | '/var/plop/uAE01-c.svg', 49 | '/var/plop/uAE03-a.svg', 50 | '/var/plop/uAE02-A.svg', 51 | '/var/plop/C.svg', 52 | '/var/plop/B.svg', 53 | '/var/plop/b.svg', 54 | ].sort(fileSorter), 55 | ).toEqual([ 56 | '/var/plop/uAE01-c.svg', 57 | '/var/plop/uAE02-A.svg', 58 | '/var/plop/uAE03-a.svg', 59 | '/var/plop/B.svg', 60 | '/var/plop/C.svg', 61 | '/var/plop/b.svg', 62 | ]); 63 | }); 64 | 65 | test('should work with the @pinin files', () => { 66 | expect( 67 | [ 68 | 'bell-disabled.svg', 69 | 'bell-disabled-o.svg', 70 | 'bell-o.svg', 71 | 'UEA01-calendar-agenda.svg', 72 | 'UEA02-calendar-alert.svg', 73 | 'UEA03-calendar.svg', 74 | 'uEA04-bookmark-favorite.svg', 75 | 'uEA05-bookmark-o.svg', 76 | 'uEA06-bookmark.svg', 77 | ].sort(fileSorter), 78 | ).toEqual([ 79 | 'UEA01-calendar-agenda.svg', 80 | 'UEA02-calendar-alert.svg', 81 | 'UEA03-calendar.svg', 82 | 'uEA04-bookmark-favorite.svg', 83 | 'uEA05-bookmark-o.svg', 84 | 'uEA06-bookmark.svg', 85 | 'bell-disabled.svg', 86 | 'bell-disabled-o.svg', 87 | 'bell-o.svg', 88 | ]); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /src/tests/metadata.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect } from '@jest/globals'; 2 | import { writeFile, readFile, unlink } from 'node:fs/promises'; 3 | import { promisify } from 'node:util'; 4 | import { getMetadataService } from '../metadata.js'; 5 | import { YError } from 'yerror'; 6 | import { mkdir } from 'node:fs/promises'; 7 | import { join } from 'node:path'; 8 | 9 | try { 10 | await mkdir(join('fixtures', 'results')); 11 | } catch (err) { 12 | // empty 13 | } 14 | 15 | describe('Metadata service', () => { 16 | describe('for code generation', () => { 17 | test('should extract right unicodes from files', async () => { 18 | const metadataService = getMetadataService(); 19 | const infos = await promisify(metadataService)('/var/plop/hello.svg'); 20 | 21 | expect(infos).toEqual({ 22 | path: '/var/plop/hello.svg', 23 | name: 'hello', 24 | unicode: [String.fromCharCode(0xea01)], 25 | renamed: false, 26 | }); 27 | }); 28 | 29 | test('should append unicodes to files when the option is set', async () => { 30 | const metadataService = getMetadataService({ 31 | prependUnicode: true, 32 | }); 33 | 34 | await writeFile(join('fixtures', 'results', 'plop.svg'), 'plop', 'utf-8'); 35 | const infos = await promisify(metadataService)( 36 | join('fixtures', 'results', 'plop.svg'), 37 | ); 38 | 39 | expect(infos).toEqual({ 40 | path: join('fixtures', 'results', 'uEA01-plop.svg'), 41 | name: 'plop', 42 | unicode: [String.fromCharCode(0xea01)], 43 | renamed: true, 44 | }); 45 | expect( 46 | await readFile(join('fixtures', 'results', 'uEA01-plop.svg')), 47 | ).toBeTruthy(); 48 | unlink(join('fixtures', 'results', 'uEA01-plop.svg')); 49 | try { 50 | await readFile(join('fixtures', 'results', 'plop.svg')); 51 | throw new YError('E_UNEXPECTED_SUCCESS'); 52 | } catch (err) { 53 | expect((err as YError).code === 'E_UNEXPECTED_SUCCESS').toBeFalsy(); 54 | } 55 | }); 56 | 57 | test('should log file rename errors', async () => { 58 | const metadataService = getMetadataService({ 59 | prependUnicode: true, 60 | startUnicode: 0xea02, 61 | }); 62 | 63 | try { 64 | await promisify(metadataService)( 65 | join('fixtures', 'results', 'plop.svg'), 66 | ); 67 | 68 | throw new YError('E_UNEXPECTED_SUCCESS'); 69 | } catch (err) { 70 | expect(err).toBeTruthy(); 71 | expect((err as YError).code === 'E_UNEXPECTED_SUCCESS').toBeFalsy(); 72 | } 73 | try { 74 | await readFile(join('fixtures', 'results', 'uEA02-plop.svg')); 75 | throw new YError('E_UNEXPECTED_SUCCESS'); 76 | } catch (err) { 77 | expect((err as YError).code === 'E_UNEXPECTED_SUCCESS').toBeFalsy(); 78 | } 79 | }); 80 | }); 81 | 82 | describe('for code extraction', () => { 83 | test('should work for simple codes', async () => { 84 | const metadataService = getMetadataService(); 85 | const infos = await promisify(metadataService)( 86 | '/var/plop/u0001-hello.svg', 87 | ); 88 | 89 | expect(infos).toEqual({ 90 | path: '/var/plop/u0001-hello.svg', 91 | name: 'hello', 92 | unicode: [String.fromCharCode(0x0001)], 93 | renamed: false, 94 | }); 95 | }); 96 | 97 | test('should work for several codes', async () => { 98 | const metadataService = getMetadataService(); 99 | const infos = await promisify(metadataService)( 100 | '/var/plop/u0001,u0002-hello.svg', 101 | ); 102 | 103 | expect(infos).toEqual({ 104 | path: '/var/plop/u0001,u0002-hello.svg', 105 | name: 'hello', 106 | unicode: [String.fromCharCode(0x0001), String.fromCharCode(0x0002)], 107 | renamed: false, 108 | }); 109 | }); 110 | 111 | test('should work for higher codepoint codes', async () => { 112 | const metadataService = getMetadataService(); 113 | const infos = await promisify(metadataService)( 114 | '/var/plop/u1F63A-hello.svg', 115 | ); 116 | 117 | expect(infos).toEqual({ 118 | path: '/var/plop/u1F63A-hello.svg', 119 | name: 'hello', 120 | unicode: [String.fromCodePoint(0x1f63a)], 121 | renamed: false, 122 | }); 123 | }); 124 | 125 | test('should work for ligature codes', async () => { 126 | const metadataService = getMetadataService(); 127 | const infos = await promisify(metadataService)( 128 | '/var/plop/u0001u0002-hello.svg', 129 | ); 130 | 131 | expect(infos).toEqual({ 132 | path: '/var/plop/u0001u0002-hello.svg', 133 | name: 'hello', 134 | unicode: [String.fromCharCode(0x0001) + String.fromCharCode(0x0002)], 135 | renamed: false, 136 | }); 137 | }); 138 | 139 | test('should work for nested codes', async () => { 140 | const metadataService = getMetadataService(); 141 | const infos = await promisify(metadataService)( 142 | '/var/plop/u0001u0002,u0001-hello.svg', 143 | ); 144 | 145 | expect(infos).toEqual({ 146 | path: '/var/plop/u0001u0002,u0001-hello.svg', 147 | name: 'hello', 148 | unicode: [ 149 | String.fromCharCode(0x0001) + String.fromCharCode(0x0002), 150 | String.fromCharCode(0x0001), 151 | ], 152 | renamed: false, 153 | }); 154 | }); 155 | 156 | test('should not set the same codepoint twice', async () => { 157 | const metadataService = getMetadataService(); 158 | 159 | const infos = await promisify(metadataService)( 160 | '/var/plop/uEA01-hello.svg', 161 | ); 162 | 163 | expect(infos).toEqual({ 164 | path: '/var/plop/uEA01-hello.svg', 165 | name: 'hello', 166 | unicode: [String.fromCharCode(0xea01)], 167 | renamed: false, 168 | }); 169 | 170 | const infos2 = await promisify(metadataService)('/var/plop/plop.svg'); 171 | 172 | expect(infos2).toEqual({ 173 | path: '/var/plop/plop.svg', 174 | name: 'plop', 175 | unicode: [String.fromCharCode(0xea02)], 176 | renamed: false, 177 | }); 178 | }); 179 | 180 | test('should not set the same codepoint twice with different cases', async () => { 181 | const metadataService = getMetadataService(); 182 | 183 | const infos = await promisify(metadataService)( 184 | '/var/plop/UEA01-hello.svg', 185 | ); 186 | 187 | expect(infos).toEqual({ 188 | path: '/var/plop/UEA01-hello.svg', 189 | name: 'hello', 190 | unicode: [String.fromCharCode(0xea01)], 191 | renamed: false, 192 | }); 193 | 194 | const infos2 = await promisify(metadataService)( 195 | '/var/plop/uEA02-hello.svg', 196 | ); 197 | 198 | expect(infos2).toEqual({ 199 | path: '/var/plop/uEA02-hello.svg', 200 | name: 'hello', 201 | unicode: [String.fromCharCode(0xea02)], 202 | renamed: false, 203 | }); 204 | 205 | const infos3 = await promisify(metadataService)('/var/plop/bell-o.svg'); 206 | 207 | expect(infos3).toEqual({ 208 | path: '/var/plop/bell-o.svg', 209 | name: 'bell-o', 210 | unicode: [String.fromCharCode(0xea03)], 211 | renamed: false, 212 | }); 213 | }); 214 | }); 215 | }); 216 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "moduleResolution": "Node16", 5 | "target": "es2022", 6 | "noImplicitAny": false, 7 | "removeComments": false, 8 | "preserveConstEnums": true, 9 | "allowSyntheticDefaultImports": true, 10 | "esModuleInterop": true, 11 | "strict": true, 12 | "declaration": true, 13 | "outDir": "dist", 14 | "sourceMap": true 15 | }, 16 | "include": [ 17 | "src/**/*.ts" 18 | ], 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } --------------------------------------------------------------------------------