I try to parse the expression in a <svg><path d="expression" /></svg>
element. I want to do transformations of the points in JavaScript.
My first step is to parse the path expression. I thought doing this with a RegExp (see below). The code I doesn't behave as I expected. It only finds the last occurrence of each group. What is the best way to make this work? Is it possible with Regexp? If this is the only way RegExp can behave in JavaScript, I suppose I have to match a first regex, then do the rest of the parsing with normal JavaScript. But I would like to first know if it's possible with another RegExp based code. I'd like to learn to use Regexp's better.
let path = `M4,24
L4,23
Q12,23,12, 9
L14, 9
C15, 3,25, 3,26,9
L28,9
Q28,23,36,23
L36,24
Z`
// This function is dysfunctional
function parsePath(path) {
// To simplify the match RegExp, I simplify the path string first:
// All comma's and all whitespace characters are simplified to a simple separator space.
let path2 = path.replaceAll(/[,\s]+/g, ' ')
let rgx = /([A-Za-z])(?:(\d+)\s+)*/g
let matches = [...path2.matchAll(rgx)].map(x => {
return [x[1], x.slice(2)]
})
return matches
}
console.log(parsePath(path));
// OUTPUT:
[
[ 'M', [ '24' ] ],
[ 'L', [ '23' ] ],
[ 'Q', [ '9' ] ],
[ 'L', [ '9' ] ],
[ 'C', [ '9' ] ],
[ 'L', [ '9' ] ],
[ 'Q', [ '23' ] ],
[ 'L', [ '24' ] ],
[ 'Z', [ undefined ] ]
]
// EXPECTED AND DESIRED OUTPUT:
[
[ 'M', [ '4', '24' ] ],
[ 'L', [ '4', '23' ] ],
[ 'Q', [ '12', '23', '12', '9' ] ],
// ...
]
You are repeating a capture group, which gives captures the value of the last iteration.
What you also can omit the the replacement first from comma's and spaces to a single space.
Then change the regex rgx
to match a single char A-Za-z
and capture following digits with a repeated comma and again digits in group 2.
Then split the group 2 value on a comma followed by optional white-space chars ,\s*
([A-Za-z])(\d+(?:,\s*\d+)+)
See the matches on regex 101
(if there can also be optional white-space chars before the comma, then you can change it to \s*,\s*
)
As an example for your input (assuming that the Z
is not matched because are no digits after it)
let path = `M4,24
L4,23
Q12,23,12, 9
L14, 9
C15, 3,25, 3,26,9
L28,9
Q28,23,36,23
L36,24
Z`
function parsePath(path) {
let rgx = /([A-Za-z])(\d+(?:,\s*\d+)+)/g
return [...path.matchAll(rgx)]
.map(x => [x[1], x[2]
.split(/,\s*/)
])
}
console.log(parsePath(path))
Or another way of writing it using Array.from
and having the Z
as undefined if there is no digit following using an optional capture group in the regex:
let path = `M4,24
L4,23
Q12,23,12, 9
L14, 9
C15, 3,25, 3,26,9
L28,9
Q28,23,36,23
L36,24
Z`
const rgx = /([A-Za-z])((?:\d+(?:,\s*\d+)+)?)?/g;
const parsePath = path =>
Array.from(
path.matchAll(rgx),
x => [x[1], x[2] ? x[2].split(/,\s*/) : undefined]
);
console.log(parsePath(path))
Well I done this one...
it accept negative values, return numeric and don't care about aleatory spaces or other [carriage return]s separators.
const
path_d = // from some real apps
document.querySelector('svg path').getAttribute('d')
, path_0 = // from PO question
`M4,24
L4,23
Q12,23,12, 9
L14, 9
C15, 3,25, 3,26,9
L28,9
Q28,23,36,23
L36,24
Z`
, path_1 = // mix space or comma separators ! 20,20 0,0,1 50,30
`M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z`
, path_2 = // a bad one but the syntax is correct...
`M-85.1487,-15.8513
a 22.4171,22.4171 0 1 0 0,31.7026h168.2974 a22.4171,22.4171 0 1 0 0,-31.7026Z
Z`
, pathRegX = /([a-z])(([-\d\s.,]+)?)/ig
, parsePath = path =>
Array.from( path.matchAll(pathRegX),([_,c,x]) =>
[ c, x ? x.split(/[\s,]+/).map(Number) : []] // space OR comma split
);
console.log('path svg :')
parsePath(path_d).forEach(x=>console.log(JSON.stringify(x)));
console.log('\npath Data 0 :')
parsePath(path_0).forEach(x=>console.log(JSON.stringify(x)));
console.log('\npath Data 1 :')
parsePath(path_1).forEach(x=>console.log(JSON.stringify(x)));
console.log('\npath Data 2 :')
parsePath(path_2).forEach(x=>console.log(JSON.stringify(x)));
svg {
width : 100px;
background : lightblue;
stroke : darkblue;
fill : darkblue;
}
.as-console-wrapper { max-height:100% !important; top:0; left:20% !important; width:80%; }
.as-console-row { background-color: #feffb6; }
.as-console-row::after { display:none !important; }
<svg viewBox="0 0 24 24" stroke-linecap="round" stroke-width="1" stroke-linejoin="round" fill="none">
<g>
<path d="M19.0717 19.8211C18.8817 19.8211 18.6917 19.7511 18.5417 19.6011C18.2517 19.3111 18.2517 18.8311 18.5417 18.5411C22.1517 14.9311 22.1517 9.06109 18.5417 5.46109C18.2517 5.17109 18.2517 4.69109 18.5417 4.40109C18.8317 4.11109 19.3117 4.11109 19.6017 4.40109C23.7917 8.59109 23.7917 15.4111 19.6017 19.6011C19.4517 19.7511 19.2617 19.8211 19.0717 19.8211Z" />
<path d="M4.93031 19.8211C4.74031 19.8211 4.55031 19.7511 4.40031 19.6011C0.210312 15.4111 0.210312 8.59109 4.40031 4.40109C4.69031 4.11109 5.17031 4.11109 5.46031 4.40109C5.75031 4.69109 5.75031 5.17109 5.46031 5.46109C1.85031 9.07109 1.85031 14.9411 5.46031 18.5411C5.75031 18.8311 5.75031 19.3111 5.46031 19.6011C5.31031 19.7511 5.12031 19.8211 4.93031 19.8211Z" />
<path d="M11.9988 22.7119C10.7488 22.7019 9.55878 22.5019 8.44878 22.1119C8.05878 21.9719 7.84878 21.5419 7.98878 21.1519C8.12878 20.7619 8.54878 20.5519 8.94878 20.6919C9.90878 21.0219 10.9288 21.2019 12.0088 21.2019C13.0788 21.2019 14.1088 21.0219 15.0588 20.6919C15.4488 20.5619 15.8788 20.7619 16.0188 21.1519C16.1588 21.5419 15.9488 21.9719 15.5588 22.1119C14.4388 22.5019 13.2488 22.7119 11.9988 22.7119Z" />
<path d="M15.2988 3.33906C15.2188 3.33906 15.1288 3.32906 15.0488 3.29906C14.0988 2.95906 13.0688 2.78906 11.9988 2.78906C10.9288 2.78906 9.90878 2.96906 8.94878 3.29906C8.54878 3.42906 8.12878 3.22906 7.98878 2.83906C7.84878 2.44906 8.05878 2.01906 8.44878 1.87906C9.56878 1.48906 10.7588 1.28906 11.9988 1.28906C13.2388 1.28906 14.4388 1.48906 15.5488 1.87906C15.9388 2.01906 16.1488 2.44906 16.0088 2.83906C15.8988 3.14906 15.6088 3.33906 15.2988 3.33906Z" />
<path d="M8.73828 12.0001V10.3301C8.73828 8.25014 10.2083 7.40014 12.0083 8.44014L13.4583 9.28014L14.9083 10.1201C16.7083 11.1601 16.7083 12.8601 14.9083 13.9001L13.4583 14.7401L12.0083 15.5801C10.2083 16.6201 8.73828 15.7701 8.73828 13.6901V12.0001Z" stroke="none" />
</g>
</svg>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With