├── .github ├── FUNDING.yml ├── codeql │ └── codeql-config.yml └── workflows │ ├── badges.yml │ ├── ci.yml │ └── codeql.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist ├── svg-path-commander.cjs ├── svg-path-commander.cjs.map ├── svg-path-commander.d.cts ├── svg-path-commander.d.ts ├── svg-path-commander.js ├── svg-path-commander.js.map ├── svg-path-commander.mjs └── svg-path-commander.mjs.map ├── docs ├── assets │ ├── SVGPathCommander.svg │ ├── apple-touch-icon.png │ ├── favicon.ico │ └── style.css ├── convert.html ├── dev.html ├── index.html ├── svg-path-commander.js └── svg-path-commander.js.map ├── package.json ├── pnpm-lock.yaml ├── src ├── convert │ ├── pathToAbsolute.ts │ ├── pathToCurve.ts │ ├── pathToRelative.ts │ └── pathToString.ts ├── index.ts ├── interface.ts ├── main.ts ├── math │ ├── arcTools.ts │ ├── bezier.ts │ ├── cubicTools.ts │ ├── distanceSquareRoot.ts │ ├── lineTools.ts │ ├── midPoint.ts │ ├── polygonTools.ts │ ├── quadTools.ts │ ├── rotateVector.ts │ └── roundTo.ts ├── options │ └── options.ts ├── parser │ ├── error.ts │ ├── finalizeSegment.ts │ ├── invalidPathValue.ts │ ├── isArcCommand.ts │ ├── isDigit.ts │ ├── isDigitStart.ts │ ├── isMoveCommand.ts │ ├── isPathCommand.ts │ ├── isSpace.ts │ ├── paramsCount.ts │ ├── paramsParser.ts │ ├── parsePathString.ts │ ├── pathParser.ts │ ├── scanFlag.ts │ ├── scanParam.ts │ ├── scanSegment.ts │ └── skipSpaces.ts ├── process │ ├── absolutizeSegment.ts │ ├── arcToCubic.ts │ ├── getSVGMatrix.ts │ ├── iterate.ts │ ├── lineToCubic.ts │ ├── normalizePath.ts │ ├── normalizeSegment.ts │ ├── optimizePath.ts │ ├── projection2d.ts │ ├── quadToCubic.ts │ ├── relativizeSegment.ts │ ├── reverseCurve.ts │ ├── reversePath.ts │ ├── roundPath.ts │ ├── roundSegment.ts │ ├── segmentToCubic.ts │ ├── shortenSegment.ts │ ├── splitCubic.ts │ ├── splitPath.ts │ └── transformPath.ts ├── types.ts └── util │ ├── distanceEpsilon.ts │ ├── getClosestPoint.ts │ ├── getDrawDirection.ts │ ├── getPathArea.ts │ ├── getPathBBox.ts │ ├── getPointAtLength.ts │ ├── getPropertiesAtLength.ts │ ├── getPropertiesAtPoint.ts │ ├── getSegmentAtLength.ts │ ├── getSegmentOfPoint.ts │ ├── getTotalLength.ts │ ├── isAbsoluteArray.ts │ ├── isCurveArray.ts │ ├── isElement.ts │ ├── isNormalizedArray.ts │ ├── isPathArray.ts │ ├── isPointInStroke.ts │ ├── isRelativeArray.ts │ ├── isValidPath.ts │ ├── shapeParams.ts │ ├── shapeToPath.ts │ └── shapeToPathArray.ts ├── test ├── class.test.ts ├── fixtures │ ├── getMarkup.ts │ ├── shapeObjects.ts │ ├── shapes.js │ └── simpleShapes.js └── static.test.ts ├── tsconfig.json ├── tsup.config.ts ├── vite.config.ts ├── vitest.config-ui.ts └── vitest.config.ts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [thednp] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /.github/codeql/codeql-config.yml: -------------------------------------------------------------------------------- 1 | paths: 2 | - src -------------------------------------------------------------------------------- /.github/workflows/badges.yml: -------------------------------------------------------------------------------- 1 | name: badges 2 | on: 3 | push: 4 | # update README badge only if the README file changes 5 | # or if the package.json file changes, or this file changes 6 | # IMPORTANT: branches must match 7 | branches: 8 | - master 9 | paths: 10 | - README.md 11 | - package.json 12 | - .github/workflows/badges.yml 13 | pull_request: 14 | # update README badge only if the README file changes 15 | # or if the package.json file changes, or this file changes 16 | # IMPORTANT: branches must match 17 | branches: 18 | - master 19 | paths: 20 | - README.md 21 | - package.json 22 | - .github/workflows/badges.yml 23 | 24 | jobs: 25 | badges: 26 | runs-on: ubuntu-24.04 27 | steps: 28 | - name: Checkout 🛎 29 | uses: actions/checkout@v3 30 | 31 | - name: Update version badges 🏷 32 | run: npx -p dependency-version-badge update-badge typescript vitest vite 33 | 34 | - name: Commit any changed files 💾 35 | uses: stefanzweifel/git-auto-commit-action@v4 36 | with: 37 | branch: master 38 | file_pattern: README.md 39 | commit_message: Updated badges 40 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | branches: 5 | - master 6 | paths: 7 | - src/**/* 8 | - src/* 9 | - test 10 | - package.json 11 | - .github/workflows/ci.yml 12 | 13 | pull_request: 14 | branches: 15 | - master 16 | paths: 17 | - src/**/* 18 | - src/* 19 | - test 20 | - package.json 21 | - .github/workflows/ci.yml 22 | 23 | jobs: 24 | test: 25 | runs-on: ubuntu-24.04 26 | name: Test on Node 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | 31 | - name: PNPM setup 32 | uses: pnpm/action-setup@v3 33 | 34 | - name: Deno Setup 35 | uses: denoland/setup-deno@v2 36 | with: 37 | deno-version: v2.x 38 | 39 | - name: Setup Node 40 | uses: actions/setup-node@v4 41 | with: 42 | node-version: 22 43 | cache: pnpm 44 | 45 | - name: Install Dependencies 46 | run: pnpm install --no-frozen-lockfile 47 | 48 | - name: Install Playwright 49 | run: pnpm exec playwright install 50 | 51 | - name: Lint 52 | run: pnpm lint 53 | 54 | - name: Build 55 | run: pnpm build 56 | 57 | - name: ViTest 58 | run: pnpm test 59 | 60 | - name: Upload coverage report on to coveralls.io... 61 | uses: coverallsapp/github-action@master 62 | with: 63 | github-token: ${{ secrets.GITHUB_TOKEN }} 64 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | paths: 18 | - src 19 | - package.json 20 | - .github/workflows/codeql.yml 21 | pull_request: 22 | # The branches below must be a subset of the branches above 23 | branches: [ master ] 24 | paths: 25 | - src 26 | - package.json 27 | - .github/workflows/codeql.yml 28 | 29 | jobs: 30 | analyze: 31 | name: Analyze 32 | runs-on: ubuntu-latest 33 | permissions: 34 | actions: read 35 | contents: read 36 | security-events: write 37 | 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | language: [ 'typescript' ] 42 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 43 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 44 | 45 | steps: 46 | - name: Checkout repository 47 | uses: actions/checkout@v3 48 | 49 | # Initializes the CodeQL tools for scanning. 50 | - name: Initialize CodeQL 51 | uses: github/codeql-action/init@v3 52 | with: 53 | languages: ${{ matrix.language }} 54 | # If you wish to specify custom queries, you can do so here or in a config file. 55 | # By default, queries listed here will override any specified in a config file. 56 | # Prefix the list here with "+" to use these queries and those in the config file. 57 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 58 | # queries: security-extended,security-and-quality 59 | config-file: ./.github/codeql/codeql-config.yml 60 | 61 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 62 | # If this step fails, then you should remove it and run the build manually (see below) 63 | - name: Autobuild 64 | uses: github/codeql-action/autobuild@v3 65 | 66 | # ℹ️ Command-line programs to run using the OS shell. 67 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 68 | 69 | # If the Autobuild fails above, remove it and uncomment the following three lines. 70 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 71 | 72 | # - run: | 73 | # echo "Run, Build Application using script" 74 | # ./location_of_script_within_repo/buildscript.sh 75 | 76 | - name: Perform CodeQL Analysis 77 | uses: github/codeql-action/analyze@v3 78 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | node_modules 3 | experiments 4 | coverage 5 | test/__screenshots__ 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | .gitignore 3 | .github 4 | node_modules 5 | experiments 6 | docs 7 | coverage 8 | test 9 | src 10 | tsconfig.json 11 | vite.config.ts 12 | vitest.config.ts 13 | vitest.config-ui.ts 14 | tsup.config.ts 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 thednp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SVGPathCommander 2 | [![Coverage Status](https://coveralls.io/repos/github/thednp/svg-path-commander/badge.svg)](https://coveralls.io/github/thednp/svg-path-commander) 3 | [![ci](https://github.com/thednp/svg-path-commander/actions/workflows/ci.yml/badge.svg)](https://github.com/thednp/svg-path-commander/actions/workflows/ci.yml) 4 | [![NPM Version](https://img.shields.io/npm/v/svg-path-commander.svg)](https://www.npmjs.com/package/svg-path-commander) 5 | [![NPM Downloads](https://img.shields.io/npm/dm/svg-path-commander.svg)](http://npm-stat.com/charts.html?svg-path-commander) 6 | [![jsDeliver](https://img.shields.io/jsdelivr/npm/hw/svg-path-commander)](https://www.jsdelivr.com/package/npm/svg-path-commander) 7 | [![typescript version](https://img.shields.io/badge/typescript-5.8.3-brightgreen)](https://www.typescriptlang.org/) 8 | [![vitest version](https://img.shields.io/badge/vitest-3.1.4-brightgreen)](https://vitest.dev/) 9 | [![vite version](https://img.shields.io/badge/vite-6.3.5-brightgreen)](https://vitejs.dev/) 10 | 11 | ![image](./docs/assets/SVGPathCommander.svg) 12 | 13 | A modern set of Typescript tools for manipulating the `d` (description) attribute for *SVGPathElement* items. The library is implementing modern JavaScript API to produce reusable path strings with lossless quality. In addition, you also have a powerful tool to convert other SVG shapes like `` or `` to ``. 14 | 15 | 16 | While you may find familiar tools inside, this library brings ***new additions***: 17 | * the build in `getBBox`, `getPointAtLength` and `getTotalLength` are more reliable and much more accurate than the native methods, not to mention their high [performance](https://github.com/thednp/svg-path-commander/issues/44) ratings; 18 | * thanks to the community contributions we've implemented useful tools like `getPropertiesAtLength`, `getSegmentOfPoint` or `isPointInStroke`; 19 | * a tool that can *reverse path draw direction* without altering path commands, even with specific shorthand path commands; 20 | * a unique tool that can *reverse path draw direction* for path strings with only 'C' path commands; 21 | * a new and unique tool to *apply transform functions to path commands* via the modern *DOMMatrix* API. 22 | 23 | **The key differences with other libraries**: 24 | * Typescript sourced with modernized codebase, all inherited codebase has been modernized as well; 25 | * along with the modern codebase, the library also comes with strong TypeScript definitions; 26 | * this library can create 3D to 2D projections, making your SVGs look like 3D but in the SVG coordinate system; 27 | * you can use this library in both web apps and Node.js, you are not restricted to a single environment; 28 | * path command transformations are all consistent with the SVG coordinates system, where others compute transform origin only for rotation transformation. 29 | 30 | **SVGPathCommander** can use the [DOMMatrix API](https://developer.mozilla.org/en-US/docs/Web/API/DOMMatrix) for *SVGPathElement* path command transformation and implements a very fast and modernized [DOMMatrix shim](https://github.com/thednp/dommatrix). 31 | There are a couple of good reasons for this implementation: 32 | * *WebKitCSSMatrix* and *SVGMatrix* APIs are slowly pushed away by DOMMatrix, the green light for new and modern implementations; 33 | * we can actually apply a [3D transformation](https://github.com/ndebeiss/svg3d) matrix to SVG path commands, by calculating a 2D projection of the actual shape in 3D coordinates; 34 | * when most tools available will be rendered absolete, we are ready for new challenges. 35 | 36 | This library is available on [CDN](https://www.jsdelivr.com/package/npm/svg-path-commander) and [npm](https://www.npmjs.com/package/svg-path-commander). 37 | 38 | 39 | # Install 40 | 41 | ``` 42 | npm install svg-path-commander 43 | ``` 44 | 45 | 46 | # CDN 47 | ```html 48 |