EmbroidePy / pyembroidery

pyembroidery library for reading and writing a variety of embroidery formats.
MIT License
181 stars 33 forks source link

SVG to PES #125

Open cvick32 opened 3 years ago

cvick32 commented 3 years ago

I think I saw a previous issue where this was shown, but I can't find it again. I remember that it involved some code that @tatarize had previously written for SVGs and loading the paths into a new pattern.

Sorry for the repeat question, but thanks for your help!

tatarize commented 3 years ago

https://stackoverflow.com/questions/57598278/use-pyembroidery-to-convert-vector-file-to-dst-embroidery-file/60424497#60424497

Exists but I think the code is older and based on a broken form of SVG() and .parse I'll have to check. There's also EmbroiderPy/vpype-embroidery that can do this with a couple pip installs though, it somewhat depends on not needing the colors preserved. Since vpype will likely not carry the colors over like the code here might.

tatarize commented 3 years ago

The code posted there works fine. Though with the vpype plugin you can do fills which is somewhat important. I haven't done much more work with that but is also use svgelements to read the files and exports with pyembroidery.

tatarize commented 3 years ago

https://github.com/EmbroidePy/vpype-embroidery

tatarize commented 3 years ago
import glob

from pyembroidery import *
from svgelements import *

for svg_file in glob.glob("*.svg"):
    pattern = EmbPattern()
    for element in SVG.parse(svg_file, transform="scale(%f)" % (254.0 / 96.0)):
        if isinstance(element, Shape):
            element = Path(element)
        if isinstance(element, Path):
            for subpath in element.as_subpaths():
                subpath = Path(subpath)
                distance = subpath.length(error=1e7, min_depth=4)
                segments = int(distance / 20.0) if distance >= 20 else 2
                points = [subpath.point(i / float(segments)) for i in range(int(segments)+1)]
                points = [(p.x, p.y) for p in points]
                pattern.add_block(points, subpath.stroke)
    pattern.write(svg_file + ".dst")
cvick32 commented 3 years ago

So I tried the above code with an svg file, and it's not working. I tried with a simpler svg file and it seemed to work pretty well. I'm not sure what parse() is doing in the above code but it looks like it's turning the more complicated svg file into a list of CubicBezier and Line elements instead of Shape and Path even though paths are written in the original svg file.

I was thinking that CubicBezier and Line are probably in the type Path or Shape but maybe not. Any way around this?

tatarize commented 3 years ago

The code above does a glob and should only require being in the same directory as the svg file. parse() there is parsing the SVG file into rendered objects. If these objects are a Shape it converts that to a Path. Then if it's a path it converts it into a series of single subpaths. It then linearizes all the points which, these segments will be of CubicBezier, Line, QuadraticBezier, Arcs, or Close or Move objects. These are PathSegments and not of type Path or Shape. They are the components which make up paths and shapes.

The complex on has issues since this code generally only does the outlines. vpype-embroidery has some fill abilities but it still couldn't do text-to-path conversions, and while svg-elements processes things like clip-paths these would need to be processed with this bit of conversion code. Mostly this just does outer-path objects of which most of that svg is clip-paths of larger filled objects. Exactly the things that don't get processed there.

Better conversion can be done. Filled paths could be done with things like vpype-embroidery but a lot of that would take a bit more work than the hour or so I might have spent writing that little bit of conversion code. Use shapes rather than clip-paths and text.