fontello / svgpath Goto Github PK
View Code? Open in Web Editor NEWSVG path low level transformations toolkit
License: MIT License
SVG path low level transformations toolkit
License: MIT License
svgpath.rotate(rotatenum).scale(scalenum).rel().toString()
something wrong?
Maybe I am doing something completly wring, but when trying to run the example code from the README, I got the following error:
test.js:9
.toFixed(1) // Here the real rounding happens
^
TypeError: Object M157.5 211L172.6 241.6 206.35 246.5 181.9 270.3 187.65 303.95 157.5 288.1 127.3 304 133.05 270.35 108.65 246.55 142.4 241.65 157.5 211Z has no method 'toFixed'
at Object.<anonymous> (/Users/ma/CODE/svgpath_test/test.js:9:2)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:902:3
On quick inspection the chaining seems to be not working as advertised.
complete example (as I said from README, I jus added a random path):
var SvgPath = require('svgpath');
var pathData = "M115,22l30.2,61.2l67.5,9.8l-48.9,47.6l11.5,67.3L115,176.2 L54.6,208l11.5-67.3L17.3,93.1l67.5-9.8L115,22z"
var transformedPath = new SvgPath(pathData)
.scale(0.5)
.translate(100,200)
.abs()
.toFixed(1) // Here the real rounding happens
.rel()
.toFixed(1) // Fix js floating point error/garbage after rel()
.toString()
The command
svgpath("M0,0 A 10,10 45 0,1 10,10z").transform("scale(1,2)").toString()
gives the wrong result
"M0 0A10 20 45 0 1 10 20z"
It is obvious that if your ellipse has angle non 0 [mod pi/2] and you scale with two different values, you can't obtain the ellipse with the same angle.
You can check the result by comparing this resulting path with the transform made by the browser :
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 40 40">
<!-- the original path -->
<path transform="scale(1,2)" d="M0,0 A 10,10 45 0,1 10,10" style="fill:none;stroke-width:.1;stroke:blue"/>
<!-- the path transformed by svgpath -->
<path transform="" d="M 0 0 A 10 20 45 0 1 10 20" style="fill:none;stroke-width:.1;stroke:red"/>
</svg>
It looks to me like the iterate function contains a bug. If one returns a segment to replace the old one from an Iterator function, every member of the new segment array is added individually to the segments array instead of adding it as a whole:
newSegments = [];
for (i = 0; i < segments.length; i++) {
if (typeof replacements[i] !== 'undefined') {
for (j = 0; j < replacements[i].length; j++) {
newSegments.push(replacements[i][j]);
}
} else {
newSegments.push(segments[i]);
}
}
this.segments = newSegments;
Should instead be:
newSegments = [];
for (i = 0; i < segments.length; i++) {
if (typeof replacements[i] !== 'undefined') {
newSegments.push(replacements[i]);
} else {
newSegments.push(segments[i]);
}
}
this.segments = newSegments;
in my opinion.
Are the any plans to support more modularity for the library, using ES modules? So only the parts of it that are used could be imported using import
or require
instead of the whole library.
This will help to reduce the size of final Webpack bundle.
As I understand right now all functions like scale
, rotate
are incapsulated as the parts of the object SvgPath
. If each function could return new copy of the object instead of mutating it, that it could make it completely independent so the function could be exported using ES6 export
statement and imported lately.
For example:
const {from, scale, round, toString} import from svgpath;
const aPath = from(_some_path); //returns new SVGPath object
const scaled = scale(aPath, 0.5); //returns new SVGPath object
const translated =translate(aPath, 100, 200); //returns new SVGPath object
console.log( toString(scaled) );
svgpath('M 0 0 A 0 10 0 0 0 10 10').unarc().toString()
returns 'M0 0'
. But arc with 0 radius should be treated as segment. So we expect as return 'M0 0 L 10 10'
.
It would be great to have the ability to detect if the path is open/closed in svgpath.
stroke="#9999CC" fill="none" |
stroke="#9999CC" fill="none" |
---|---|
d="M5 8.5, 13.2c0, 79.9, 91.8, 79.9, 91.8, 159.7 Z" |
d="M 58.5, 13.2c0, 79.9, 91.8, 79.9, 91.8, 159.7 " |
Fore example:
const path = svgpath(__your_path__)
path.isOpen() // true or false
And corresponded methods for opening/closing the path.
const path = svgpath(__your_path__)
path.close()
path.open()
Hi!
This library seems to do what I want to do, but I can't figure out how to use it, since I don't know what paths are in this case. There is a note on this in the README, and I think a brief explanation on what is actually is would be very helpful.
Thanks!
If one attempts to translate a path and calls .rel
nothing will happen.
testPath = "m 70, 70 l 20, 20 l -20, 0 l 0, -20"
svgpath(testPath).translate(100, 100).toString()
// "M170 170l20 20-20 0 0-20" - as expected
svgpath(testPath).translate(100, 100).rel().toString()
// "m70 70l20 20-20 0 0-20" - now.. that one is not translated
svgpath( svgpath(testPath).rel().toString() ).translate(100,100).toString()
// "M170 170l20 20-20 0 0-20" - as expected
Since I use this script on a "normal web", not node js. ??
It's a small svg editor, which runs 100% in the client's browser.
Something like..
But that obviously doesn't work
Is it possible for svgpath to have the pair of transformed segments returned in an array of objects? For example,
<path d="M650,300L650,150L640,150L640,300" fill="none" stroke="red"></path>
const d = document.querySelector('path').getAttribute('d');
const path = svgpath(d)
.matrix([0.707107, 0.707107, -0.707107, 0.707107, 402.513, 371.751])
.toString();
console.log(path);
The above would return M650.00045 300.00065000000006L756.0664999999999 193.93460000000005 748.9954299999999 186.86353000000008 642.92938 292.9295800000001
.
But I was wondering if there is a method that could return the transformed coordinates in an array of objects like
[{650.00045, 300.00065000000006},{756.0664999999999, 193.93460000000005},{748.9954299999999, 186.86353000000008},{642.92938, 292.9295800000001}]
Hi. First of all, thank you very much for this utility, it's awesome!
In simple-icons we are using svgpath a lot to lint paths. I was wondering if it might be possible to differentiate between a chained relative segment and an isolated one. For example, the set of chained segments l-.014-.076-.01-.04-.012-.048
are parsed as three segments, and there's no way to tell them apart from l-.014-.076l-.01-.04l-.012-.048
. With this functionality we could be able to show more accurate error messages in the linter.
Hi there.
I'm working on a new component for morphing SVGs and was wandering if your script can split segments somehow? Let me explain.
I am currently using some modified code from Raphael.js called path2curve
which converts path commands to cubic-bezier, then replicates some of them in order to make both startShape and endShape have same amount of cubic-bezier points, but that's not always best in terms of visuals.
I need something that can normalize path commands by splitting long segments (in terms of length) into 2/more segments so that they're evenly distributed along both paths, and in the end have same number of points in both shapes.
Is this possible with your library?
First, thanks for your work, it's really awesome.
I've been asking around and nobody seems to have an answer, so I came here to ask, do you think this is possible, achievable?
Thank you
This project is currently unable to run in browser due to the unicode variable names in the a2c.js file. Browsers like the latest chrome are complaining about unknown/unidentified tokens in the source code as a result of this.
Can we rewrite this to plain ol english so this project compiles in browsers?
I have a renderer that understands cubic and quadratic beziers, but not elliptical arcs. It would be convenient if svgpath had a method to convert elliptical arc commands to equivalent cubic bezier commands.
As per the SVG spec with regards to separator characters, commas should be treated as optional:
Superfluous white space and separators such as commas can be eliminated (e.g., "M 100 100 L 200 200" contains unnecessary spaces and could be expressed more compactly as "M100 100L200 200").
SVGPath, however, chokes on a path such as:
M50 0 L10.908425876598514 18.82550990706332, 1.2536043909088193 61.12604669781571, 28.305813044122086 95.04844339512096, 71.6941869558779 95.04844339512096, 98.74639560909117 61.12604669781573, 89.0915741234015 18.82550990706333, 50.000000000000014 0, Z
(The problem seems to be the last part, 0, Z
)
Is there any reason why the comma imposes restrictions on the syntax? One comment reads: // Stop on next segment (but after comma next param is mandatory)
It would be great if one could just scale is using a command line.
I found two type of parse errors that the parser do not detect :
.e3
, it is parsed silently to NaN
.svgpath('M 0 0 0 H 0 .e3 -.e3').toString();
=> 'M0 0H0 NaN NaN'
I just came to push a big rewrite of your library to my repo.
First I want to thank you for your work and very good code.
I slightly renamed my project to SVGPath(y).
The final goal is to create a gh-pages
with a GUI for this library ... some day ;)
e0b1aff#commitcomment-15230210
Seems Z or M have reversed assignement in contourDelta
.
Need to create test for that.
Are there any plans to have support for reversing the path?
const path = svgpath(__your_path__)
const reversed = path.reverse();
Direct Path | Inverse Path |
---|---|
d="M50,300 L50,250 C50,150 75,150 100,250 C150,450 200,450 200,250 Q200,100 400,100" |
d="M400,100 Q200,100 200,250 C200,450 150,450 100,250 C75,150 50,150 50,250 L50,300" |
svg-path-commander
allows for setting the origin of a transformation, and I find it pretty convenient. Perhaps it could be added to svgpath
too?
Add tests & travis-ci
e.g. i have a font's svg path define:
<glyph glyph-name="uni4E38" unicode="丸"
d="M855 1163v-78q0 -222 -31 -409q211 -157 211 -258q0 -64 -53 -64q-30 0 -66 57q-44 70 -116 150q-108 -432 -502 -706l-35 43q345 282 438 759q-107 97 -254 189l27 43q112 -56 241 -139q21 149 21 329v74h-458l-23 51h481v481q203 -3 203 -49q0 -25 -84 -53v-369h436
l76 96q125 -100 125 -127q0 -12.5508 -17 -24l-49 -33v-1054q0 -59 90 -59q97 0 111 61q30 126 43 379l55 -23q-2 -92 -2 -219q0 -108 16 -143q14 -32 54 -29q-3 -93 -64 -117q-50 -20 -207 -20q-138 0 -176 29q-39 23 -39 108v1124h-452z" />
i want to scale the path data, but keep the line 's width (Horizontal and vertical)
Hi there, I'm currently working on a project that needs to use svgpath's scale
and translate
methods, but nothing else. Would you be interested in splitting them into standalone modules? If so, I'm more than happy to assist. :)
https://github.com/fontello/svgpath/blob/2.2.1/lib/a2c.js#L15-L16
2 lines are the same.
Is it possible to partially support transformation units such as deg
, rad
, px
? Currently, transform parameters are converted to numbers which fails with units and results in the parameters being zero.
Here's the snippet from transform_parse.js:
params = item.split(PARAMS_SPLIT_RE).map(function (i) {
return +i || 0;
});
I think a potentially easy fix would be to just strip away the units while potentially converting others:
var UNITS_SPLIT_RE = /\d+/;
var RAD_TO_DEG = 180 / Math.PI;
var TWO_PI = Math.PI * 2;
// ...
params = item.split(PARAMS_SPLIT_RE).map(function (param) {
let value = Number(param.match(UNITS_SPLIT_RE)[0]) || 0;
let unit = param.replace(value, '');
switch (unit) {
case 'rad': {
value = value * RAD_TO_DEG;
break;
}
case 'turn': {
value = value * TWO_PI * RAD_TO_DEG;
break;
}
case 'in': {
value = value * 96;
break;
}
}
return value;
});
The following path M11.467 3.727c.289.189.37.576.181.865l-4.25 6.5a.625.625 0 01-.943.12l-2.75-2.5a.625.625 0 01.84-.925l2.208 2.007 3.849-5.886a.625.625 0 01.865-.181z
gives a parsing error SvgPath: numbers started with '0' such as '09' are ilegal (at pos 59)
while being correctly rendered by browsers.
a.625.625 0 01.84-.925
is supposed to be parsed as a 0.625 0.625 0 0 1 0.84 -0.925
. Notice how 01.
become 0 1 0.
. Instead svgpath parses it as a 0.625 0.625 0 1.84 -0.925
, and so misses 2 params.
How I can create path for multiline text, like below?
<svg width="200" height="200"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<!-- define lines for text lies on -->
<path id="path1" d="M10,30 H190 M10,60 H190 M10,90 H190 M10,120 H190"></path>
</defs>
<use xlink:href="#path1" x="0" y="35" stroke="blue" stroke-width="1" />
<text transform="translate(0,35)" fill="red" font-size="20">
<textPath xlink:href="#path1">This is a long long long text ......</textPath>
</text>
</svg>
https://www.jsdelivr.com/package/npm/svgpath doesn't have any.
I know we can use something like browserify, but it'll be great if there is an official/default one served on platforms like jsdelivr.
I would expect the "unshort" function to also replace occurrences of "V" and "H" with L.
If this is not intended, a sperate function for this would be nice.
I would submit a PR for this if you'd accept it.
Hello, I noticed your README doesn't list how to clone this repository and get the code running locally. Would you be able to share some steps for this?
Hi there,
Are there any plans to have support for calculating bounding boxes, similar to SVGGraphicsElement.getBBox(), except without rendering?
Here is an example:
var path = svgpath(__your_path__);
const {x, y, width, hight} = path.getBbox()
I'm using different library to calculate it for now, SnapSVG, but I would like to use svgpath for everething, because it looks cleaner and smaller.
i wish to have a feature 'non-scaling-stroke' result data to keep lines width while scaling.
thanks.
String parse speed is narrow place in svg2ttf. Try to replace global regex with more "classic" parser.
fontello/fontello#479 (comment)
Probably, issue with a2c()
. To reproduce - drop svg examples to fontello.
I noticed using transform functions in other order different to the spec, the script crashes at line 296, something related to toFixed()
. Here is a regular use case:
var SvgPath = require(svgpath);
var pathData = 'ANY_VALID_PATH_DATA';
var newData = new SvgPath(pathData).scale(1,1.5).translate(0,150).round(3).toString();
Just to be clear, I have some knowledge on SVG, as you will know in a sec, I'm going to try and briefly explain my best what's wrong and hopefully offer a solution.
According to the spec, in short, the SVG coordinate system expects us to compute path/transform coordinates in a specific order, translate, rotate (rotate/skew), scale, despite some people who would argue otherwise. I mean we all want to have full control over these coordinates despite the severe inconsistencies with CSS and other specific issues, we all want to have consistent coordinate values.
Only when working with the transform attribute it's actually recommended to use translate after rotation for instance when you want to rotate a shape around a certain coordinate (transform origin), take for instance rotate the shape around the center of the parent SVG itself.
<svg viewBox="0 0 100 100">
<path d="SOME_VALID_PATH" transform="translate(50,50) rotate(45) translate(-50,-50)">
</svg>
Back to our issue, I would suggest to advise developers with your documentation to always use methods in this order: 1 translate
, 2a rotate
and/or 2b skew
, 3 scale
, 4 round
, 5 toString
.
As for you, I suggest to focus on consistency. Allow the transform methods to be used in any order, but build an object to store transform functions and compute them always in the order I mentioned above. I mean what's the point of using toString/round in the middle?
Have a look at my SVG plugin, see how I apply transform functions in animation and see how consistent looks in the demo. As you can imagine I've updated the code in my development (with transform functions order) and I never gotten any crashes.
Let me know if you like what you see, if I find some time I can give a hand.
Usage example:
var SvgPath = require('svgpath');
SvgPath.cacheSize = 10000;
Since objects will be modified, cache should return cloned copy of segments
(do it fast, via splice)
I know that you follow the W3C where we can read "If the endpoints (x1, y1) and (x2, y2) are identical, then this is equivalent to omitting the elliptical arc segment entirely.".
But this is dangerous because the browsers don't do this. Check the following paths
You can see that the browser treats the empty arc like empty line segment, and don't drop it.
The only situation where dropping arc is dangerous is before short commands (S, s, T,t).
By the way this 'drop empty arcs' rule is inconsistent with the other type of segments. Why to drop empty arcs and not to drop empty lines ?
It's really hard to flip a path if it changes its position in the svg file
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.