pax_global_header00006660000000000000000000000064152207546260014523gustar00rootroot0000000000000052 comment=2364bc777578d484151acc11c998886911bc8df6 jaywcjlove-svgtofont-2472abc/000077500000000000000000000000001522075462600163355ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/.editorconfig000066400000000000000000000001051522075462600210060ustar00rootroot00000000000000root = true [*] charset = utf-8 indent_style = space indent_size = 2 jaywcjlove-svgtofont-2472abc/.github/000077500000000000000000000000001522075462600176755ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/.github/FUNDING.yml000066400000000000000000000001661522075462600215150ustar00rootroot00000000000000github: [jaywcjlove] #ko_fi: jaywcjlove #buy_me_a_coffee: jaywcjlove # custom: ["https://wangchujiang.com/#/sponsor"] jaywcjlove-svgtofont-2472abc/.github/workflows/000077500000000000000000000000001522075462600217325ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/.github/workflows/ci.yml000066400000000000000000000075501522075462600230570ustar00rootroot00000000000000name: Build on: push: branches: - master jobs: build-deploy: runs-on: ubuntu-latest permissions: contents: write id-token: write steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v6 with: node-version: 24 registry-url: 'https://registry.npmjs.org' - run: npm install - run: npm run checked - run: npm run example - run: npm run example:cli - run: npm run example:simple - run: npm run example:templates - run: npm run coverage - run: cp -rf examples/example/dist coverage/example - run: cp -rf examples/templates/dist2 coverage/templates - name: Create idoc config. run: | cat > idoc.yml << EOF site: "SVG To Font {{version}}" menus: Home: index.html Sponsor: url: https://wangchujiang.com/#/sponsor target: __blank Demo: url: example/font-class.html target: __blank EOF - run: npm install idoc@1 -g - run: idoc --output coverage # - name: Converts Markdown to HTML # uses: jaywcjlove/markdown-to-html-cli@main # with: # output: coverage/index.html # github-corners: https://github.com/jaywcjlove/svgtofont.git - uses: jaywcjlove/coverage-badges-cli@main - name: Generate Contributors Images uses: jaywcjlove/github-action-contributors@main with: filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\]) avatarSize: 42 output: coverage/CONTRIBUTORS.svg - name: Create Tag id: create_tag uses: jaywcjlove/create-tag-action@main with: package-path: ./package.json - name: get tag version id: tag_version uses: jaywcjlove/changelog-generator@main - name: Deploy uses: peaceiris/actions-gh-pages@v4 with: commit_message: ${{steps.tag_version.outputs.tag}} ${{ github.event.head_commit.message }} github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./coverage - name: Generate Changelog id: changelog uses: jaywcjlove/changelog-generator@main if: steps.create_tag.outputs.successful with: head-ref: ${{steps.create_tag.outputs.version}} filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' - name: ♻� Create Release uses: jaywcjlove/create-tag-action@main with: package-path: ./package.json release: true body: | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/svgtofont@${{steps.create_tag.outputs.versionNumber}}/file/README.md) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/svgtofont)](https://bundlephobia.com/result?p=svgtofont@${{steps.create_tag.outputs.versionNumber}}) [![npm version](https://img.shields.io/npm/v/svgtofont.svg)](https://www.npmjs.com/package/svgtofont) Documentation ${{ steps.changelog.outputs.tag }}: https://raw.githack.com/jaywcjlove/svgtofont/${{ steps.changelog.outputs.gh-pages-short-hash }}/index.html Comparing Changes: ${{ steps.changelog.outputs.compareurl }} ```bash npm i svgtofont ``` ${{ steps.changelog.outputs.changelog }} # - name: 📦 svgtofont publish to NPM # uses: JS-DevTools/npm-publish@v1 # with: # token: ${{ secrets.NPM_TOKEN }} # package: ./package.json - name: 📦 svgtofont publish to NPM run: NODE_AUTH_TOKEN="" npm publish --access public --provenance continue-on-error: truejaywcjlove-svgtofont-2472abc/.gitignore000066400000000000000000000031641522075462600203310ustar00rootroot00000000000000# Created by https://www.gitignore.io/api/node # Edit at https://www.gitignore.io/?templates=node dist examples/example/dist examples/templates/dist examples/example#211/font lib package-lock.json yarn.lock __snapshots__ ### Node ### # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.test # parcel-bundler cache (https://parceljs.org/) .cache # next.js build output .next # nuxt.js build output .nuxt # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # End of https://www.gitignore.io/api/node .DS_Store .cache .vscode .idea .env *.bak *.tem *.temp #.swp *.*~ ~*.* # IDEA *.iml *.ipr *.iws .idea/ jaywcjlove-svgtofont-2472abc/.npmignore000066400000000000000000000000051522075462600203270ustar00rootroot00000000000000test jaywcjlove-svgtofont-2472abc/LICENSE000066400000000000000000000020601522075462600173400ustar00rootroot00000000000000MIT License Copyright (c) 2018 �弟调调™ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jaywcjlove-svgtofont-2472abc/README.md000066400000000000000000000731721522075462600176260ustar00rootroot00000000000000
Using my app is also a way to support me:
Zipora: Zip/RAR/7Z Unarchiver Scap: Screenshot & Markup Edit Screen Test Deskmark Keyzer Vidwall Hub VidCrop Vidwall Mousio Hint Mousio Musicer Audioer FileSentinel FocusCursor Videoer KeyClicker DayBar Iconed Menuist Quick RSS Quick RSS Web Serve Copybook Generator DevTutor for SwiftUI RegexMate Time Passage Iconize Folder Textsound Saver Create Custom Symbols DevHub Resume Revise Palette Genius Symbol Scribe

[Free Font](https://github.com/jaywcjlove/free-font)

SVG To Font

Buy me a coffee Follow On X Build & Deploy Open in unpkg NPM Download npm version

Read a set of SVG icons and ouput a TTF/EOT/WOFF/WOFF2/SVG font, Generator of fonts from SVG icons. [Install](#install) · [Usage](#using-with-nodejs) · [Command](#using-with-command) · [Font Usage](#font-usage) · [API](#api) · [options](#options) · [npm](http://npm.im/svgtofont) · [License](#license) **Features:** - Supported font formats: `WOFF2`, `WOFF`, `EOT`, `TTF` and `SVG`. - Support SVG Symbol file. - Support [`React`](https://github.com/facebook/react), [`ReactNative`](https://github.com/facebook/react-native), [`Vue`](https://github.com/vuejs/core) & [`TypeScript`](https://github.com/microsoft/TypeScript). - Support [`Less`](https://github.com/less/less.js)/[`Sass`](https://github.com/sass/sass)/[`Stylus`](https://github.com/stylus/stylus). - Allows to use custom templates (example `css`, `less` and etc). - Automatically generate a preview site. ```bash ╭┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╮ ┆ Project ┆ ┆ ╭┈┈┈┈┈┈┈┈┈┈┈╮ ┆ ╭┈┈┈┈┈┈┈┈╮ ┆ ┆ svg ┆┈┈╮ ┆ ┆iconfont┆┈┈╮ ┆ ╰┈┈┈┈┈┈┈┈┈┈┈╯ ┆ ┆ ╰┈┈┈┈┈┈┈┈╯ ┆ ╭┈┈┈┈┈┈┈┈┈┈┈┈╮ ┆ ╭┈┈┈┈┈┈┈┈┈┈┈╮ ┆ ┆ ├┈▶┆download svg┆┈┈▶┆ ┆┈svgtofont┈┆ ┆ ┆ ╭┈┈┈┈┈┈┈┈╮ ┆ ╰┈┈┈┈┈┈┈┈┈┈┈┈╯ ┆╭┈┈┆create font┆◀┈╯ ┆ ┆icomoon ┆┈┈╯ ┆┆ ╰┈┈┈┈┈┈┈┈┈┈┈╯ ┆ ╰┈┈┈┈┈┈┈┈╯ ┆┆ ╭┈┈┈┈┈┈┈┈┈┈┈╮ ┆ ┆╰┈▶┆ use font ┆ ┆ ┆ ╰┈┈┈┈┈┈┈┈┈┈┈╯ ┆ ╰┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╯ ``` ```mermaid graph LR; A[iconfont]-->C[Download SVG]; B[icomoon]-->C; D[icongo]-->C; E[yesicon]-->C; click A "https://www.iconfont.cn" "阿里巴巴矢é‡�图标库" _blank click B "https://icomoon.io" "Pixel Perfect Icon Solutions" _blank click D "https://icongo.github.io" "Include popular icons in your React projects easily icons." _blank click E "https://yesicon.app/" "216,162 High-Quality Vector Icons from Top Design Teams." _blank C .-> ide1 subgraph ide1 [Project] svg -->a2[svgtofont create font] a2 .-> b3[use font] end ``` **Icon Font Created By svgtofont** - [file-icons](https://uiwjs.github.io/file-icons/) File icons in the file tree. - [uiw-iconfont](https://github.com/uiwjs/icons) The premium icon font for [@uiwjs](https://github.com/uiwjs) Component Library. Support [`React`](https://github.com/facebook/react) & [`TypeScript`](https://github.com/microsoft/TypeScript). - [Bootstrap Icons Font](https://github.com/uiwjs/bootstrap-icons) Official open source SVG icon library for Bootstrap. - [test example](./test) For a simple test example, run `npm run test` in the root directory to see the results. ## Install ```bash npm i svgtofont ``` > [!NOTE] > This package `v5+` is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): Node 18+ is needed to use it and it must be `import` instead of `require`. > ```js > import svgtofont from 'svgtofont'; > ``` #### Using With Command ```json { "scripts": { "font": "svgtofont --sources ./svg --output ./font --fontName uiw-font" }, "svgtofont": { "css": { "fontSize": "12px" } } } ``` You can add configuration to package.json. [#48](https://github.com/jaywcjlove/svgtofont/issues/48) Support for `.svgtofontrc` and [more](https://github.com/jaywcjlove/auto-config-loader/blob/add7ae012f5c3903296fbf0ef06e3631e379c2cc/core/README.md?plain=1#L106-L135) configuration files. ```js { "fontName": "svgtofont", "css": true } ``` ```js /** * @type {import('svgtofont').SvgToFontOptions} */ export default { fontName: "iconfont", } ``` #### Using With Nodejs > [!NOTE] > This package `v5+` is now pure ESM. Please [read this](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). ```js import svgtofont from 'svgtofont'; import path from 'node:path'; svgtofont({ src: path.resolve(process.cwd(), 'icon'), // svg path, only searches one level, not recursive dist: path.resolve(process.cwd(), 'fonts'), // output path fontName: 'svgtofont', // font name css: true, // Create CSS files. }).then(() => { console.log('done!'); }); ``` Or ```js import svgtofont from 'svgtofont'; import path from 'node:path'; svgtofont({ src: path.resolve(process.cwd(), "icon"), // svg path, only searches one level, not recursive dist: path.resolve(process.cwd(), "fonts"), // output path styleTemplates: path.resolve(rootPath, "styles"), // file templates path (optional) fontName: "svgtofont", // font name css: true, // Create CSS files. startUnicode: 0xea01, // unicode start number svgicons2svgfont: { fontHeight: 1000, normalize: true }, // website = null, no demo html files website: { title: "svgtofont", // Must be a .svg format image. logo: path.resolve(process.cwd(), "svg", "git.svg"), version: pkg.version, meta: { description: "Converts SVG fonts to TTF/EOT/WOFF/WOFF2/SVG format.", keywords: "svgtofont,TTF,EOT,WOFF,WOFF2,SVG" }, description: ``, // Add a Github corner to your website // Like: https://github.com/uiwjs/react-github-corners corners: { url: 'https://github.com/jaywcjlove/svgtofont', width: 62, // default: 60 height: 62, // default: 60 bgColor: '#dc3545' // default: '#151513' }, links: [ { title: "GitHub", url: "https://github.com/jaywcjlove/svgtofont" }, { title: "Feedback", url: "https://github.com/jaywcjlove/svgtofont/issues" }, { title: "Font Class", url: "index.html" }, { title: "Unicode", url: "unicode.html" } ], footerInfo: `Licensed under MIT. (Yes it's free and open-sourced` } }).then(() => { console.log('done!'); });; ``` ## API ```js import { createSVG, createTTF, createEOT, createWOFF, createWOFF2, createSvgSymbol, copyTemplate, createHTML } from 'svgtofont/lib/utils'; const options = { ... }; async function createFont() { const unicodeObject = await createSVG(options); const ttf = await createTTF(options); // SVG Font => TTF await createEOT(options, ttf); // TTF => EOT await createWOFF(options, ttf); // TTF => WOFF await createWOFF2(options, ttf); // TTF => WOFF2 await createSvgSymbol(options); // SVG Files => SVG Symbol } ``` ## options > svgtofont(options) ### config > Type: `config?: AutoConfOption` By default, settings are automatically loaded from `.svgtofontrc` and `package.json`. You can add configuration to `package.json`. [#48](https://github.com/jaywcjlove/svgtofont/issues/48) Support for `.svgtofontrc` and [more](https://github.com/jaywcjlove/auto-config-loader/blob/add7ae012f5c3903296fbf0ef06e3631e379c2cc/core/README.md?plain=1#L106-L135) configuration files. ### log > Type: `Boolean` A value of `false` disables logging ### logger > Type: `(msg) => void` log callback function ### dist > Type: `String` > Default value: ~~`dist`~~ => `fonts` The output directory. ### outSVGReact > Type: `Boolean` > Default value: `false` Output `./dist/react/`, SVG generates `react` components. ```js git/git.svg // ↓↓↓↓↓↓↓↓↓↓ import React from 'react'; export const Git = props => ( ); ``` ### outSVGReactNative > Type: `Boolean` > Default value: `false` Output `./dist/reactNative/`, SVG generates `reactNative` components. ```js import { Text } from 'react-native'; const icons = { "Git": "__GitUnicodeChar__", "Adobe": "__AdobeUnicodeChar__" }; export const RangeIconFont = props => { const { name, ...rest } = props; return ( {icons[name]} ); }; ``` ### outSVGVue > Type: `Boolean` > Default value: `false` Output `./dist/vue/`, SVG generates `vue` components. ```js git/git.svg // ↓↓↓↓↓↓↓↓↓↓ import { defineComponent, h } from 'vue'; export const Git = defineComponent({ name: 'Git', props: { class: { type: String, default: '' } }, setup(props, { attrs }) { return () => h( 'svg', { viewBox: '0 0 20 20', width: undefined, height: undefined, class: `svgtofont ${props.class}`, ...attrs }, [] ); } }); ``` ### outSVGPath > Type: `Boolean` > Default value: `false` Output `./dist/svgtofont.json`, The content is as follows: ```js { "adobe": ["M14.868 3H23v19L14.868 3zM1 3h8.138L1 22V3zm.182 11.997H13.79l-1.551-3.82H8.447z...."], "git": ["M2.6 10.59L8.38 4.8l1.69 1.7c-.24.85.15 1.78.93 2.23v5.54c-.6.34-1 .99-1..."], "stylelint": ["M129.74 243.648c28-100.109 27.188-100.5.816c2.65..."] } ``` Or you can generate the file separately: ```js const { generateIconsSource } = require('svgtofont/src/generate'); const path = require('path'); async function generate () { const outPath = await generateIconsSource({ src: path.resolve(process.cwd(), 'svg'), dist: path.resolve(process.cwd(), 'dist'), fontName: 'svgtofont', }); } generate(); ``` ### generateInfoData > Type: `Boolean` > Default value: `false` Output `./dist/info.json`, The content is as follows: ```js { "adobe": { "encodedCode": "\\ea01", "prefix": "svgtofont", "className": "svgtofont-adobe", "unicode": "" }, ... } ``` ### src > Type: `String` > Default value: `svg` output path ### emptyDist > Type: `String` > Default value: `false` Clear output directory contents ### fontName > Type: `String` > Default value: `iconfont` The font family name you want. ### styleTemplates > Type: `String` > Default value: `undefined` The path of the templates, see `src/styles` or `test/templates/styles` to get reference about how to create a template, file names can have the extension .template, like a `filename.scss.template` ### startUnicode > Type: `Number` > Default value: `0xea01` unicode start number ### getIconUnicode Get Icon Unicode ```ts getIconUnicode?: (name: string, unicode: string, startUnicode: number) => [string, number]; ``` ### useNameAsUnicode > Type: `Boolean` > Default value: `false` should the name(file name) be used as unicode? this switch allows for the support of ligatures. let's say you have an svg with a file name of `add` and you want to use ligatures for it. you would set up your processing as mentioned above and turn on this switch. ```js { ... useNameAsUnicode: true } ``` while processing, instead of using a single sequential char for the unicode, it uses the file name. using the file name as the unicode allows the following code to work as expected. ```css .icons { font-family: 'your-font-icon-name' !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } ``` ```html add ``` as you add more svgs and process them into your font you would just use the same pattern. ```html add remove edit ``` ### addLigatures > Type: `Boolean` > Default value: `false` adds possibility to use name (file name) in addition to codepoints. adds support of ligatures. let's say you have some svgs and you want to use codepoints but for some of them for example with a file name of `add` you want to use ligatures for it. this option only adds ligatures and still allows for using codepoints as usual. this is in contrary to useNameAsUnicode which basically removes support for codepoints in favour of ligatures. ```js { ... addLigatures: true } ``` ### useCSSVars > Type: `Boolean` > Default value: `false` consoles whenever {{ cssString }} template outputs unicode characters or css vars ### classNamePrefix > Type: `String` > Default value: font name Create font class name prefix, default value font name. ### css > Type: `Boolean|CSSOptions` > Default value: `false` Create CSS/LESS files, default `true`. ```ts type CSSOptions = { /** * Output the css file to the specified directory */ output?: string; /** * Which files are exported. */ include?: RegExp; /** * Setting font size. */ fontSize?: string | boolean; /** * Set the path in the css file * https://github.com/jaywcjlove/svgtofont/issues/48#issuecomment-739547189 */ cssPath?: string; /** * Set file name * https://github.com/jaywcjlove/svgtofont/issues/48#issuecomment-739547189 */ fileName?: string; /** * Ad hoc template variables. */ templateVars?: Record; /** * When including CSS files in a CSS file, * you can add a timestamp parameter or custom text to the file path to prevent browser caching issues and ensure style updates are applied. @default true * @example `path/to/iconfont.css?t=1612345678` */ hasTimestamp?: boolean | string; } ``` ### svgicons2svgfont This is the setting for [svgicons2svgfont](https://github.com/nfroidure/svgicons2svgfont/tree/dd713bea4f97afa59f7dba6a21ff7f22db565bcf#api) #### svgicons2svgfont.fontName > Type: `String` > Default value: `'iconfont'` The font family name you want. #### svgicons2svgfont.fontId > Type: `String` > Default value: the options.fontName value The font id you want. #### svgicons2svgfont.fontStyle > Type: `String` > Default value: `''` The font style you want. #### svgicons2svgfont.fontWeight > Type: `String` > Default value: `''` The font weight you want. #### svgicons2svgfont.fixedWidth > Type: `Boolean` > Default value: `false` Creates a monospace font of the width of the largest input icon. #### svgicons2svgfont.centerHorizontally > Type: `Boolean` > Default value: `false` Calculate the bounds of a glyph and center it horizontally. #### svgicons2svgfont.normalize > Type: `Boolean` > Default value: `false` Normalize icons by scaling them to the height of the highest icon. #### svgicons2svgfont.fontHeight > Type: `Number` > Default value: `MAX(icons.height)` The outputted font height (defaults to the height of the highest input icon). #### svgicons2svgfont.round > Type: `Number` > Default value: `10e12` Setup SVG path rounding. #### svgicons2svgfont.descent > Type: `Number` > Default value: `0` The font descent. It is useful to fix the font baseline yourself. **Warning:** The descent is a positive value! #### svgicons2svgfont.ascent > Type: `Number` > Default value: `fontHeight - descent` The font ascent. Use this options only if you know what you're doing. A suitable value for this is computed for you. #### svgicons2svgfont.metadata > Type: `String` > Default value: `undefined` The font [metadata](http://www.w3.org/TR/SVG/metadata.html). You can set any character data in but it is the be suited place for a copyright mention. #### svgicons2svgfont.log > Type: `Function` > Default value: `console.log` Allows you to provide your own logging function. Set to `function(){}` to disable logging. ### svgoOptions > Type: `OptimizeOptions` > Default value: `undefined` Some options can be configured with `svgoOptions` though it. [svgo](https://github.com/svg/svgo#configuration) ### svg2ttf This is the setting for [svg2ttf](https://github.com/fontello/svg2ttf/tree/c33a126920f46b030e8ce960cc7a0e38a6946bbc#svg2ttfsvgfontstring-options---buf) #### svg2ttf.copyright > Type: `String` copyright string #### svg2ttf.ts > Type: `String` Unix timestamp (in seconds) to override creation time #### svg2ttf.version > Type: `Number` font version string, can be Version `x.y` or `x.y`. ### website Define preview web content. Example: ```js { ... // website = null, no demo html files website: { title: "svgtofont", logo: path.resolve(process.cwd(), "svg", "git.svg"), version: pkg.version, meta: { description: "Converts SVG fonts to TTF/EOT/WOFF/WOFF2/SVG format.", keywords: "svgtofont,TTF,EOT,WOFF,WOFF2,SVG", favicon: "./favicon.png" }, // Add a Github corner to your website // Like: https://github.com/uiwjs/react-github-corners corners: { url: 'https://github.com/jaywcjlove/svgtofont', width: 62, // default: 60 height: 62, // default: 60 bgColor: '#dc3545' // default: '#151513' }, links: [ { title: "GitHub", url: "https://github.com/jaywcjlove/svgtofont" }, { title: "Feedback", url: "https://github.com/jaywcjlove/svgtofont/issues" }, { title: "Font Class", url: "index.html" }, { title: "Unicode", url: "unicode.html" } ] } } ``` #### website.template > Type: `String` > Default value: [index.njk](src/website/index.njk) Custom template can customize parameters. You can define your own template based on the [default template](src/website/index.njk). ```js { website: { template: path.join(process.cwd(), "my-template.njk") } } ``` #### website.index > Type: `String` > Default value: `font-class`, Enum{`font-class`, `unicode`, `symbol`} Set default home page. ## Font Usage Suppose the font name is defined as `svgtofont`, The default home page is `unicode`, Will generate: ```bash font-class.html index.html svgtofont.css svgtofont.eot svgtofont.json svgtofont.less svgtofont.module.less svgtofont.scss svgtofont.styl svgtofont.svg svgtofont.symbol.svg svgtofont.ttf svgtofont.woff svgtofont.woff2 symbol.html ``` Preview demo `font-class.html`, `symbol.html` and `index.html`. Automatically generated style `svgtofont.css` and `svgtofont.less`. ### symbol svg ```xml ``` ### Unicode ```html ``` ### Class Name Support for `.less` and `.css` styles references. ```html ``` ### Using With React Icons are used as components. `v3.16.7+` support. ```jsx import { Adobe, Alipay } from '@uiw/icons'; ``` #### In the project created by [create-react-app](https://github.com/facebook/create-react-app) ```jsx import logo from './logo.svg'; ``` ```jsx import { ReactComponent as ComLogo } from './logo.svg'; ``` #### In the project created by [webpack](https://github.com/webpack/webpack) ```bash yarn add babel-plugin-named-asset-import yarn add @svgr/webpack ``` ```js // webpack.config.js [ require.resolve('babel-plugin-named-asset-import'), { loaderMap: { svg: { ReactComponent: '@svgr/webpack?-svgo,+ref![path]', }, }, }, ], ``` ```jsx import { ReactComponent as ComLogo } from './logo.svg'; ``` ### Using With ReactNative A unique component named after the font name is generated. Props are TextProps and are used as inline style. In addition, the iconName prop is mandatory and refers to svg names written in camelCase ```jsx SvgToFont.jsx // ↓↓↓↓↓↓↓↓↓↓ import { SvgToFont } from './SvgToFont'; ``` ```ts SvgToFont.d.ts // ↓↓↓↓↓↓↓↓↓↓ import { TextStyle } from 'react-native'; export type SvgToFontIconNames = 'git'| 'adobe'| 'demo' | 'left' | 'styleInline' export interface SvgToFontProps extends Omit { iconName: SvgToFontIconNames } export declare const SvgToFont: (props: SvgToFontProps) => JSX.Element; ``` ### Using with Vue Icons are used as components. `v3+` support. ```vue ``` ## Contributors As always, thanks to our amazing contributors! Made with [contributors](https://github.com/jaywcjlove/github-action-contributors). ## License Licensed under the [MIT License](https://opensource.org/licenses/MIT). jaywcjlove-svgtofont-2472abc/examples/000077500000000000000000000000001522075462600201535ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/example#211/000077500000000000000000000000001522075462600220755ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/example#211/.gitignore000066400000000000000000000000041522075462600240570ustar00rootroot00000000000000fontjaywcjlove-svgtofont-2472abc/examples/example#211/.svgtofontrc.js000066400000000000000000000002431522075462600250660ustar00rootroot00000000000000import path from "path"; /** * @type {import('svgtofont').SvgToFontOptions} */ export default { fontName: "iconfont", css: { hasTimestamp: "v1.2.3" } }jaywcjlove-svgtofont-2472abc/examples/example#211/package.json000066400000000000000000000004321522075462600243620ustar00rootroot00000000000000{ "name": "ex211", "version": "6.0.0", "description": "", "main": "index.js", "scripts": { "start": "svgtofont --sources ./svg --output ./font" }, "keywords": [], "author": "jaywcjlove", "license": "ISC", "devDependencies": { "svgtofont": "^6.0.0" } } jaywcjlove-svgtofont-2472abc/examples/example#211/svg/000077500000000000000000000000001522075462600226745ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/example#211/svg/adobe.svg000066400000000000000000000006751522075462600244770ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example#211/svg/git.svg000066400000000000000000000011571522075462600242040ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example#211/svg/stylelint.svg000066400000000000000000000046641522075462600254560ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example#233/000077500000000000000000000000001522075462600221015ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/example#233/simple.mjs000066400000000000000000000005571522075462600241140ustar00rootroot00000000000000import svgtofont from '../../lib/index.js'; import path from 'path'; svgtofont({ src: path.resolve(process.cwd(), "svg"), // svg path dist: path.resolve(process.cwd(), "dist"), // output path fontName: 'pst-font', // font name css: true, // Create CSS files. }).then(() => { console.log('done!'); }).catch((error) => { console.error('Error:', error); });jaywcjlove-svgtofont-2472abc/examples/example#233/svg/000077500000000000000000000000001522075462600227005ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/example#233/svg/adobe.svg000066400000000000000000000006751522075462600245030ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example#233/svg/git.svg000066400000000000000000000011571522075462600242100ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example/000077500000000000000000000000001522075462600216065ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/example/.gitignore000066400000000000000000000000071522075462600235730ustar00rootroot00000000000000examplejaywcjlove-svgtofont-2472abc/examples/example/favicon.png000066400000000000000000000026221522075462600237430ustar00rootroot00000000000000‰PNG  IHDR00Wù‡sRGB®ÎéLIDATh½ZMˆE~¯f& ó7Ó³F0A ‹!z0w QvvW=äàÙ”€è%ñ7lð?õ¢'½ „€Ù ¨$ñ xYQÐ,âI“ž™�˜(î³ÓU¾×“Zzzªzú§&uØêzïÕ«ïÛ×õªë1#hK³ÞAs àÁÐ=ÂB©s›Oø?º^];ô§ê/ È£  õ�€«ˆê`µÙú */ú,Š:ˆÎo7j/È÷âàÙF�ªHï·¦½ƒÑ9EŸ�EàŸoãr  üæa Pà¡ÚIÿè0»4zg¸eÞÿ*• Dè [XIõ®«H8‹€í7îÜ�píŒRPÕ2[ï"…"Ð~rÛÀëâ5ÏýRÈ‘hÛ€k¹‹HäŽ@«Q�6æs¢,&«_\øYƒÒ}§±õ. Á²+A¶éóì‰Ôlà5&H¸[�m=�ÈßÚt,ÏJ"�ð„�½6&ê?“¼O¦Ôú¾±a�…ÄP ž>Þ4¬3 ¢,óð€0&@(Ÿ¥(´bâ�aZ‰›¸=]{IJxkÀ»EÀ›ù&¬Ü{[óüe‹I(¾4ëÝÓíÂ7”†kIv¬¶±­È žã}pV�s¶á±­m:áÿZ.Ä‹H ´§¼³üç£@é5zde¥wFDåñgW$„àA½_0˘^�¾»ÏUûÆ×Å}¸ ÑG ÕðÉ‚àã õ¸µ|eniª¾S�u_”į”zG;vÞ£ÜÐEuª3;vÜ7“ÀRew q7¢˜¦�{$<Ýã†4Žg§�¥ÊçG ^QjCÈÓíÇÇîÓ"ÝWçÏý¶¥éWk^lÒegn} Çñ”ÖG{&Á˜Y&ø®:¹E±>+µQ­§[3[Ç­6¤à ÒÍž²F‚03vtáßW“œ¹ÖQºÝôíÔyâŽmI¾Ã[â‡&ÆÌ•~…v™ F-ã3ƒ`èAB,X±PÙfm[�F¨ ³&ñÀK³´ ‰�aElPõ _I>¤´ÛvÁ3.:%ù•N*|Æt¯Öëq©†.?ô8Ú3fÆ.¸Üdz¨ò†=sFºvõ3 ÿ¯„c¼WLx3c�|.÷Q�fçW“ñ(etþL‰E:H?¢lAѧЫEE²gi]3x*ŒUOúa‰rí›…+eD"<銦¼M˜Ä>HF5SúÖã:@¬q$«€þr»ÞõýãŸ×θbV$qµéÒ¿loÔnxOë�Ø4'*‹ƒgÝæ&�ø•×ôc¶æ7¼/)F�Úô6¹ <ÛÏ~�x‚Í™].>µë´&��¶íõ6ð¬5`Ee!ç¹I-�Mt~x¶³`eVA€Ûy^RKc£çÏv‰Ø …jÏIjilx~ð¡]ÒbQ]š�Í)”²Ð^ú®7~ÇwÞÊB_Sid±´àyÊÐh¿i"ÁÀè:Þžñöëyºgë\‚g߯4ª5õi"ÎC¼Hý÷×}ì²t£kdùÏëy™ ðÄÔ$ô*)ú<àÙm.<Ñ%‰¼à pE¢øÂŠ’( Þ �¼$\€wF + Wà�HKÂ%xçØa¯Ê‡ôc�þZßaGñc�Üi”ÁÚÚ�ü¹Íÿº~FÅK‚IEND®B`‚jaywcjlove-svgtofont-2472abc/examples/example/index.mjs000066400000000000000000000045601522075462600234350ustar00rootroot00000000000000import path from 'node:path'; import { fileURLToPath } from 'node:url'; import fs from 'fs-extra'; import svgtofont from '../../lib/index.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const pkg = fs.readJSONSync(path.resolve(__dirname, "../../package.json")); console.log(pkg.name); // 输出: example console.log(pkg.version); // 输出: 1.0.0 const rootPath = path.resolve(process.cwd(), "examples", "example"); svgtofont({ src: path.resolve(rootPath, "svg"), // svg path dist: path.resolve(rootPath, "dist"), // output path // emptyDist: true, // Clear output directory contents fontName: "svgtofont", // font name css: true, // Create CSS files. outSVGReact: true, outSVGReactNative: true, outSVGPath: true, outSVGVue: true, startNumber: 20000, // unicode start number svgicons2svgfont: { fontHeight: 1000, normalize: true }, typescript: true, // website = null, no demo html files website: { // Add a Github corner to your website // Like: https://github.com/uiwjs/react-github-corners corners: { url: 'https://github.com/jaywcjlove/svgtofont', width: 62, // default: 60 height: 62, // default: 60 bgColor: '#dc3545' // default: '#151513' }, index: "unicode", // Enum{"font-class", "unicode", "symbol"} title: "svgtofont", favicon: path.resolve(rootPath, "favicon.png"), // Must be a .svg format image. Missing here to ensure the example works without it. // logo: path.resolve(rootPath, "svg", "git.svg"), version: pkg.version, meta: { description: "Converts SVG fonts to TTF/EOT/WOFF/WOFF2/SVG format.", keywords: "svgtofont,TTF,EOT,WOFF,WOFF2,SVG" }, description: ``, links: [ { title: "GitHub", url: "https://github.com/jaywcjlove/svgtofont" }, { title: "Feedback", url: "https://github.com/jaywcjlove/svgtofont/issues" }, { title: "Font Class Demo", url: "font-class.html" }, { title: "Symbol Demo", url: "symbol.html" }, { title: "Unicode Demo", url: "index.html" } ], footerInfo: `Licensed under MIT. (Yes it's free and open-sourced)` } }) .then(() => { console.log("Example::::done!"); }); jaywcjlove-svgtofont-2472abc/examples/example/simple.mjs000066400000000000000000000006731522075462600236200ustar00rootroot00000000000000import svgtofont from '../../lib/index.js'; import path from 'path'; const rootPath = path.resolve(process.cwd(), "examples", "example"); svgtofont({ src: path.resolve(rootPath, "svg"), // svg path dist: path.resolve(rootPath, "example"), // output path fontName: "svgtofont", // font name css: true, // Create CSS files. startNumber: 20000, // unicode start number emptyDist: true, }).then(() => { console.log("done!!!!"); });jaywcjlove-svgtofont-2472abc/examples/example/svg/000077500000000000000000000000001522075462600224055ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/example/svg/adobe.svg000066400000000000000000000006751522075462600242100ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example/svg/demo.svg000066400000000000000000000005341522075462600240540ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example/svg/git.svg000066400000000000000000000011571522075462600237150ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example/svg/left.svg000066400000000000000000000011321522075462600240550ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/example/svg/stylelint.svg000066400000000000000000000046641522075462600251670ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/multicolor/000077500000000000000000000000001522075462600223445ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/multicolor/index.mjs000066400000000000000000000016261522075462600241730ustar00rootroot00000000000000import svgtofont from '../../lib/index.js'; import path from 'path'; const rootPath = path.resolve(process.cwd(), "examples", "multicolor"); svgtofont({ src: path.resolve(rootPath, "svg"), // svg path dist: path.resolve(rootPath, "dist"), // output path fontName: "multicolor-font", // font name css: true, // Create CSS files. multicolor: true, // Enable multicolor icon support emptyDist: true, svgicons2svgfont: { fontHeight: 1000, normalize: true, }, website: { index: "font-class", title: "Multicolor Icons Demo", description: "Demonstration of multicolor icon font support", links: [ { title: "Font Class Demo", url: "index.html" }, { title: "Unicode Demo", url: "unicode.html" }, { title: "Symbol Demo", url: "symbol.html" } ], } }).then(() => { console.log("done!!!!"); }); jaywcjlove-svgtofont-2472abc/examples/multicolor/svg/000077500000000000000000000000001522075462600231435ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/multicolor/svg/check.svg000066400000000000000000000002521522075462600247400ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/multicolor/svg/heart.svg000066400000000000000000000006441522075462600247730ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/multicolor/svg/star.svg000066400000000000000000000004501522075462600246340ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/multicolor/svg/stoplight.svg000066400000000000000000000005551522075462600257060ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/templates/000077500000000000000000000000001522075462600221515ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/templates/.gitignore000066400000000000000000000000051522075462600241340ustar00rootroot00000000000000dist2jaywcjlove-svgtofont-2472abc/examples/templates/.svgtofontrc.js000066400000000000000000000001561522075462600251450ustar00rootroot00000000000000import path from "path"; export default { "dist": path.resolve(process.cwd(), "examples/templates/dist2") }jaywcjlove-svgtofont-2472abc/examples/templates/favicon.png000066400000000000000000000026221522075462600243060ustar00rootroot00000000000000‰PNG  IHDR00Wù‡sRGB®ÎéLIDATh½ZMˆE~¯f& ó7Ó³F0A ‹!z0w QvvW=äàÙ”€è%ñ7lð?õ¢'½ „€Ù ¨$ñ xYQÐ,âI“ž™�˜(î³ÓU¾×“Zzzªzú§&uØêzïÕ«ïÛ×õªë1#hK³ÞAs àÁÐ=ÂB©s›Oø?º^];ô§ê/ È£  õ�€«ˆê`µÙú */ú,Š:ˆÎo7j/È÷âàÙF�ªHï·¦½ƒÑ9EŸ�EàŸoãr  üæa Pà¡ÚIÿè0»4zg¸eÞÿ*• Dè [XIõ®«H8‹€í7îÜ�píŒRPÕ2[ï"…"Ð~rÛÀëâ5ÏýRÈ‘hÛ€k¹‹HäŽ@«Q�6æs¢,&«_\øYƒÒ}§±õ. Á²+A¶éóì‰Ôlà5&H¸[�m=�ÈßÚt,ÏJ"�ð„�½6&ê?“¼O¦Ôú¾±a�…ÄP ž>Þ4¬3 ¢,óð€0&@(Ÿ¥(´bâ�aZ‰›¸=]{IJxkÀ»EÀ›ù&¬Ü{[óüe‹I(¾4ëÝÓíÂ7”†kIv¬¶±­È žã}pV�s¶á±­m:áÿZ.Ä‹H ´§¼³üç£@é5zde¥wFDåñgW$„àA½_0˘^�¾»ÏUûÆ×Å}¸ ÑG ÕðÉ‚àã õ¸µ|eniª¾S�u_”į”zG;vÞ£ÜÐEuª3;vÜ7“ÀRew q7¢˜¦�{$<Ýã†4Žg§�¥ÊçG ^QjCÈÓíÇÇîÓ"ÝWçÏý¶¥éWk^lÒegn} Çñ”ÖG{&Á˜Y&ø®:¹E±>+µQ­§[3[Ç­6¤à ÒÍž²F‚03vtáßW“œ¹ÖQºÝôíÔyâŽmI¾Ã[â‡&ÆÌ•~…v™ F-ã3ƒ`èAB,X±PÙfm[�F¨ ³&ñÀK³´ ‰�aElPõ _I>¤´ÛvÁ3.:%ù•N*|Æt¯Öëq©†.?ô8Ú3fÆ.¸Üdz¨ò†=sFºvõ3 ÿ¯„c¼WLx3c�|.÷Q�fçW“ñ(etþL‰E:H?¢lAѧЫEE²gi]3x*ŒUOúa‰rí›…+eD"<銦¼M˜Ä>HF5SúÖã:@¬q$«€þr»ÞõýãŸ×θbV$qµéÒ¿loÔnxOë�Ø4'*‹ƒgÝæ&�ø•×ôc¶æ7¼/)F�Úô6¹ <ÛÏ~�x‚Í™].>µë´&��¶íõ6ð¬5`Ee!ç¹I-�Mt~x¶³`eVA€Ûy^RKc£çÏv‰Ø …jÏIjilx~ð¡]ÒbQ]š�Í)”²Ð^ú®7~ÇwÞÊB_Sid±´àyÊÐh¿i"ÁÀè:Þžñöëyºgë\‚g߯4ª5õi"ÎC¼Hý÷×}ì²t£kdùÏëy™ ðÄÔ$ô*)ú<àÙm.<Ñ%‰¼à pE¢øÂŠ’( Þ �¼$\€wF + Wà�HKÂ%xçØa¯Ê‡ôc�þZßaGñc�Üi”ÁÚÚ�ü¹Íÿº~FÅK‚IEND®B`‚jaywcjlove-svgtofont-2472abc/examples/templates/index.mjs000066400000000000000000000045741522075462600240050ustar00rootroot00000000000000import path from 'path'; import fs from 'fs-extra'; import { fileURLToPath } from 'url'; import svgtofont from '../../lib/index.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const pkg = fs.readJSONSync(path.resolve(__dirname, "../../package.json")); const rootPath = path.resolve(process.cwd(), "examples", "templates"); /** * @type {import('../../lib/index.js').SvgToFontOptions} */ const options = { config: { cwd: rootPath, }, src: path.resolve(rootPath, "svg"), // svg path dist: path.resolve(rootPath, "dist"), // output path // emptyDist: true, // Clear output directory contents styleTemplates: path.resolve(rootPath, "styles"), fontName: "svgtofont", // font name css: true, // Create CSS files. outSVGReact: true, outSVGReactNative: true, outSVGPath: true, outSVGVue: true, startNumber: 20000, // unicode start number svgicons2svgfont: { fontHeight: 1000, normalize: true }, useCSSVars: true, // website = null, no demo html files website: { // Add a Github corner to your website // Like: https://github.com/uiwjs/react-github-corners corners: { url: 'https://github.com/jaywcjlove/svgtofont', width: 62, // default: 60 height: 62, // default: 60 bgColor: '#dc3545' // default: '#151513' }, index: "unicode", // Enum{"font-class", "unicode", "symbol"} title: "svgtofont", favicon: path.resolve(rootPath, "favicon.png"), // Must be a .svg format image. logo: path.resolve(rootPath, "svg", "git.svg"), version: pkg.version, meta: { description: "Converts SVG fonts to TTF/EOT/WOFF/WOFF2/SVG format.", keywords: "svgtofont,TTF,EOT,WOFF,WOFF2,SVG" }, description: ``, links: [ { title: "GitHub", url: "https://github.com/jaywcjlove/svgtofont" }, { title: "Feedback", url: "https://github.com/jaywcjlove/svgtofont/issues" }, { title: "Font Class Demo", url: "font-class.html" }, { title: "Symbol Demo", url: "symbol.html" }, { title: "Unicode Demo", url: "index.html" } ], footerInfo: `Licensed under MIT. (Yes it's free and open-sourced)` } } svgtofont(options).then(() => { console.log("done!"); }); jaywcjlove-svgtofont-2472abc/examples/templates/simple.mjs000066400000000000000000000007371522075462600241640ustar00rootroot00000000000000import path from 'path'; import svgtofont from '../../lib/index.js'; const rootPath = path.resolve(process.cwd(), "examples", "templates"); svgtofont({ config: { cwd: rootPath, }, src: path.resolve(rootPath, "svg"), // svg path dist: path.resolve(rootPath, "dist3"), // output path fontName: "svgtofont", // font name css: true, // Create CSS files. startNumber: 20000, // unicode start number emptyDist: true, }).then(() => { console.log("done!!!!"); });jaywcjlove-svgtofont-2472abc/examples/templates/styles/000077500000000000000000000000001522075462600234745ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/templates/styles/_{{filename}}.css.template000066400000000000000000000014701522075462600307210ustar00rootroot00000000000000/* Hello CSS! */ @font-face { font-family: "{{fontname}}"; src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } [class^="{{prefix}}-"], [class*=" {{prefix}}-"] { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {{cssString}} jaywcjlove-svgtofont-2472abc/examples/templates/styles/_{{filename}}.less.template000066400000000000000000000014431522075462600310770ustar00rootroot00000000000000@font-face {font-family: "{{fontname}}"; src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } [class^="{{prefix}}-"], [class*=" {{prefix}}-"] { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {{cssString}}jaywcjlove-svgtofont-2472abc/examples/templates/styles/_{{filename}}.module.less.template000066400000000000000000000014571522075462600323700ustar00rootroot00000000000000@font-face {font-family: "{{fontname}}"; src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } [class^="{{prefix}}-"], [class*=" {{prefix}}-"] { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } :global { {{cssString}} }jaywcjlove-svgtofont-2472abc/examples/templates/styles/_{{filename}}.scss.template000066400000000000000000000014621522075462600311050ustar00rootroot00000000000000@font-face {font-family: "{{fontname}}"; src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } [class^="{{prefix}}-"], [class*=" {{prefix}}-"] { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {{cssString}} {{cssToVars}} jaywcjlove-svgtofont-2472abc/examples/templates/styles/_{{filename}}.styl.template000066400000000000000000000014431522075462600311240ustar00rootroot00000000000000@font-face {font-family: "{{fontname}}"; src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } [class^="{{prefix}}-"], [class*=" {{prefix}}-"] { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {{cssString}}jaywcjlove-svgtofont-2472abc/examples/templates/svg/000077500000000000000000000000001522075462600227505ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/examples/templates/svg/adobe.svg000066400000000000000000000006751522075462600245530ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/templates/svg/git.svg000066400000000000000000000011571522075462600242600ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/examples/templates/svg/stylelint.svg000066400000000000000000000046641522075462600255320ustar00rootroot00000000000000 jaywcjlove-svgtofont-2472abc/package.json000066400000000000000000000051421522075462600206250ustar00rootroot00000000000000{ "name": "svgtofont", "version": "6.5.3", "description": "Converts SVG to TTF/EOT/WOFF/WOFF2/SVG format fonts.", "homepage": "https://jaywcjlove.github.io/svgtofont/", "funding": "https://jaywcjlove.github.io/#/sponsor", "main": "./lib/index.js", "typings": "./lib/index.d.ts", "exports": { ".": { "default": "./lib/index.js", "types": "./lib/index.d.ts" }, "./lib/utils": { "default": "./lib/utils.js", "types": "./lib/utils.d.ts" } }, "type": "module", "bin": { "svgtofont": "lib/cli.js" }, "scripts": { "prepare": "npm run build", "start": "node lib/index.js", "watch": "tsbb watch src/*.ts", "build": "tsbb build src/*.ts", "example": "node examples/example/index.mjs", "example:cli": "node lib/cli.js --sources ./examples/example/svg --output ./dist --fontName uiw-font", "example:simple": "node examples/example/simple.mjs", "example:templates": "node examples/templates/index.mjs", "pretest": "npm run example && npm run example:simple && npm run example:templates", "checked": "tsc --noEmit", "test": "tsbb test", "coverage": "tsbb test --coverage" }, "author": "Kenny ", "repository": { "type": "git", "url": "git+https://github.com/jaywcjlove/svgtofont.git" }, "svgtofont": { "css": { "fontSize": false } }, "keywords": [ "webfont", "font", "icon", "iconfont", "font-face", "compress", "minify", "font-cli", "ttf", "woff", "eot", "svg", "ttf2eot", "ttf2woff", "ttf2svg", "svg2ttf", "css", "base64" ], "license": "MIT", "files": [ "lib", "src" ], "engines": { "node": ">=18.0.0" }, "jest": { "transformIgnorePatterns": [ "/node_modules/?!(.*)" ], "moduleNameMapper": { "^(\\.{1,2}/.*)\\.js$": "$1" } }, "dependencies": { "auto-config-loader": "^2.0.0", "cheerio": "~1.0.0", "colors-cli": "~1.0.28", "fs-extra": "~11.2.0", "image2uri": "^2.1.2", "nunjucks": "^3.2.4", "svg2ttf": "~6.1.0", "svgicons2svgfont": "~15.0.0", "svgo": "~3.3.0", "ttf2eot": "~3.1.0", "ttf2woff": "~3.0.0", "ttf2woff2": "~8.0.0", "yargs": "^17.7.2" }, "devDependencies": { "@types/fs-extra": "^11.0.1", "@types/nunjucks": "^3.2.6", "@types/svg2ttf": "~5.0.1", "@types/ttf2eot": "~2.0.0", "@types/ttf2woff": "~2.0.2", "tsbb": "^4.4.0" }, "peerDependencies": { "@types/svg2ttf": "~5.0.1" }, "peerDependenciesMeta": { "@types/svg2ttf": { "optional": true } } } jaywcjlove-svgtofont-2472abc/renovate.json000066400000000000000000000003261522075462600210540ustar00rootroot00000000000000{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" ], "packageRules": [ { "matchPackagePatterns": ["*"], "rangeStrategy": "replace" } ] } jaywcjlove-svgtofont-2472abc/src/000077500000000000000000000000001522075462600171245ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/src/cli.ts000066400000000000000000000032251522075462600202450ustar00rootroot00000000000000#!/usr/bin/env node import FS from 'fs-extra'; import { Arguments } from 'yargs'; import yargs from 'yargs' import { hideBin } from 'yargs/helpers' import path from 'path'; import svgtofont from './index.js'; import { log } from './log.js'; type ArgvResult = Arguments<{ sources: string; output: string; fontName: string; multicolor: boolean; }> const argv = yargs(hideBin(process.argv)) .alias('s', 'sources') .describe('s', 'The root from which all sources are relative.') .alias('o', 'output') .describe('o', 'Output directory.') .alias('f', 'fontName') .describe('f', 'Font Name.') .describe('multicolor', 'Enable multicolor icon support.') .boolean('multicolor') .default('multicolor', false) .demandOption(['output', 'sources']) .help('h') .alias('h', 'help') .epilog('copyright 2019') .argv as ArgvResult; const sourcesPath = path.resolve(process.cwd(), argv.sources); const outputPath = path.resolve(process.cwd(), argv.output); if (!FS.pathExistsSync(sourcesPath)) { log.error('The directory does not exist!', sourcesPath); process.exit(); } if (!FS.pathExistsSync(outputPath)) { FS.mkdirpSync(outputPath); } svgtofont({ src: sourcesPath, // svg path dist: outputPath, // output path // emptyDist: true, // Clear output directory contents fontName: (argv.fontName) || "svgfont", // font name css: true, // Create CSS files. outSVGReact: true, outSVGReactNative: false, outSVGVue: true, outSVGPath: true, multicolor: argv.multicolor || false, svgicons2svgfont: { fontHeight: 1000, normalize: true, }, }) .then(() => { log.log('done!'); }).catch((err) => { log.log('SvgToFont:ERR:', err); }); jaywcjlove-svgtofont-2472abc/src/generate.ts000066400000000000000000000222131522075462600212660ustar00rootroot00000000000000import fs from 'fs-extra'; import path from 'path'; import { optimize } from 'svgo'; import { filterSvgFiles, toPascalCase } from './utils.js'; import { type SvgToFontOptions } from './'; /** * Generate Icon SVG Path Source * .json */ export async function generateIconsSource(options: SvgToFontOptions = {}){ const ICONS_PATH = filterSvgFiles(options.src) const data = await buildPathsObject(ICONS_PATH, options); const outPath = path.join(options.dist, `${options.fontName}.json`); await fs.outputFile(outPath, `{${data}\n}`); return outPath; } /** * Loads SVG file for each icon, extracts path strings `d="path-string"`, * and constructs map of icon name to array of path strings. * @param {array} files */ async function buildPathsObject(files: string[], options: SvgToFontOptions = {}) { const svgoOptions = options.svgoOptions || {}; return Promise.all( files.map(async filepath => { const name = path.basename(filepath, '.svg'); const svg = fs.readFileSync(filepath, 'utf-8'); const pathStrings = optimize(svg, { path: filepath, ...options, plugins: [ 'convertTransform', ...(svgoOptions.plugins || []) // 'convertShapeToPath' ], }); const str: string[] = (pathStrings.data.match(/ d="[^"]+"/g) || []).map(s => s.slice(3)); return `\n"${name}": [${str.join(',\n')}]`; }), ); } const reactSource = (name: string, size: string, fontName: string, source: string) => `import React from 'react'; export const ${name} = props => ( ${source} ); `; const reactTypeSource = (name: string) => `import React from 'react'; export declare const ${name}: (props: React.SVGProps) => JSX.Element; `; /** * Generate React Icon * .json */ export async function generateReactIcons(options: SvgToFontOptions = {}) { const ICONS_PATH = filterSvgFiles(options.src); const data = await outputReactFile(ICONS_PATH, options); const outPath = path.join(options.dist, 'react', 'index.js'); fs.outputFileSync(outPath, data.join('\n')); fs.outputFileSync(outPath.replace(/\.js$/, '.d.ts'), data.join('\n')); return outPath; } async function outputReactFile(files: string[], options: SvgToFontOptions = {}) { const svgoOptions = options.svgoOptions || {}; const fontSizeOpt = typeof options.css !== 'boolean' && options.css.fontSize const fontSize = typeof fontSizeOpt === 'boolean' ? (fontSizeOpt === true ? '16px' : '') : fontSizeOpt; const fontName = options.classNamePrefix || options.fontName return Promise.all( files.map(async filepath => { let name = toPascalCase(path.basename(filepath, '.svg')); if (/^[rR]eact$/.test(name)) { name = name + toPascalCase(fontName); } const svg = fs.readFileSync(filepath, 'utf-8'); const pathData = optimize(svg, { path: filepath, ...svgoOptions, plugins: [ 'removeXMLNS', 'removeEmptyAttrs', 'convertTransform', // 'convertShapeToPath', // 'removeViewBox' ...(svgoOptions.plugins || []) ] }); const str: string[] = (pathData.data.match(/ d="[^"]+"/g) || []).map(s => s.slice(3)); const outDistPath = path.join(options.dist, 'react', `${name}.js`); const pathStrings = str.map((d, i) => ``); const comName = isNaN(Number(name.charAt(0))) ? name : toPascalCase(fontName) + name; fs.outputFileSync(outDistPath, reactSource(comName, fontSize, fontName, pathStrings.join(',\n'))); fs.outputFileSync(outDistPath.replace(/\.js$/, '.d.ts'), reactTypeSource(comName)); return `export * from './${name}';`; }), ); } const reactNativeSource = (fontName: string, defaultSize: number, iconMap: Map) => `import { Text } from 'react-native'; const icons = ${JSON.stringify(Object.fromEntries(iconMap))}; export const ${fontName} = ({iconName, ...rest}) => { return ( {icons[iconName]} ); }; `; const reactNativeTypeSource = (name: string, iconMap: Map) => `import { TextStyle } from 'react-native'; export type ${name}IconNames = ${[...iconMap.keys()].reduce((acc, key, index) => { if (index === 0) { acc = `'${key}'` } else { acc += ` | '${key}'` } return acc; }, `${'string'}`)} export interface ${name}Props extends Omit { iconName: ${name}IconNames } export declare const ${name}: (props: ${name}Props) => JSX.Element; `; /** * Generate ReactNative Icon * .json */ export function generateReactNativeIcons(options: SvgToFontOptions = {}, unicodeObject: Record) { const ICONS_PATH = filterSvgFiles(options.src); outputReactNativeFile(ICONS_PATH, options, unicodeObject); } function outputReactNativeFile(files: string[], options: SvgToFontOptions = {}, unicodeObject: Record) { const fontSizeOpt = typeof options.css !== 'boolean' && options.css.fontSize; const fontSize = typeof fontSizeOpt === 'boolean' ? 16 : parseInt(fontSizeOpt); const fontName = options.classNamePrefix || options.fontName const iconMap = new Map(); files.map(filepath => { const baseFileName = path.basename(filepath, '.svg'); iconMap.set(baseFileName, unicodeObject[baseFileName]) }); const outDistPath = path.join(options.dist, 'reactNative', `${fontName}.jsx`); const comName = isNaN(Number(fontName.charAt(0))) ? fontName : toPascalCase(fontName) + name; fs.outputFileSync(outDistPath, reactNativeSource(comName, fontSize, iconMap)); fs.outputFileSync(outDistPath.replace(/\.jsx$/, '.d.ts'), reactNativeTypeSource(comName, iconMap)); } /** * Generate Vue Icon * .json */ export async function generateVueIcons(options: SvgToFontOptions = {}) { const ICONS_PATH = filterSvgFiles(options.src); const data = await outputVueFile(ICONS_PATH, options); const outPath = path.join(options.dist, 'vue', 'index.js'); fs.outputFileSync(outPath, data.join('\n')); fs.outputFileSync(outPath.replace(/\.js$/, '.d.ts'), data.join('\n')); return outPath; } async function outputVueFile(files: string[], options: SvgToFontOptions = {}) { const svgoOptions = options.svgoOptions || {}; const fontSizeOpt = typeof options.css !== 'boolean' && options.css.fontSize const fontSize = typeof fontSizeOpt === 'boolean' ? (fontSizeOpt === true ? '16px' : '') : fontSizeOpt; const fontName = options.classNamePrefix || options.fontName return Promise.all( files.map(async filepath => { let name = toPascalCase(path.basename(filepath, '.svg')); if (/^[vV]ue$/.test(name)) { name = name + toPascalCase(fontName); } const svg = fs.readFileSync(filepath, 'utf-8'); const pathData = optimize(svg, { path: filepath, ...svgoOptions, plugins: [ 'removeXMLNS', 'removeEmptyAttrs', 'convertTransform', // 'convertShapeToPath', // 'removeViewBox' ...(svgoOptions.plugins || []) ] }); const str: string[] = (pathData.data.match(/ d="[^"]+"/g) || []).map(s => s.slice(3)); const outDistPath = path.join(options.dist, 'vue', `${name}.js`); const pathStrings = str.map((d, i) => ``); const comName = isNaN(Number(name.charAt(0))) ? name : toPascalCase(fontName) + name; fs.outputFileSync(outDistPath, vueSource(comName, fontSize, fontName, pathStrings.join(',\n'))); fs.outputFileSync(outDistPath.replace(/\.js$/, '.d.ts'), vueTypeSource(comName)); return `export * from './${name}';`; }), ); } const vueSource = (name: string, size: string, fontName: string, source: string) => `import { defineComponent, h } from 'vue'; export const ${name} = defineComponent({ name: '${name}', props: { class: { type: String, default: '' } }, setup(props, { attrs }) { return () => h( 'svg', { viewBox: '0 0 20 20', ${size ? `width: '${size}', height: '${size}',` : ''} class: \`${fontName} \${props.class}\`, ...attrs }, [ ${source .split('\n') .filter(Boolean) .map(path => { const attrPairs = []; const attrRegex = /([a-zA-Z\-:]+)=("[^"]*"|'[^']*'|[^\s"']+)/g; let match; const pathContent = path.replace(/^$/g, ''); while ((match = attrRegex.exec(pathContent)) !== null) { const key = match[1]; const value = match[2]; attrPairs.push(`"${key}": ${value}`); } return `h('path', {${attrPairs.join(', ')}})`; }) .join(',\n ') } ] ); } }); `; const vueTypeSource = (name: string) => `import type { DefineComponent } from 'vue'; declare const ${name}: DefineComponent>; export { ${name} }; `; jaywcjlove-svgtofont-2472abc/src/index.ts000066400000000000000000000447331522075462600206160ustar00rootroot00000000000000import path from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs-extra'; import image2uri from 'image2uri'; import { type SVGIcons2SVGFontStreamOptions } from 'svgicons2svgfont'; import color from 'colors-cli'; import { autoConf, merge, type AutoConfOption } from 'auto-config-loader'; import type { FontOptions } from 'svg2ttf'; import type { Config } from 'svgo'; import { log } from './log.js'; import { generateIconsSource, generateReactIcons, generateReactNativeIcons, generateVueIcons } from './generate.js'; import { createSVG, createTTF, createEOT, createWOFF, createWOFF2, createSvgSymbol, copyTemplate, type CSSOptions, createHTML, createTypescript, type TypescriptOptions } from './utils.js'; import { generateFontFaceCSS, getDefaultOptions } from './utils.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export type SvgToFontOptions = { /** Support for .svgtofontrc and more configuration files. */ config?: AutoConfOption /** A value of `false` disables logging */ log?: boolean; /** log callback function */ logger?: (message: string) => void; /** * The output directory. * @default fonts * @example * ``` * path.join(process.cwd(), 'fonts') * ``` */ dist?: string; /** * svg path * @default svg * @example * ``` * path.join(process.cwd(), 'svg') * ``` */ src?: string; /** * The font family name you want. * @default iconfont */ fontName?: string; /** * Create CSS/LESS/Scss/Styl files, default `true`. */ css?: boolean | CSSOptions; /** * Output `./dist/react/`, SVG generates `react` components. */ outSVGReact?: boolean; /** * Output `./dist/reactNative/`, SVG generates `reactNative` component. */ outSVGReactNative?: boolean; /** * Output `./dist/vue/`, SVG generates `vue` components. */ outSVGVue?: boolean; /** * Output `./dist/svgtofont.json`, The content is as follows: * @example * ```js * { * "adobe": ["M14.868 3H23v19L14.868 3zM1 3h8.8.447z...."], * "git": ["M2.6 10.59L8.38 4.8l1.69 1.7c-.24c-.6.34-1 .99-1..."], * "stylelint": ["M129.74 243.648c28-100.5.816c2.65..."] * } * ``` */ outSVGPath?: boolean; /** * Output `./dist/info.json`, The content is as follows: * @example * ```js * { * "adobe": { * "encodedCode": "\\ea01", * "prefix": "svgtofont", * "className": "svgtofont-adobe", * "unicode": "" * }, * ..... * } * ``` */ generateInfoData?: boolean; /** * This is the setting for [svgicons2svgfont](https://github.com/nfroidure/svgicons2svgfont/tree/dd713bea4f97afa59f7dba6a21ff7f22db565bcf#api) */ svgicons2svgfont?: Partial; /** Some options can be configured with svgoOptions though it. [svgo](https://github.com/svg/svgo#configuration) */ svgoOptions?: Config; /** * Create font class name prefix, default value font name. * @default fontName */ classNamePrefix?: SvgToFontOptions['fontName']; /** * Symbol Name Delimiter, @default `-` */ symbolNameDelimiter?: string; /** * Directory of custom templates. */ styleTemplates?: string; /** * unicode start number * @default 10000 */ startUnicode?: number; /** Get Icon Unicode */ getIconUnicode?: (name: string, unicode: string, startUnicode: number) => [string, number]; /** * should the name(file name) be used as unicode? this switch allows for the support of ligatures. * @default false */ useNameAsUnicode?: boolean; /** * adds possibility to use name (file name) in addition to codepoints. adds support of ligatures. * @default false */ addLigatures?: boolean /** * consoles whenever {{ cssString }} template outputs unicode characters or css vars * @default false */ useCSSVars?: boolean; /** * Clear output directory contents * @default false */ emptyDist?: boolean; /** * This is the setting for [svg2ttf](https://github.com/fontello/svg2ttf/tree/c33a126920f46b030e8ce960cc7a0e38a6946bbc#svg2ttfsvgfontstring-options---buf) */ svg2ttf?: FontOptions; /** * You can configure which font files to exclude from generation. By default, all font files will be generated. * https://github.com/jaywcjlove/svgtofont/issues/238 */ excludeFormat?: Array<"eot" | "woff" | "woff2" | "ttf" | "svg" | "symbol.svg">; website?: { /** * Add a Github corner to your website * @like https://github.com/uiwjs/react-github-corners */ corners?: { /** * @example `https://github.com/jaywcjlove/svgtofont` */ url?: string, /** * @default 60 */ width?: number, /** * @default 60 */ height?: number, /** * @default #151513 */ bgColor?: '#dc3545' }, /** * @default unicode */ index?: 'font-class' | 'unicode' | 'symbol'; /** * website title */ title?: string; /** * @example * ```js * path.resolve(rootPath, "favicon.png") * ``` */ favicon?: string; /** * Must be a .svg format image. * @example * ```js * path.resolve(rootPath, "svg", "git.svg") * ``` */ logo?: string, version?: string, meta?: { description?: string; keywords?: string; }, description?: string; template?: string; footerInfo?: string; links: Array<{ title: string; url: string; }>; }; /** * Create typescript file with declarations for icon classnames * @default false */ typescript?: boolean | TypescriptOptions /** * Enable multicolor icon support. When true, SVGs with multiple * fill colors are automatically split into separate layers in the font, * with CSS that overlays them using IcoMoon-style span elements. * @default false */ multicolor?: boolean; } export type IconInfo = { prefix: string; symbol: string; unicode: string; className: string; encodedCode: string | number; multicolor?: boolean; layers?: Array<{ path: string; className: string; encodedCode: string; unicode: string; color: string; }>; } export type InfoData = Record> const loadConfig = async (options: SvgToFontOptions): Promise => { const defaultOptions = getDefaultOptions(options); const data = await autoConf('svgtofont', { mustExist: true, default: defaultOptions, ...options.config, }); return merge(defaultOptions, data); }; const handlePkgConfig = (options: SvgToFontOptions): SvgToFontOptions => { const pkgPath = path.join(process.cwd(), 'package.json'); if (fs.pathExistsSync(pkgPath)) { const pkg = fs.readJSONSync(pkgPath); if (pkg.svgtofont) { const cssOptions = options.css; options = merge(options, pkg.svgtofont); if (pkg.svgtofont.css && cssOptions && typeof cssOptions === 'object') { options.css = merge(cssOptions, pkg.svgtofont.css); } } if (options.website && pkg.version) { options.website.version = options.website.version ?? pkg.version; } } return options; }; export default async (options: SvgToFontOptions = {}) => { options = await loadConfig(options); options = handlePkgConfig(options); if (options.log === undefined) options.log = true; log.disabled = !options.log; if (options.logger && typeof options.logger === 'function') log.logger = options.logger; options.svgicons2svgfont.fontName = options.fontName; options.classNamePrefix = options.classNamePrefix || options.fontName; const excludeFormat = options.excludeFormat || []; const fontSizeOpt = typeof options.css !== 'boolean' && options.css.fontSize; const fontSize = typeof fontSizeOpt === 'boolean' ? (fontSizeOpt === true ? 'font-size: 16px;' : '') : `font-size: ${fontSizeOpt};`; // If you generate a font you need to generate a style. if (options.website && !options.css) options.css = true; const infoDataPath = path.resolve(options.dist, 'info.json'); try { if (options.emptyDist) { await fs.emptyDir(options.dist); } // Ensures that the directory exists. await fs.ensureDir(options.dist); const { unicodeObject, multicolorMap } = await createSVG(options); // Set of multicolor layer glyph names to skip in standard CSS loop const multicolorGlyphNames = new Set(); if (options.multicolor) { for (const duo of Object.values(multicolorMap)) { for (const layer of duo.layers) { multicolorGlyphNames.add(layer.glyphName); } } } /** @deprecated */ let cssToVars: string[] = []; let cssString: string[] = []; let cssRootVars: string[] = []; let cssIconHtml: string[] = []; let unicodeHtml: string[] = []; let symbolHtml: string[] = []; const prefix = options.classNamePrefix || options.fontName; const infoData: InfoData = {} Object.keys(unicodeObject).forEach((name, index, self) => { // Skip multicolor layer glyphs from standard CSS generation if (multicolorGlyphNames.has(name)) return; if (!infoData[name]) infoData[name] = {}; const _code = unicodeObject[name]; let symbolName = options.classNamePrefix + options.symbolNameDelimiter + name let iconPart = symbolName + '">'; let encodedCodes: string | number = _code.codePointAt(0); if (options.useNameAsUnicode) { symbolName = name; iconPart = prefix + '">' + name; encodedCodes = [..._code].map(x => x.codePointAt(0)).join(';&#'); } else { cssToVars.push(`$${symbolName}: "\\${encodedCodes.toString(16)}";\n`); if (options.useCSSVars) { if (index === 0) cssRootVars.push(`:root {\n`) cssRootVars.push(`--${symbolName}: "\\${encodedCodes.toString(16)}";\n`); cssString.push(`.${symbolName}::before { content: var(--${symbolName}); }\n`); if (index === self.length - 1) cssRootVars.push(`}\n`) } else { cssString.push(`.${symbolName}::before { content: "\\${encodedCodes.toString(16)}"; }\n`); } } infoData[name].encodedCode = `\\${encodedCodes.toString(16)}`; infoData[name].prefix = prefix; infoData[name].className = symbolName; infoData[name].unicode = `&#${encodedCodes};`; cssIconHtml.push(`
  • ${name}

  • `); unicodeHtml.push(`
  • ${_code}

    ${name}

    &#${encodedCodes};
  • `); symbolHtml.push(`
  • ${symbolName}

  • `); }); // Generate multicolor CSS and HTML if (options.multicolor) { for (const [iconName, mcInfo] of Object.entries(multicolorMap)) { const symbolName = prefix + options.symbolNameDelimiter + iconName; // Base positioning class for the multicolor icon cssString.push(`.${symbolName} { display: inline-block; position: relative; }\n`); // infoData for the original icon name if (!infoData[iconName]) infoData[iconName] = {}; infoData[iconName].prefix = prefix; infoData[iconName].className = symbolName; infoData[iconName].multicolor = true; infoData[iconName].layers = []; infoData[iconName].encodedCode = `\\${mcInfo.layers[0].encodedCode.toString(16)}`; infoData[iconName].unicode = `&#${mcInfo.layers[0].encodedCode};`; mcInfo.layers.forEach((layer, idx) => { const pathClass = `path${idx + 1}`; const hex = layer.encodedCode.toString(16); if (idx === 0) { // First layer: establishes the size, no overlay cssString.push(`.${symbolName} .${pathClass}::before { content: "\\${hex}"; color: ${layer.color}; }\n`); } else { // Subsequent layers: overlay with margin-left: -1em cssString.push(`.${symbolName} .${pathClass}::before { content: "\\${hex}"; color: ${layer.color}; margin-left: -1em; }\n`); } infoData[iconName].layers!.push({ path: pathClass, className: `${symbolName} ${pathClass}`, encodedCode: `\\${hex}`, unicode: `&#${layer.encodedCode};`, color: layer.color, }); }); // HTML for font-class preview const spans = mcInfo.layers.map((_, i) => `` ).join(''); cssIconHtml.push( `
  • ${spans}

    ${iconName}

  • ` ); // HTML for unicode preview (show each layer separately) mcInfo.layers.forEach((layer, idx) => { unicodeHtml.push( `
  • ${layer.unicode}

    ${iconName}-path${idx + 1}

    &#${layer.encodedCode};
  • ` ); }); } } if (options.useCSSVars) { cssString = [...cssRootVars, ...cssString] } if (options.generateInfoData) { await fs.writeJSON(infoDataPath, infoData, { spaces: 2 }); log.log(`${color.green('SUCCESS')} Created ${infoDataPath} `); } const ttf = await createTTF(options); if (!excludeFormat.includes('eot')) await createEOT(options, ttf); if (!excludeFormat.includes('woff')) await createWOFF(options, ttf); if (!excludeFormat.includes('woff2')) await createWOFF2(options, ttf); if (!excludeFormat.includes('symbol.svg')) await createSvgSymbol(options); const ttfPath = path.join(options.dist, options.fontName + ".ttf"); if (excludeFormat.includes('ttf')) { fs.removeSync(ttfPath); } const svgPath = path.join(options.dist, options.fontName + ".svg"); if (excludeFormat.includes('svg')) { fs.removeSync(svgPath) } if (options.css) { const styleTemplatePath = options.styleTemplates || path.resolve(__dirname, 'styles') const outDir = typeof options.css === 'object' ? options.css.output || options.dist : options.dist; const hasTimestamp = typeof options.css === 'object' ? options.css.hasTimestamp : true; const cssOptions = typeof options.css === 'object' ? options.css : {}; const fontFamilyString = generateFontFaceCSS(options.fontName, cssOptions.cssPath || "", Date.now(), excludeFormat, hasTimestamp); await copyTemplate(styleTemplatePath, outDir, { fontname: options.fontName, cssString: cssString.join(''), cssToVars: cssToVars.join(''), infoData, fontSize: fontSize, timestamp: new Date().getTime(), prefix, fontFamily: fontFamilyString, nameAsUnicode: options.useNameAsUnicode, hasMulticolor: options.multicolor && Object.keys(multicolorMap).length > 0, _opts: cssOptions }); } if (options.typescript) { await createTypescript({ ...options, typescript: options.typescript }) } if (options.website) { const pageNames = ['font-class', 'unicode', 'symbol']; const htmlPaths: Record = {}; // setting default home page. const indexName = pageNames.includes(options.website.index) ? options.website.index : 'font-class'; pageNames.forEach(name => { const fileName = name === indexName ? 'index.html' : `${name}.html`; htmlPaths[name] = path.join(options.dist, fileName); }); const fontClassPath = htmlPaths['font-class']; const unicodePath = htmlPaths['unicode']; const symbolPath = htmlPaths['symbol']; // default template options.website.template = options.website.template || path.join(__dirname, 'website', 'index.njk'); // template data const tempData: SvgToFontOptions['website'] & { fontname: string; classNamePrefix: string; _type: string; _link: string; _IconHtml: string; _title: string; } = { meta: null, links: null, corners: null, description: null, footerInfo: null, ...options.website, fontname: options.fontName, classNamePrefix: options.classNamePrefix, _type: 'font-class', _link: `${(options.css && typeof options.css !== 'boolean' && options.css.fileName) || options.fontName}.css`, _IconHtml: cssIconHtml.join(''), _title: options.website.title || options.fontName }; // website logo if (options.website.logo && fs.pathExistsSync(options.website.logo) && path.extname(options.website.logo) === '.svg') { tempData.logo = fs.readFileSync(options.website.logo).toString(); } // website favicon if (options.website.favicon && fs.pathExistsSync(options.website.favicon)) { tempData.favicon = await image2uri(options.website.favicon); } else { tempData.favicon = ''; } const classHtmlStr = await createHTML(options.website.template, tempData); fs.outputFileSync(fontClassPath, classHtmlStr); log.log(`${color.green('SUCCESS')} Created ${fontClassPath} `); tempData._IconHtml = unicodeHtml.join(''); tempData._type = 'unicode'; const unicodeHtmlStr = await createHTML(options.website.template, tempData); fs.outputFileSync(unicodePath, unicodeHtmlStr); log.log(`${color.green('SUCCESS')} Created ${unicodePath} `); tempData._IconHtml = symbolHtml.join(''); tempData._type = 'symbol'; const symbolHtmlStr = await createHTML(options.website.template, tempData); fs.outputFileSync(symbolPath, symbolHtmlStr); log.log(`${color.green('SUCCESS')} Created ${symbolPath} `); } if (options.outSVGPath) { const outPath = await generateIconsSource(options); log.log(`${color.green('SUCCESS')} Created ${outPath} `); } if (options.outSVGReact) { const outPath = await generateReactIcons(options); log.log(`${color.green('SUCCESS')} Created React Components. `); } if (options.outSVGReactNative) { generateReactNativeIcons(options, unicodeObject); log.log(`${color.green('SUCCESS')} Created React Native Components. `); } if (options.outSVGVue) { const outPath = await generateVueIcons(options); log.log(`${color.green('SUCCESS')} Created Vue Components. `); } return infoData; } catch (error) { log.log('SvgToFont:CLI:ERR:', error); } } jaywcjlove-svgtofont-2472abc/src/log.ts000066400000000000000000000012571522075462600202620ustar00rootroot00000000000000export class Log { _disabled?:boolean; constructor(disabled?: boolean) { this.disabled = disabled || false } get disabled () { return this._disabled; } set disabled(val: boolean) { this._disabled = val; } log = (message?: any, ...optionalParams: any[]) => { if (this.logger) this.logger(message); if (this.disabled) return () => {} return console.log(message, ...optionalParams) } error = (message?: any, ...optionalParams: any[]) => { if (this.logger) this.logger(message); if (this.disabled) return () => {} return console.error(message, ...optionalParams) } logger = (message?: string) => {} } export const log = new Log();jaywcjlove-svgtofont-2472abc/src/multicolor.ts000066400000000000000000000145151522075462600216730ustar00rootroot00000000000000import { load, type CheerioAPI, type Cheerio } from 'cheerio'; import type { AnyNode } from 'domhandler'; // --- Types --- export interface ColorGroup { color: string; elements: string[]; } export interface SvgAnalysis { isMulticolor: boolean; colorGroups: ColorGroup[]; viewBox: string; width: string; height: string; } export interface LayerSvg { layerIndex: number; color: string; svgContent: string; } export interface MulticolorLayerInfo { layerIndex: number; glyphName: string; unicode: string; color: string; encodedCode: number; } export interface MulticolorIconInfo { originalName: string; layerCount: number; layers: MulticolorLayerInfo[]; } export type MulticolorMap = Record; export interface CreateSVGResult { unicodeObject: Record; multicolorMap: MulticolorMap; } // --- Named CSS colors map (subset of most common) --- const CSS_COLORS: Record = { black: '#000000', white: '#ffffff', red: '#ff0000', green: '#008000', blue: '#0000ff', yellow: '#ffff00', cyan: '#00ffff', magenta: '#ff00ff', orange: '#ffa500', purple: '#800080', pink: '#ffc0cb', gray: '#808080', grey: '#808080', silver: '#c0c0c0', maroon: '#800000', olive: '#808000', lime: '#00ff00', aqua: '#00ffff', teal: '#008080', navy: '#000080', fuchsia: '#ff00ff', brown: '#a52a2a', coral: '#ff7f50', crimson: '#dc143c', gold: '#ffd700', indigo: '#4b0082', ivory: '#fffff0', khaki: '#f0e68c', lavender: '#e6e6fa', linen: '#faf0e6', orchid: '#da70d6', peru: '#cd853f', plum: '#dda0dd', salmon: '#fa8072', sienna: '#a0522d', tan: '#d2b48c', thistle: '#d8bfd8', tomato: '#ff6347', turquoise: '#40e0d0', violet: '#ee82ee', wheat: '#f5deb3', darkblue: '#00008b', darkgreen: '#006400', darkred: '#8b0000', darkgray: '#a9a9a9', darkgrey: '#a9a9a9', lightblue: '#add8e6', lightgreen: '#90ee90', lightgray: '#d3d3d3', lightgrey: '#d3d3d3', lightpink: '#ffb6c1', lightyellow: '#ffffe0', }; // --- Color normalization --- export function normalizeColor(color: string): string { if (!color) return '#000000'; const trimmed = color.trim().toLowerCase(); if (trimmed === 'none' || trimmed === 'transparent') return 'none'; if (trimmed === 'currentcolor') return 'currentColor'; // Already hex if (trimmed.startsWith('#')) { if (trimmed.length === 4) { // #abc -> #aabbcc return '#' + trimmed[1] + trimmed[1] + trimmed[2] + trimmed[2] + trimmed[3] + trimmed[3]; } return trimmed; } // rgb(r, g, b) or rgba(r, g, b, a) const rgbMatch = trimmed.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/); if (rgbMatch) { const r = parseInt(rgbMatch[1], 10).toString(16).padStart(2, '0'); const g = parseInt(rgbMatch[2], 10).toString(16).padStart(2, '0'); const b = parseInt(rgbMatch[3], 10).toString(16).padStart(2, '0'); return `#${r}${g}${b}`; } // Named CSS color if (CSS_COLORS[trimmed]) { return CSS_COLORS[trimmed]; } // Fallback: return as-is (e.g. url(#gradient)) return trimmed; } // --- Resolve effective fill --- export function resolveEffectiveFill($: CheerioAPI, $el: Cheerio): string { // Check the element itself first const fill = $el.attr('fill'); if (fill !== undefined && fill !== null) { return fill; } // Walk up parent elements let $parent = $el.parent(); while ($parent.length > 0) { const tagName = ($parent[0] as any)?.tagName || ($parent[0] as any)?.name; if (tagName === 'svg') break; const parentFill = $parent.attr('fill'); if (parentFill !== undefined && parentFill !== null) { return parentFill; } $parent = $parent.parent(); } // SVG default fill is black return '#000000'; } // --- Analyze SVG colors --- export function analyzeSvgColors(svgContent: string): SvgAnalysis { const $ = load(svgContent, { xmlMode: true }); const svg = $('svg'); const viewBox = svg.attr('viewBox') || '0 0 24 24'; const width = svg.attr('width') || '24'; const height = svg.attr('height') || '24'; const shapeSelectors = 'path, rect, circle, ellipse, polygon, polyline, line'; const colorMap = new Map(); $(shapeSelectors).each((_, el) => { const $el = $(el); const fill = resolveEffectiveFill($, $el); const normalized = normalizeColor(fill); // Skip invisible elements if (normalized === 'none') return; // Skip gradient/pattern fills (url references) if (normalized.startsWith('url(')) return; if (!colorMap.has(normalized)) { colorMap.set(normalized, []); } // Serialize this element, preserving transforms from parent groups const elHtml = serializeElementWithTransforms($, $el); colorMap.get(normalized)!.push(elHtml); }); const colorGroups: ColorGroup[] = Array.from(colorMap.entries()).map(([color, elements]) => ({ color, elements, })); return { isMulticolor: colorGroups.length >= 2, colorGroups, viewBox, width, height, }; } // --- Serialize element with ancestor transforms --- function serializeElementWithTransforms($: CheerioAPI, $el: Cheerio): string { const transforms: string[] = []; // Collect transforms from ancestor elements let $parent = $el.parent(); while ($parent.length > 0) { const tagName = ($parent[0] as any)?.tagName || ($parent[0] as any)?.name; if (tagName === 'svg') break; const transform = $parent.attr('transform'); if (transform) { transforms.unshift(transform); } $parent = $parent.parent(); } // Get the element HTML, strip fill attribute let elHtml = $.html($el) || ''; elHtml = elHtml.replace(/\s*fill="[^"]*"/g, ''); // If there are ancestor transforms, wrap in elements if (transforms.length > 0) { let wrapped = elHtml; for (let i = transforms.length - 1; i >= 0; i--) { wrapped = `${wrapped}`; } return wrapped; } return elHtml; } // --- Generate layer SVGs --- export function generateLayerSvgs(analysis: SvgAnalysis): LayerSvg[] { return analysis.colorGroups.map((group, index) => { const svgContent = [ ``, ...group.elements, '', ].join('\n'); return { layerIndex: index, color: group.color, svgContent, }; }); } jaywcjlove-svgtofont-2472abc/src/styles/000077500000000000000000000000001522075462600204475ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/src/styles/_{{filename}}.css000066400000000000000000000011541522075462600240610ustar00rootroot00000000000000@font-face { {{fontFamily}} } {% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% if hasMulticolor %} [class^="{{prefix}}-"] [class^="path"]::before, [class*=" {{prefix}}-"] [class^="path"]::before { font-family: '{{fontname}}' !important; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% endif %} {% if not nameAsUnicode %} {{cssString}} {% endif %} jaywcjlove-svgtofont-2472abc/src/styles/_{{filename}}.less000066400000000000000000000011531522075462600242360ustar00rootroot00000000000000@font-face { {{fontFamily}} } {% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% if hasMulticolor %} [class^="{{prefix}}-"] [class^="path"]::before, [class*=" {{prefix}}-"] [class^="path"]::before { font-family: '{{fontname}}' !important; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% endif %} {% if not nameAsUnicode %} {{cssString}} {% endif %}jaywcjlove-svgtofont-2472abc/src/styles/_{{filename}}.module.less000066400000000000000000000011671522075462600255270ustar00rootroot00000000000000@font-face { {{fontFamily}} } {% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% if hasMulticolor %} [class^="{{prefix}}-"] [class^="path"]::before, [class*=" {{prefix}}-"] [class^="path"]::before { font-family: '{{fontname}}' !important; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% endif %} {% if not nameAsUnicode %} :global { {{cssString}} } {% endif %}jaywcjlove-svgtofont-2472abc/src/styles/_{{filename}}.scss000066400000000000000000000013201522075462600242370ustar00rootroot00000000000000@font-face { {{fontFamily}} } {% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% if hasMulticolor %} [class^="{{prefix}}-"] [class^="path"]::before, [class*=" {{prefix}}-"] [class^="path"]::before { font-family: '{{fontname}}' !important; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% endif %} {% if not nameAsUnicode %} {{ cssString }} {% for name, value in infoData %} ${{prefix}}-{{ name }}: '{{ value.encodedCode }}'; {%- endfor %} {% endif %}jaywcjlove-svgtofont-2472abc/src/styles/_{{filename}}.styl000066400000000000000000000013201522075462600242570ustar00rootroot00000000000000@font-face { {{fontFamily}} } {% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% if hasMulticolor %} [class^="{{prefix}}-"] [class^="path"]::before, [class*=" {{prefix}}-"] [class^="path"]::before { font-family: '{{fontname}}' !important; font-style: normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } {% endif %} {% if not nameAsUnicode %} {{ cssString }} {% for name, value in infoData %} ${{prefix}}-{{ name }} = '{{ value.encodedCode }}' {%- endfor %} {% endif %}jaywcjlove-svgtofont-2472abc/src/utils.ts000066400000000000000000000354221522075462600206420ustar00rootroot00000000000000import { SVGIcons2SVGFontStream } from 'svgicons2svgfont'; import fs, { ReadStream } from 'fs-extra'; import path from 'path'; import color from 'colors-cli'; import { load } from 'cheerio'; import svg2ttf from 'svg2ttf'; import ttf2eot from 'ttf2eot'; import ttf2woff from 'ttf2woff'; import ttf2woff2 from 'ttf2woff2'; import nunjucks from 'nunjucks'; import { merge } from 'auto-config-loader'; import { type SvgToFontOptions } from './'; import { log } from './log.js'; import { analyzeSvgColors, generateLayerSvgs, type MulticolorMap, type CreateSVGResult } from './multicolor.js'; let UnicodeObj: Record = {}; /** * Unicode Private Use Area start. * https://en.wikipedia.org/wiki/Private_Use_Areas */ let startUnicode = 0xea01; /** * SVG to SVG font */ export function createSVG(options: SvgToFontOptions = {}): Promise { startUnicode = options.startUnicode UnicodeObj = {} const multicolorMap: MulticolorMap = {}; // Temp directory for multicolor layer SVGs const tempDir = options.multicolor ? path.join(options.dist, '.multicolor-tmp') : null; if (tempDir) fs.ensureDirSync(tempDir); return new Promise(async (resolve, reject) => { const fontStream = new SVGIcons2SVGFontStream({ ...options.svgicons2svgfont }); function writeFontStream(svgPath: string) { // file name let _name = path.basename(svgPath, ".svg"); const glyph = fs.createReadStream(svgPath) as ReadStream & { metadata: { unicode: string[], name: string } }; const curUnicode = String.fromCodePoint(startUnicode); const [_curUnicode, _startUnicode] = options.getIconUnicode ? (options.getIconUnicode(_name, curUnicode, startUnicode) || [curUnicode]) : [curUnicode]; if (_startUnicode) startUnicode = _startUnicode; const unicode: string[] = [_curUnicode]; if (curUnicode === _curUnicode && (!_startUnicode || startUnicode === _startUnicode)) startUnicode++; UnicodeObj[_name] = unicode[0]; if (!!options.useNameAsUnicode) { unicode[0] = _name; UnicodeObj[_name] = _name; } if (!!options.addLigatures) { unicode.push(_name) } glyph.metadata = { unicode, name: _name }; fontStream.write(glyph); } function writeGlyphFromFile(filePath: string, glyphName: string): { unicode: string; codepoint: number } { const glyph = fs.createReadStream(filePath) as ReadStream & { metadata: { unicode: string[], name: string } }; const curUnicode = String.fromCodePoint(startUnicode); const unicode: string[] = [curUnicode]; UnicodeObj[glyphName] = unicode[0]; startUnicode++; glyph.metadata = { unicode, name: glyphName }; fontStream.write(glyph); return { unicode: curUnicode, codepoint: curUnicode.codePointAt(0)! }; } const DIST_PATH = path.join(options.dist, options.fontName + ".svg"); // Setting the font destination fontStream.pipe(fs.createWriteStream(DIST_PATH)) .on("finish", () => { // Clean up temp directory if (tempDir) fs.removeSync(tempDir); log.log(`${color.green('SUCCESS')} ${color.blue_bt('SVG')} font successfully created!\n ╰┈▶ ${DIST_PATH}`); resolve({ unicodeObject: UnicodeObj, multicolorMap }); }) .on("error", (err) => { if (err) { reject(err); } }); filterSvgFiles(options.src).forEach((svg: string) => { if (typeof svg !== 'string') return false; if (options.multicolor) { const iconName = path.basename(svg, '.svg'); const svgContent = fs.readFileSync(svg, 'utf-8'); const analysis = analyzeSvgColors(svgContent); if (analysis.isMulticolor) { const layerSvgs = generateLayerSvgs(analysis); const layers: import('./multicolor.js').MulticolorLayerInfo[] = []; layerSvgs.forEach((layer, idx) => { const glyphName = `${iconName}-path${idx + 1}`; const tmpPath = path.join(tempDir!, glyphName + '.svg'); fs.writeFileSync(tmpPath, layer.svgContent); const result = writeGlyphFromFile(tmpPath, glyphName); layers.push({ layerIndex: idx, glyphName, unicode: result.unicode, color: layer.color, encodedCode: result.codepoint, }); }); multicolorMap[iconName] = { originalName: iconName, layerCount: layers.length, layers, }; return; // Skip normal processing for this multicolor icon } } writeFontStream(svg); }); // Do not forget to end the stream fontStream.end(); }); } /** * Converts a string to pascal case. * * @example * * ```js * toPascalCase('some_database_field_name'); // 'SomeDatabaseFieldName' * toPascalCase('Some label that needs to be pascalized'); * // 'SomeLabelThatNeedsToBePascalized' * toPascalCase('some-javascript-property'); // 'SomeJavascriptProperty' * toPascalCase('some-mixed_string with spaces_underscores-and-hyphens'); * // 'SomeMixedStringWithSpacesUnderscoresAndHyphens' * ``` */ export const toPascalCase = (str: string) => str .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) .map(x => x.charAt(0).toUpperCase() + x.slice(1).toLowerCase()) .join(''); /* * Filter svg files * @return {Array} svg files */ export function filterSvgFiles(svgFolderPath: string): string[] { let files = fs.readdirSync(svgFolderPath, 'utf-8'); let svgArr = []; if (!files) { throw new Error(`Error! Svg folder is empty.${svgFolderPath}`); } for (let i in files) { if (typeof files[i] !== 'string' || path.extname(files[i]) !== '.svg') continue; if (!~svgArr.indexOf(files[i])) { svgArr.push(path.join(svgFolderPath, files[i])); } } return svgArr; } export function snakeToUppercase(str: string) { return str.split(/[-_]/) .map(partial => partial.charAt(0).toUpperCase() + partial.slice(1)) .join('') } export type TypescriptOptions = { extension?: 'd.ts' | 'ts' | 'tsx', enumName?: string } /** * Create typescript declarations for icon classnames */ export async function createTypescript(options: Omit & { typescript: TypescriptOptions | true }) { const tsOptions = options.typescript === true ? {} : options.typescript; const uppercaseFontName = snakeToUppercase(options.fontName); const { extension = 'd.ts', enumName = uppercaseFontName } = tsOptions; const DIST_PATH = path.join(options.dist, `${options.fontName}.${extension}`); const fileNames = filterSvgFiles(options.src).map(svgPath => path.basename(svgPath, path.extname(svgPath))); await fs.writeFile( DIST_PATH, [ `export enum ${enumName} {`, ...fileNames.map(name => ` ${snakeToUppercase(name)} = "${options.classNamePrefix}-${name}",`), '}', `export type ${enumName}Classname = ${fileNames.map(name => `"${options.classNamePrefix}-${name}"`).join(' | ')}`, `export type ${enumName}Icon = ${fileNames.map(name => `"${name}"`).join(' | ')}`, `export const ${enumName}Prefix = "${options.classNamePrefix}-"`, ].join('\n'), ); log.log(`${color.green('SUCCESS')} Created ${DIST_PATH}`); } /** * SVG font to TTF */ export function createTTF(options: SvgToFontOptions = {}): Promise { return new Promise((resolve, reject) => { options.svg2ttf = options.svg2ttf || {}; const DIST_PATH = path.join(options.dist, options.fontName + ".ttf"); let ttf = svg2ttf(fs.readFileSync(path.join(options.dist, options.fontName + ".svg"), "utf8"), options.svg2ttf); const ttfBuf = Buffer.from(ttf.buffer); fs.writeFile(DIST_PATH, ttfBuf, (err: NodeJS.ErrnoException) => { if (err) { return reject(err); } log.log(`${color.green('SUCCESS')} ${color.blue_bt('TTF')} font successfully created!\n ╰┈▶ ${DIST_PATH}`); resolve(ttfBuf); }); }); }; /** * TTF font to EOT */ export function createEOT(options: SvgToFontOptions = {}, ttf: Buffer) { return new Promise((resolve, reject) => { const DIST_PATH = path.join(options.dist, options.fontName + '.eot'); const ttf2eotResult = ttf2eot(ttf); const eot = Buffer.from(ttf2eotResult.buffer, ttf2eotResult.byteOffset, ttf2eotResult.byteLength); fs.writeFile(DIST_PATH, eot, (err: NodeJS.ErrnoException) => { if (err) { return reject(err); } log.log(`${color.green('SUCCESS')} ${color.blue_bt('EOT')} font successfully created!\n ╰┈▶ ${DIST_PATH}`); resolve(eot); }); }); }; /** * TTF font to WOFF */ export function createWOFF(options: SvgToFontOptions = {}, ttf: Buffer) { return new Promise((resolve, reject) => { const DIST_PATH = path.join(options.dist, options.fontName + ".woff"); const woff = Buffer.from(ttf2woff(ttf).buffer); fs.writeFile(DIST_PATH, woff, (err) => { if (err) { return reject(err); } log.log(`${color.green('SUCCESS')} ${color.blue_bt('WOFF')} font successfully created!\n ╰┈▶ ${DIST_PATH}`); resolve(woff); }); }); }; /** * TTF font to WOFF2 */ export function createWOFF2(options: SvgToFontOptions = {}, ttf: Buffer) { return new Promise((resolve, reject) => { const DIST_PATH = path.join(options.dist, options.fontName + ".woff2"); const woff2 = Buffer.from(ttf2woff2(ttf).buffer); fs.writeFile(DIST_PATH, woff2, (err) => { if (err) { return reject(err); } log.log(`${color.green('SUCCESS')} ${color.blue_bt('WOFF2')} font successfully created!\n ╰┈▶ ${DIST_PATH}`); resolve({ path: DIST_PATH }); }); }); }; /** * Create SVG Symbol */ export function createSvgSymbol(options: SvgToFontOptions = {}) { const DIST_PATH = path.join(options.dist, `${options.fontName}.symbol.svg`); const $ = load(''); return new Promise((resolve, reject) => { filterSvgFiles(options.src).forEach(svgPath => { const fileName = path.basename(svgPath, path.extname(svgPath)); let file = fs.readFileSync(svgPath, "utf8"); // trim xml declaration file = file.replace(/<\?xml.*?\?>\s*/g, '').trim(); const svgNode = $(file); const symbolNode = $(""); symbolNode.attr("viewBox", svgNode.attr("viewBox")); symbolNode.attr("id", `${options.classNamePrefix}-${fileName}`); symbolNode.append(svgNode.html()); $('svg').append(symbolNode); }); fs.writeFile(DIST_PATH, $.html("svg"), (err) => { if (err) { return reject(err); } log.log(`${color.green('SUCCESS')} ${color.blue_bt('Svg Symbol')} font successfully created!\n ╰┈▶ ${DIST_PATH}`); resolve({ path: DIST_PATH, svg: $.html("svg") }); }); }); }; export type CSSOptions = { /** * Output the css file to the specified directory */ output?: string; /** * Which files are exported. */ include?: RegExp; /** * Setting font size. */ fontSize?: string | boolean; /** * Set the path in the css file * https://github.com/jaywcjlove/svgtofont/issues/48#issuecomment-739547189 */ cssPath?: string; /** * Set file name * https://github.com/jaywcjlove/svgtofont/issues/48#issuecomment-739547189 */ fileName?: string; /** * Ad hoc template variables. */ templateVars?: Record; /** * When including CSS files in a CSS file, * you can add a timestamp parameter or custom text to the file path to prevent browser caching issues and ensure style updates are applied. @default true * @example `path/to/iconfont.css?t=1612345678` */ hasTimestamp?: boolean | string; } // As we are processing css files, we need to eacape HTML entities. const safeNunjucks = nunjucks.configure({ autoescape: false }); /** * Copy template files */ export async function copyTemplate(inDir: string, outDir: string, { _opts, ...vars }: Record & { _opts: CSSOptions }) { const files = await fs.readdir(inDir, { withFileTypes: true }); const context = { ...(_opts.templateVars || {}), ...vars, cssPath: _opts.cssPath || '', filename: _opts.fileName || vars.fontname, } await fs.ensureDir(outDir); for (const file of files) { if (!file.isFile()) continue; if (_opts.include && !(new RegExp(_opts.include)).test(file.name)) continue; let newFileName = file.name.replace(/\.template$/, '').replace(/^_/, ''); for (const key in context) newFileName = newFileName.replace(`{{${key}}}`, `${context[key]}`); const template = await fs.readFile(path.join(inDir, file.name), 'utf8'); const content = safeNunjucks.renderString(template, context); const filePath = path.join(outDir, newFileName) await fs.writeFile(filePath, content); log.log(`${color.green('SUCCESS')} Created ${filePath} `); } }; /** * Create HTML */ export function createHTML(templatePath: string, data: Record): string { return nunjucks.renderString(fs.readFileSync(templatePath, 'utf8'), { ...data, Date: Date, JSON: JSON, Math: Math, Number: Number, Object: Object, RegExp: RegExp, String: String, typeof: (v: any) => typeof v, }); }; export function generateFontFaceCSS(fontName: string, cssPath: string, timestamp: number, excludeFormat: string[], hasTimestamp: boolean | string = true): string { const timestamString = hasTimestamp === true ? `?t=${timestamp}` : (typeof hasTimestamp == 'string' ? `?t=${hasTimestamp}` : undefined); const formats = [ { ext: 'eot', format: 'embedded-opentype', ieFix: true }, { ext: 'woff2', format: 'woff2' }, { ext: 'woff', format: 'woff' }, { ext: 'ttf', format: 'truetype' }, { ext: 'svg', format: 'svg' } ]; let cssString = ` font-family: "${fontName}";\n`; if (!excludeFormat.includes('eot')) { cssString += ` src: url('${cssPath}${fontName}.eot${timestamString || ''}'); /* IE9*/\n`; } cssString += ' src: '; const srcParts = formats .filter(format => !excludeFormat.includes(format.ext)) .map(format => { if (format.ext === 'eot') { return `url('${cssPath}${fontName}.eot${timestamString || '?'}#iefix') format('${format.format}') /* IE6-IE8 */`; } return `url('${cssPath}${fontName}.${format.ext}${timestamString || ''}') format('${format.format}')`; }); cssString += srcParts.join(',\n ') + ';'; return cssString; } export const getDefaultOptions = (options: SvgToFontOptions): SvgToFontOptions => { return merge({ dist: path.resolve(process.cwd(), 'fonts'), src: path.resolve(process.cwd(), 'svg'), startUnicode: 0xea01, svg2ttf: {}, svgicons2svgfont: { fontName: 'iconfont', }, fontName: 'iconfont', symbolNameDelimiter: '-', multicolor: false, }, options); }; jaywcjlove-svgtofont-2472abc/src/website/000077500000000000000000000000001522075462600205665ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/src/website/index.njk000066400000000000000000000152271522075462600224100ustar00rootroot00000000000000 {{ _title }} {% for k, v in meta|default({}) %} {% endfor %} {% if favicon %} {% endif %} {% if _type === 'font-class' and _link %} {% endif %} {% if corners and corners.url %} {% endif %}
    {% if typeof(logo) === 'string' %} {% endif %}

    {{ _title }}{{ version }}

    {{ description|default(meta.description) }}

    {% for linkItem in links|default([]) %} {{ linkItem.title }}{% if not loop.last %} · {% endif %} {% endfor %}

      {{ _IconHtml|safe }}
    jaywcjlove-svgtofont-2472abc/test/000077500000000000000000000000001522075462600173145ustar00rootroot00000000000000jaywcjlove-svgtofont-2472abc/test/index.test.js000066400000000000000000000042331522075462600217410ustar00rootroot00000000000000import fs from 'fs-extra'; import path from 'path'; import { fileURLToPath } from 'url'; import pkg from '../package.json'; // const __filename = fileURLToPath(import.meta.url); // const __dirname = path.dirname(__filename); // const pkg = fs.readJSONSync(path.resolve(__dirname, "../package.json")); // const fs = require('fs-extra'); // const path = require('path'); // const svgtofont = require('../lib/index.js'); it('example test case.', async () => { const dist = path.resolve(process.cwd(), 'examples', 'example', 'dist'); const fileNames = await fs.readdir(dist); expect(fileNames).toEqual([ 'font-class.html', 'index.html', 'react', 'reactNative', 'svgtofont.css', 'svgtofont.d.ts', 'svgtofont.eot', 'svgtofont.json', 'svgtofont.less', 'svgtofont.module.less', 'svgtofont.scss', 'svgtofont.styl', 'svgtofont.svg', 'svgtofont.symbol.svg', 'svgtofont.ttf', 'svgtofont.woff', 'svgtofont.woff2', 'symbol.html', 'vue', ]); }); it('example simple test case.', async () => { const dist = path.resolve(process.cwd(), 'examples', 'example', 'example'); const fileNames = await fs.readdir(dist); expect(fileNames).toEqual([ 'svgtofont.css', 'svgtofont.eot', 'svgtofont.less', 'svgtofont.module.less', 'svgtofont.scss', 'svgtofont.styl', 'svgtofont.svg', 'svgtofont.symbol.svg', 'svgtofont.ttf', 'svgtofont.woff', 'svgtofont.woff2', ]); }) it('templates templates test case.', async () => { const dist = path.resolve(process.cwd(), 'examples', 'templates', 'dist2'); const fileNames = await fs.readdir(dist); expect(fileNames).toEqual([ 'font-class.html', 'index.html', 'react', 'reactNative', 'svgtofont.css', 'svgtofont.eot', 'svgtofont.json', 'svgtofont.less', 'svgtofont.module.less', 'svgtofont.scss', 'svgtofont.styl', 'svgtofont.svg', 'svgtofont.symbol.svg', 'svgtofont.ttf', 'svgtofont.woff', 'svgtofont.woff2', 'symbol.html', 'vue' ]); const css = await fs.readFile(path.resolve(dist, 'svgtofont.css')); expect(css.toString().indexOf('Hello CSS!') > -1).toBeTruthy(); }) jaywcjlove-svgtofont-2472abc/tsconfig.json000066400000000000000000000011571522075462600210500ustar00rootroot00000000000000{ "compilerOptions": { "module": "ESNext", "target": "ESNext", "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "declaration": true, "resolveJsonModule": true, "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, "sourceMap": true, "strict": false, "skipLibCheck": true, "outDir": "lib", "baseUrl": "." }, "include": [ "src/**/*" , "test/index.test.mjs", "test/index.test.js" ], "exclude": [ "node_modules", "**/*.spec.ts", "**/*.test.ts" ] }