maker.js icon indicating copy to clipboard operation
maker.js copied to clipboard

SVG Import - some paths fail to import

Open ldstein opened this issue 7 years ago • 11 comments
trafficstars

Hi,

I noticed makerJs sometimes failed to import some SVG paths generated by other libraries.

M 0 0 L 10 0 will import ok. M 0 0 10 0 will fail to import.

The SVG Move Path command spec mentions:

Move the begining of the next subpath to the coordinate x,y. All subsequente pair of coordinates are considered implicite absolute LineTo (L) command.

I suspect the SVG Importer is not handling this part of the spec.

Simple demo below (can copy and paste into https://maker.js.org/playground):

var makerjs = require('makerjs');

// Draw a square from origin 0,0 with sides of 10 units.
// Equivalent to:
// <svg xmlns="http://www.w3.org/2000/svg">
//     <path stroke="red" fill="none" d="M 0 0 L 10 0"/>
//     <path stroke="red" fill="none" d="M 10,0 L 10,10"/>
//     <path stroke="red" fill="none" d="M 10 10 0 10"/>
//     <path stroke="red" fill="none" d="M 0,10 0,0"/>
// </svg>

var svgTestPaths = 
{
    path1: "M 0 0 L 10 0",    // Top    (ok)
    path2: "M 10,0 L 10,10",  // Right  (ok) 
    path3: "M 10 10 0 10",    // Bottom (failed)
    path4: "M 0,10 0,0"       // Left   (failed)
};

this.models = 
{
    path1: makerjs.importer.fromSVGPathData(svgTestPaths.path1),
    path2: makerjs.importer.fromSVGPathData(svgTestPaths.path2),
    path3: makerjs.importer.fromSVGPathData(svgTestPaths.path3),
    path4: makerjs.importer.fromSVGPathData(svgTestPaths.path4)
};

this.notes = 
[
	'# SVG path import test',
	'Draw four sides of a square using various SVG Path syntax. ' + 
    'Note the bottom and left paths fail to import'
].join('\n');

ldstein avatar Jun 29 '18 02:06 ldstein

Wow, good catch. I'm sure that I haven't implemented that part of the spec.

Until I do, do you have a good workaround?

danmarshall avatar Jun 29 '18 02:06 danmarshall

Yep, I've been able to workaround it using https://github.com/jkroso/parse-svg-path

const parseSvgPath = require('parse-svg-path');

function fixPath(path)
{
    var resolvedParts = [];

    parseSvgPath(path).forEach(function(command)
    {
        resolvedParts.push(command.join(' '));
    });

    return resolvedParts.join(' ');
}

ldstein avatar Jun 29 '18 03:06 ldstein

Good to see you have a workaround, as I might not get to the fix until after some other fixes. Thanks for posting! 🥇

danmarshall avatar Jun 29 '18 03:06 danmarshall

No worries, great work on this lib by the way. makerjs.path.expand solved a huge roadblock for me.

ldstein avatar Jun 29 '18 04:06 ldstein

Oh, happy day! Thank you @ldstein, this was driving me nuts. Here's my test case:

const makerjs = require('makerjs');

/* original SVG:

<svg width="60" height="60">
    <path d="M0 30a30 30 0 0 1 60 0 30 30 0 0 1-60 0zm10 0a20 20 0 0 1 40 0 20 20 0 0 1-40 0z" vector-effect="non-scaling-stroke" stroke-linecap="round" fill-rule="evenodd" font-size="12" stroke="#000" stroke-width=".25mm" fill="none"/>
</svg>

*/

const path = "M0 30a30 30 0 0 1 60 0 30 30 0 0 1-60 0zm10 0a20 20 0 0 1 40 0 20 20 0 0 1-40 0z";

let model = makerjs.importer.fromSVGPathData(path);

let svg = makerjs.exporter.toSVG(model);

console.log(svg);

/* output:

<svg width="60" height="30" viewBox="0 0 60 30"><g id="svgGroup" stroke-linecap="round" fill-rule="evenodd" font-size="9pt" stroke="#000" stroke-width="0.25mm" fill="none" style="stroke:#000;stroke-width:0.25mm;fill:none"><path d="M 60 30 A 30 30 0 0 0 0 30 L 60 30 Z M 50 30 A 20 20 0 0 0 10 30 L 50 30 Z" vector-effect="non-scaling-stroke"/></g></svg>

*/

const parseSvgPath = require('parse-svg-path');

function fixPath(path)
{
    var resolvedParts = [];

    parseSvgPath(path).forEach(function(command)
    {
        resolvedParts.push(command.join(' '));
    });

    return resolvedParts.join(' ');
}

const path2 = fixPath(path);

model = makerjs.importer.fromSVGPathData(path2);

svg = makerjs.exporter.toSVG(model);

console.log(svg);

/* output:

<svg width="60" height="60" viewBox="0 0 60 60"><g id="svgGroup" stroke-linecap="round" fill-rule="evenodd" font-size="9pt" stroke="#000" stroke-width="0.25mm" fill="none" style="stroke:#000;stroke-width:0.25mm;fill:none"><path d="M 60 30 A 30 30 0 0 0 0 30 A 30 30 0 0 0 60 30 Z M 50 30 A 20 20 0 0 0 10 30 A 20 20 0 0 0 50 30 Z" vector-effect="non-scaling-stroke"/></g></svg>

*/

original

new

fixed

mrbluecoat avatar Dec 21 '18 00:12 mrbluecoat

If you need more examples... https://github.com/jscad/sample-files

z3dev avatar Dec 21 '18 03:12 z3dev

Thanks for posting your solution @mrbluecoat 👍

danmarshall avatar Dec 21 '18 05:12 danmarshall

I managed to get this working, I'd be happy to submit a pull request

ggoodkey avatar May 27 '22 02:05 ggoodkey

@ggoodkey thank you, a PR would be welcome!

danmarshall avatar May 27 '22 19:05 danmarshall

Seems like the PR was merged but there has not been a recent makerjs release containing it.

rla avatar Nov 24 '22 11:11 rla

@rla thanks for the reminder, 0.17.2 now published.

danmarshall avatar Dec 05 '22 23:12 danmarshall