meerk40t 0.9.2000__py2.py3-none-any.whl → 0.9.3001__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerk40t/balormk/balor_params.py +1 -43
- meerk40t/balormk/controller.py +1 -41
- meerk40t/balormk/device.py +16 -22
- meerk40t/balormk/driver.py +4 -4
- meerk40t/balormk/gui/balorconfig.py +2 -2
- meerk40t/balormk/gui/balorcontroller.py +13 -5
- meerk40t/balormk/gui/baloroperationproperties.py +0 -46
- meerk40t/balormk/gui/gui.py +17 -17
- meerk40t/camera/gui/camerapanel.py +18 -11
- meerk40t/core/cutcode/rastercut.py +3 -1
- meerk40t/core/cutplan.py +145 -14
- meerk40t/core/elements/clipboard.py +18 -9
- meerk40t/core/elements/element_treeops.py +320 -180
- meerk40t/core/elements/element_types.py +7 -2
- meerk40t/core/elements/elements.py +53 -27
- meerk40t/core/elements/geometry.py +8 -0
- meerk40t/core/elements/offset_clpr.py +129 -4
- meerk40t/core/elements/offset_mk.py +3 -1
- meerk40t/core/elements/shapes.py +28 -25
- meerk40t/core/laserjob.py +7 -0
- meerk40t/core/node/bootstrap.py +4 -0
- meerk40t/core/node/effect_hatch.py +85 -96
- meerk40t/core/node/effect_wobble.py +309 -0
- meerk40t/core/node/elem_image.py +49 -19
- meerk40t/core/node/elem_line.py +60 -0
- meerk40t/core/node/elem_rect.py +5 -3
- meerk40t/core/node/image_processed.py +766 -0
- meerk40t/core/node/image_raster.py +113 -0
- meerk40t/core/node/node.py +120 -1
- meerk40t/core/node/op_cut.py +2 -8
- meerk40t/core/node/op_dots.py +0 -8
- meerk40t/core/node/op_engrave.py +2 -18
- meerk40t/core/node/op_image.py +22 -35
- meerk40t/core/node/op_raster.py +0 -9
- meerk40t/core/planner.py +32 -2
- meerk40t/core/svg_io.py +699 -461
- meerk40t/core/treeop.py +191 -0
- meerk40t/core/undos.py +15 -1
- meerk40t/core/units.py +14 -4
- meerk40t/device/dummydevice.py +3 -2
- meerk40t/device/gui/defaultactions.py +43 -55
- meerk40t/device/gui/formatterpanel.py +58 -49
- meerk40t/device/gui/warningpanel.py +12 -12
- meerk40t/device/mixins.py +13 -0
- meerk40t/dxf/dxf_io.py +9 -5
- meerk40t/extra/ezd.py +28 -26
- meerk40t/extra/imageactions.py +300 -308
- meerk40t/extra/lbrn.py +19 -2
- meerk40t/fill/fills.py +6 -6
- meerk40t/fill/patternfill.py +1061 -1061
- meerk40t/fill/patterns.py +2 -6
- meerk40t/grbl/controller.py +168 -52
- meerk40t/grbl/device.py +23 -18
- meerk40t/grbl/driver.py +39 -0
- meerk40t/grbl/emulator.py +79 -19
- meerk40t/grbl/gcodejob.py +10 -0
- meerk40t/grbl/gui/grblconfiguration.py +2 -2
- meerk40t/grbl/gui/grblcontroller.py +24 -8
- meerk40t/grbl/gui/grblhardwareconfig.py +153 -0
- meerk40t/grbl/gui/gui.py +17 -14
- meerk40t/grbl/mock_connection.py +15 -34
- meerk40t/grbl/plugin.py +0 -4
- meerk40t/grbl/serial_connection.py +2 -1
- meerk40t/gui/about.py +8 -5
- meerk40t/gui/alignment.py +10 -6
- meerk40t/gui/basicops.py +27 -17
- meerk40t/gui/bufferview.py +2 -2
- meerk40t/gui/choicepropertypanel.py +101 -13
- meerk40t/gui/consolepanel.py +12 -9
- meerk40t/gui/devicepanel.py +38 -25
- meerk40t/gui/executejob.py +6 -4
- meerk40t/gui/help_assets/help_assets.py +13 -10
- meerk40t/gui/hersheymanager.py +8 -6
- meerk40t/gui/icons.py +1951 -3065
- meerk40t/gui/imagesplitter.py +14 -7
- meerk40t/gui/keymap.py +3 -3
- meerk40t/gui/laserpanel.py +151 -84
- meerk40t/gui/laserrender.py +61 -70
- meerk40t/gui/lasertoolpanel.py +8 -7
- meerk40t/gui/materialtest.py +3 -3
- meerk40t/gui/mkdebug.py +254 -1
- meerk40t/gui/navigationpanels.py +321 -180
- meerk40t/gui/notes.py +3 -3
- meerk40t/gui/opassignment.py +12 -12
- meerk40t/gui/operation_info.py +13 -13
- meerk40t/gui/plugin.py +5 -0
- meerk40t/gui/position.py +20 -18
- meerk40t/gui/preferences.py +21 -6
- meerk40t/gui/propertypanels/attributes.py +70 -22
- meerk40t/gui/propertypanels/blobproperty.py +2 -2
- meerk40t/gui/propertypanels/consoleproperty.py +2 -2
- meerk40t/gui/propertypanels/groupproperties.py +3 -3
- meerk40t/gui/propertypanels/hatchproperty.py +11 -18
- meerk40t/gui/propertypanels/imageproperty.py +4 -3
- meerk40t/gui/propertypanels/opbranchproperties.py +1 -1
- meerk40t/gui/propertypanels/pathproperty.py +2 -2
- meerk40t/gui/propertypanels/pointproperty.py +2 -2
- meerk40t/gui/propertypanels/propertywindow.py +4 -4
- meerk40t/gui/propertypanels/textproperty.py +3 -3
- meerk40t/gui/propertypanels/wobbleproperty.py +204 -0
- meerk40t/gui/ribbon.py +367 -259
- meerk40t/gui/scene/scene.py +31 -5
- meerk40t/gui/scenewidgets/elementswidget.py +12 -4
- meerk40t/gui/scenewidgets/gridwidget.py +2 -2
- meerk40t/gui/scenewidgets/laserpathwidget.py +7 -2
- meerk40t/gui/scenewidgets/machineoriginwidget.py +6 -2
- meerk40t/gui/scenewidgets/relocatewidget.py +1 -1
- meerk40t/gui/scenewidgets/reticlewidget.py +9 -0
- meerk40t/gui/scenewidgets/selectionwidget.py +12 -7
- meerk40t/gui/simpleui.py +95 -8
- meerk40t/gui/simulation.py +44 -36
- meerk40t/gui/spoolerpanel.py +124 -26
- meerk40t/gui/statusbarwidgets/defaultoperations.py +18 -6
- meerk40t/gui/statusbarwidgets/infowidget.py +2 -2
- meerk40t/gui/statusbarwidgets/opassignwidget.py +12 -12
- meerk40t/gui/statusbarwidgets/shapepropwidget.py +45 -18
- meerk40t/gui/statusbarwidgets/statusbar.py +11 -4
- meerk40t/gui/themes.py +78 -0
- meerk40t/gui/toolwidgets/toolcircle.py +2 -1
- meerk40t/gui/toolwidgets/toolellipse.py +2 -1
- meerk40t/gui/toolwidgets/toolimagecut.py +132 -0
- meerk40t/gui/toolwidgets/toolline.py +144 -0
- meerk40t/gui/toolwidgets/toolnodeedit.py +72 -145
- meerk40t/gui/toolwidgets/toolpoint.py +1 -1
- meerk40t/gui/toolwidgets/toolpolygon.py +8 -55
- meerk40t/gui/toolwidgets/toolrect.py +2 -1
- meerk40t/gui/usbconnect.py +2 -2
- meerk40t/gui/utilitywidgets/cyclocycloidwidget.py +2 -2
- meerk40t/gui/utilitywidgets/harmonograph.py +7 -7
- meerk40t/gui/utilitywidgets/scalewidget.py +1 -1
- meerk40t/gui/wordlisteditor.py +33 -18
- meerk40t/gui/wxmeerk40t.py +166 -66
- meerk40t/gui/wxmmain.py +236 -157
- meerk40t/gui/wxmribbon.py +49 -25
- meerk40t/gui/wxmscene.py +49 -38
- meerk40t/gui/wxmtree.py +216 -85
- meerk40t/gui/wxutils.py +62 -4
- meerk40t/image/imagetools.py +443 -15
- meerk40t/internal_plugins.py +2 -10
- meerk40t/kernel/kernel.py +12 -4
- meerk40t/lihuiyu/controller.py +7 -7
- meerk40t/lihuiyu/device.py +3 -1
- meerk40t/lihuiyu/driver.py +3 -0
- meerk40t/lihuiyu/gui/gui.py +8 -8
- meerk40t/lihuiyu/gui/lhyaccelgui.py +2 -2
- meerk40t/lihuiyu/gui/lhycontrollergui.py +73 -27
- meerk40t/lihuiyu/gui/lhydrivergui.py +2 -2
- meerk40t/lihuiyu/gui/tcpcontroller.py +22 -9
- meerk40t/main.py +6 -1
- meerk40t/moshi/controller.py +5 -5
- meerk40t/moshi/device.py +5 -2
- meerk40t/moshi/driver.py +4 -0
- meerk40t/moshi/gui/gui.py +8 -8
- meerk40t/moshi/gui/moshicontrollergui.py +24 -8
- meerk40t/moshi/gui/moshidrivergui.py +2 -2
- meerk40t/newly/controller.py +2 -0
- meerk40t/newly/device.py +9 -2
- meerk40t/newly/driver.py +4 -0
- meerk40t/newly/gui/gui.py +16 -17
- meerk40t/newly/gui/newlyconfig.py +2 -2
- meerk40t/newly/gui/newlycontroller.py +13 -5
- meerk40t/rotary/gui/gui.py +2 -2
- meerk40t/rotary/gui/rotarysettings.py +2 -2
- meerk40t/ruida/device.py +3 -0
- meerk40t/ruida/driver.py +4 -0
- meerk40t/ruida/gui/gui.py +6 -6
- meerk40t/ruida/gui/ruidaconfig.py +2 -2
- meerk40t/ruida/gui/ruidacontroller.py +13 -5
- meerk40t/svgelements.py +9 -9
- meerk40t/tools/geomstr.py +849 -153
- meerk40t/tools/kerftest.py +8 -4
- meerk40t/tools/livinghinges.py +15 -8
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/METADATA +21 -16
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/RECORD +185 -177
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/entry_points.txt +0 -1
- test/test_core_elements.py +8 -24
- test/test_file_svg.py +88 -0
- test/test_fill.py +9 -9
- test/test_geomstr.py +258 -8
- test/test_kernel.py +4 -0
- test/test_tools_rasterplotter.py +29 -0
- meerk40t/extra/embroider.py +0 -56
- meerk40t/extra/pathoptimize.py +0 -249
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/LICENSE +0 -0
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/WHEEL +0 -0
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/top_level.txt +0 -0
- {meerk40t-0.9.2000.dist-info → meerk40t-0.9.3001.dist-info}/zip-safe +0 -0
meerk40t/core/svg_io.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
"""
|
2
|
-
This
|
2
|
+
This extension governs SVG loading and saving, registering both the load and the save values for SVG.
|
3
3
|
"""
|
4
4
|
|
5
5
|
import ast
|
@@ -122,6 +122,11 @@ MEERK40T_XMLS_ID = "meerk40t"
|
|
122
122
|
|
123
123
|
|
124
124
|
def capstr(linecap):
|
125
|
+
"""
|
126
|
+
Given the mk enum values for linecap, returns the svg string.
|
127
|
+
@param linecap:
|
128
|
+
@return:
|
129
|
+
"""
|
125
130
|
if linecap == Linecap.CAP_BUTT:
|
126
131
|
return "butt"
|
127
132
|
elif linecap == Linecap.CAP_SQUARE:
|
@@ -131,6 +136,12 @@ def capstr(linecap):
|
|
131
136
|
|
132
137
|
|
133
138
|
def joinstr(linejoin):
|
139
|
+
"""
|
140
|
+
Given the mk enum value for linejoin, returns the svg string.
|
141
|
+
|
142
|
+
@param linejoin:
|
143
|
+
@return:
|
144
|
+
"""
|
134
145
|
if linejoin == Linejoin.JOIN_ARCS:
|
135
146
|
return "arcs"
|
136
147
|
elif linejoin == Linejoin.JOIN_BEVEL:
|
@@ -144,19 +155,18 @@ def joinstr(linejoin):
|
|
144
155
|
|
145
156
|
|
146
157
|
def rulestr(fillrule):
|
158
|
+
"""
|
159
|
+
Given the mk enum value for fillrule, returns the svg string.
|
160
|
+
|
161
|
+
@param fillrule:
|
162
|
+
@return:
|
163
|
+
"""
|
147
164
|
if fillrule == Fillrule.FILLRULE_EVENODD:
|
148
165
|
return "evenodd"
|
149
166
|
else:
|
150
167
|
return "nonzero"
|
151
168
|
|
152
169
|
|
153
|
-
def copy_attributes(source, target):
|
154
|
-
if hasattr(source, "stroke"):
|
155
|
-
target.stroke = source.stroke
|
156
|
-
if hasattr(source, "fill"):
|
157
|
-
target.fill = source.fill
|
158
|
-
|
159
|
-
|
160
170
|
class SVGWriter:
|
161
171
|
@staticmethod
|
162
172
|
def save_types():
|
@@ -252,13 +262,11 @@ class SVGWriter:
|
|
252
262
|
return flag
|
253
263
|
|
254
264
|
if c.type == "elem ellipse":
|
255
|
-
element = c.shape
|
256
|
-
copy_attributes(c, element)
|
257
265
|
subelement = SubElement(xml_tree, SVG_TAG_ELLIPSE)
|
258
|
-
subelement.set(SVG_ATTR_CENTER_X, str(
|
259
|
-
subelement.set(SVG_ATTR_CENTER_Y, str(
|
260
|
-
subelement.set(SVG_ATTR_RADIUS_X, str(
|
261
|
-
subelement.set(SVG_ATTR_RADIUS_Y, str(
|
266
|
+
subelement.set(SVG_ATTR_CENTER_X, str(c.cx))
|
267
|
+
subelement.set(SVG_ATTR_CENTER_Y, str(c.cy))
|
268
|
+
subelement.set(SVG_ATTR_RADIUS_X, str(c.rx))
|
269
|
+
subelement.set(SVG_ATTR_RADIUS_Y, str(c.ry))
|
262
270
|
t = Matrix(c.matrix)
|
263
271
|
if not t.is_identity():
|
264
272
|
subelement.set(
|
@@ -266,7 +274,6 @@ class SVGWriter:
|
|
266
274
|
f"matrix({t.a}, {t.b}, {t.c}, {t.d}, {t.e}, {t.f})",
|
267
275
|
)
|
268
276
|
elif c.type == "elem image":
|
269
|
-
element = c.image
|
270
277
|
subelement = SubElement(xml_tree, SVG_TAG_IMAGE)
|
271
278
|
stream = BytesIO()
|
272
279
|
try:
|
@@ -289,23 +296,19 @@ class SVGWriter:
|
|
289
296
|
f"matrix({t.a}, {t.b}, {t.c}, {t.d}, {t.e}, {t.f})",
|
290
297
|
)
|
291
298
|
elif c.type == "elem line":
|
292
|
-
element = c.shape
|
293
|
-
copy_attributes(c, element)
|
294
299
|
subelement = SubElement(xml_tree, SVG_TAG_LINE)
|
295
|
-
subelement.set(SVG_ATTR_X1, str(
|
296
|
-
subelement.set(SVG_ATTR_Y1, str(
|
297
|
-
subelement.set(SVG_ATTR_X2, str(
|
298
|
-
subelement.set(SVG_ATTR_Y2, str(
|
300
|
+
subelement.set(SVG_ATTR_X1, str(c.x1))
|
301
|
+
subelement.set(SVG_ATTR_Y1, str(c.y1))
|
302
|
+
subelement.set(SVG_ATTR_X2, str(c.x2))
|
303
|
+
subelement.set(SVG_ATTR_Y2, str(c.y2))
|
299
304
|
t = c.matrix
|
300
305
|
if not t.is_identity():
|
301
306
|
subelement.set(
|
302
307
|
"transform",
|
303
308
|
f"matrix({t.a}, {t.b}, {t.c}, {t.d}, {t.e}, {t.f})",
|
304
309
|
)
|
305
|
-
|
306
310
|
elif c.type == "elem path":
|
307
|
-
element = c.
|
308
|
-
copy_attributes(c, element)
|
311
|
+
element = c.geometry.as_path()
|
309
312
|
subelement = SubElement(xml_tree, SVG_TAG_PATH)
|
310
313
|
subelement.set(SVG_ATTR_DATA, element.d(transformed=False))
|
311
314
|
t = c.matrix
|
@@ -315,9 +318,6 @@ class SVGWriter:
|
|
315
318
|
f"matrix({t.a}, {t.b}, {t.c}, {t.d}, {t.e}, {t.f})",
|
316
319
|
)
|
317
320
|
elif c.type == "elem point":
|
318
|
-
element = Point(c.point)
|
319
|
-
c.x = element.x
|
320
|
-
c.y = element.y
|
321
321
|
subelement = SubElement(xml_tree, "element")
|
322
322
|
t = c.matrix
|
323
323
|
if not t.is_identity():
|
@@ -325,14 +325,14 @@ class SVGWriter:
|
|
325
325
|
"transform",
|
326
326
|
f"matrix({t.a}, {t.b}, {t.c}, {t.d}, {t.e}, {t.f})",
|
327
327
|
)
|
328
|
-
|
328
|
+
subelement.set("x", str(c.x))
|
329
|
+
subelement.set("y", str(c.y))
|
329
330
|
elif c.type == "elem polyline":
|
330
|
-
element = c.shape
|
331
|
-
copy_attributes(c, element)
|
332
331
|
subelement = SubElement(xml_tree, SVG_TAG_POLYLINE)
|
332
|
+
points = list(c.geometry.as_points())
|
333
333
|
subelement.set(
|
334
334
|
SVG_ATTR_POINTS,
|
335
|
-
" ".join([f"{e
|
335
|
+
" ".join([f"{e.real} {e.imag}" for e in points]),
|
336
336
|
)
|
337
337
|
t = c.matrix
|
338
338
|
if not t.is_identity():
|
@@ -341,15 +341,13 @@ class SVGWriter:
|
|
341
341
|
f"matrix({t.a}, {t.b}, {t.c}, {t.d}, {t.e}, {t.f})",
|
342
342
|
)
|
343
343
|
elif c.type == "elem rect":
|
344
|
-
element = c.shape
|
345
|
-
copy_attributes(c, element)
|
346
344
|
subelement = SubElement(xml_tree, SVG_TAG_RECT)
|
347
|
-
subelement.set(SVG_ATTR_X, str(
|
348
|
-
subelement.set(SVG_ATTR_Y, str(
|
349
|
-
subelement.set(SVG_ATTR_RADIUS_X, str(
|
350
|
-
subelement.set(SVG_ATTR_RADIUS_Y, str(
|
351
|
-
subelement.set(SVG_ATTR_WIDTH, str(
|
352
|
-
subelement.set(SVG_ATTR_HEIGHT, str(
|
345
|
+
subelement.set(SVG_ATTR_X, str(c.x))
|
346
|
+
subelement.set(SVG_ATTR_Y, str(c.y))
|
347
|
+
subelement.set(SVG_ATTR_RADIUS_X, str(c.rx))
|
348
|
+
subelement.set(SVG_ATTR_RADIUS_Y, str(c.ry))
|
349
|
+
subelement.set(SVG_ATTR_WIDTH, str(c.width))
|
350
|
+
subelement.set(SVG_ATTR_HEIGHT, str(c.height))
|
353
351
|
t = c.matrix
|
354
352
|
if not t.is_identity():
|
355
353
|
subelement.set(
|
@@ -405,9 +403,6 @@ class SVGWriter:
|
|
405
403
|
group_element = SubElement(xml_tree, SVG_TAG_GROUP)
|
406
404
|
SVGWriter._write_custom(group_element, c)
|
407
405
|
SVGWriter._write_elements(group_element, c, version)
|
408
|
-
if hasattr(c, "_operands"):
|
409
|
-
for q in c._operands:
|
410
|
-
SVGWriter._write_element(group_element, q, version)
|
411
406
|
return
|
412
407
|
elif c.type == "file":
|
413
408
|
# This is a structural group node of elements. Recurse call to write values.
|
@@ -531,15 +526,11 @@ class SVGWriter:
|
|
531
526
|
Write the operations branch part of the tree to disk.
|
532
527
|
|
533
528
|
@param xml_tree:
|
534
|
-
@param
|
529
|
+
@param op_tree:
|
535
530
|
@return:
|
536
531
|
"""
|
537
532
|
for c in op_tree.children:
|
538
|
-
|
539
|
-
subelement = SubElement(xml_tree, MEERK40T_XMLS_ID + ":operation")
|
540
|
-
SVGWriter._write_custom(subelement, c)
|
541
|
-
else:
|
542
|
-
SVGWriter._write_operation(xml_tree, c)
|
533
|
+
SVGWriter._write_operation(xml_tree, c, version)
|
543
534
|
|
544
535
|
@staticmethod
|
545
536
|
def _write_regmarks(xml_tree, reg_tree, version):
|
@@ -550,7 +541,7 @@ class SVGWriter:
|
|
550
541
|
SVGWriter._write_elements(regmark, reg_tree, version)
|
551
542
|
|
552
543
|
@staticmethod
|
553
|
-
def _write_operation(xml_tree, node):
|
544
|
+
def _write_operation(xml_tree, node, version):
|
554
545
|
"""
|
555
546
|
Write an individual operation. This is any node directly under `branch ops`
|
556
547
|
|
@@ -558,17 +549,16 @@ class SVGWriter:
|
|
558
549
|
@param node:
|
559
550
|
@return:
|
560
551
|
"""
|
561
|
-
|
562
|
-
|
563
|
-
xml_tree = SubElement(xml_tree, SVG_TAG_GROUP)
|
564
|
-
SVGWriter._write_custom(xml_tree, c)
|
565
|
-
|
566
|
-
subelement = SubElement(xml_tree, MEERK40T_XMLS_ID + ":operation")
|
552
|
+
# All operations are groups.
|
553
|
+
subelement = SubElement(xml_tree, SVG_TAG_GROUP)
|
567
554
|
subelement.set("type", str(node.type))
|
555
|
+
|
568
556
|
if node.label is not None:
|
569
557
|
subelement.set("label", str(node.label))
|
558
|
+
|
570
559
|
if node.lock is not None:
|
571
560
|
subelement.set("lock", str(node.lock))
|
561
|
+
|
572
562
|
try:
|
573
563
|
for key, value in node.settings.items():
|
574
564
|
if not key:
|
@@ -583,15 +573,46 @@ class SVGWriter:
|
|
583
573
|
continue
|
584
574
|
subelement.set(key, str(value))
|
585
575
|
except AttributeError:
|
586
|
-
|
576
|
+
# Node does not have settings, write object dict
|
577
|
+
for key, value in node.__dict__.items():
|
578
|
+
if not key:
|
579
|
+
# If key is None, do not save.
|
580
|
+
continue
|
581
|
+
if key.startswith("_"):
|
582
|
+
continue
|
583
|
+
if value is None:
|
584
|
+
continue
|
585
|
+
if key in (
|
586
|
+
"references",
|
587
|
+
"tag",
|
588
|
+
"type",
|
589
|
+
"draw",
|
590
|
+
"stroke_width",
|
591
|
+
"matrix",
|
592
|
+
):
|
593
|
+
# References key from previous loaded version (filter out, rebuild)
|
594
|
+
continue
|
595
|
+
subelement.set(key, str(value))
|
596
|
+
|
597
|
+
# Store current node reference values.
|
598
|
+
SVGWriter._write_references(subelement, node)
|
599
|
+
subelement.set(SVG_ATTR_ID, str(node.id))
|
600
|
+
|
601
|
+
for c in node.children:
|
602
|
+
# Recurse all non-ref nodes
|
603
|
+
if c.type == "reference":
|
604
|
+
continue
|
605
|
+
SVGWriter._write_operation(subelement, c, version)
|
606
|
+
|
607
|
+
@staticmethod
|
608
|
+
def _write_references(subelement, node):
|
587
609
|
contains = list()
|
588
610
|
for c in node.children:
|
589
611
|
if c.type == "reference":
|
590
612
|
c = c.node # Contain direct reference not reference node reference.
|
591
|
-
|
613
|
+
contains.append(c.id)
|
592
614
|
if contains:
|
593
615
|
subelement.set("references", " ".join(contains))
|
594
|
-
subelement.set(SVG_ATTR_ID, str(node.id))
|
595
616
|
|
596
617
|
@staticmethod
|
597
618
|
def _write_custom(subelement, node):
|
@@ -608,15 +629,7 @@ class SVGWriter:
|
|
608
629
|
# References key from previous loaded version (filter out, rebuild)
|
609
630
|
continue
|
610
631
|
subelement.set(key, str(value))
|
611
|
-
|
612
|
-
contains = list()
|
613
|
-
for c in node.children:
|
614
|
-
if c.type == "reference":
|
615
|
-
c = c.node # Contain direct reference not reference node reference.
|
616
|
-
contains.append(c.id)
|
617
|
-
if contains:
|
618
|
-
subelement.set("references", " ".join(contains))
|
619
|
-
|
632
|
+
SVGWriter._write_references(subelement, node)
|
620
633
|
subelement.set(SVG_ATTR_ID, str(node.id))
|
621
634
|
|
622
635
|
@staticmethod
|
@@ -633,17 +646,25 @@ class SVGWriter:
|
|
633
646
|
|
634
647
|
|
635
648
|
class SVGProcessor:
|
649
|
+
"""
|
650
|
+
SVGProcessor is the parser for svg objects. We employ svgelements to do the actual parsing of the file and convert
|
651
|
+
the parsed objects into mk nodes, operations, elements, and regmarks.
|
652
|
+
|
653
|
+
Special care is taken to load MK specific objects like `note` and `operations`
|
654
|
+
"""
|
655
|
+
|
636
656
|
def __init__(self, elements, load_operations):
|
637
657
|
self.elements = elements
|
658
|
+
|
659
|
+
self.operation_list = list()
|
638
660
|
self.element_list = list()
|
639
661
|
self.regmark_list = list()
|
662
|
+
|
640
663
|
self.reverse = False
|
641
664
|
self.requires_classification = True
|
642
665
|
self.operations_replaced = False
|
643
666
|
self.pathname = None
|
644
|
-
self.regmark = None
|
645
667
|
self.load_operations = load_operations
|
646
|
-
self.operation_list = list()
|
647
668
|
|
648
669
|
# Setting this is bringing as much benefit as anticipated
|
649
670
|
# Both the time to load the file (unexpectedly) and the time
|
@@ -658,26 +679,39 @@ class SVGProcessor:
|
|
658
679
|
self.precalc_bbox = True
|
659
680
|
|
660
681
|
def process(self, svg, pathname):
|
682
|
+
"""
|
683
|
+
Process sends the data to parse and deals with creating the file_node, setting the operations, classifying
|
684
|
+
either directly from the data within the file or automatically.
|
685
|
+
|
686
|
+
@param svg:
|
687
|
+
@param pathname:
|
688
|
+
@return:
|
689
|
+
"""
|
661
690
|
self.pathname = pathname
|
662
|
-
|
691
|
+
|
692
|
+
context_node = self.elements.elem_branch
|
663
693
|
file_node = context_node.add(type="file", filepath=pathname)
|
664
|
-
self.regmark = self.elements.reg_branch
|
665
694
|
file_node.focus()
|
666
695
|
|
667
|
-
self.parse(svg, file_node, self.element_list)
|
696
|
+
self.parse(svg, file_node, self.element_list, branch="elements")
|
697
|
+
|
668
698
|
if self.load_operations and self.operations_replaced:
|
699
|
+
for child in list(self.elements.op_branch.children):
|
700
|
+
if not hasattr(child, "_ref_load"):
|
701
|
+
child.remove_all_children(fast=True, destroy=True)
|
702
|
+
child.remove_node(fast=True, destroy=True)
|
669
703
|
self.elements.undo.mark("op-replaced")
|
670
|
-
self.elements.
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
# Some special nodes might lack settings these can't have references.
|
704
|
+
for op in self.elements.op_branch.flat():
|
705
|
+
try:
|
706
|
+
refs = op._ref_load
|
707
|
+
del op._ref_load
|
708
|
+
except AttributeError:
|
676
709
|
continue
|
677
|
-
refs = op.settings.get("references")
|
678
710
|
if refs is None:
|
679
711
|
continue
|
712
|
+
|
680
713
|
self.requires_classification = False
|
714
|
+
|
681
715
|
for ref in refs.split(" "):
|
682
716
|
for e in self.element_list:
|
683
717
|
if e.id == ref:
|
@@ -687,6 +721,14 @@ class SVGProcessor:
|
|
687
721
|
self.elements.classify(self.element_list)
|
688
722
|
|
689
723
|
def check_for_mk_path_attributes(self, node, element):
|
724
|
+
"""
|
725
|
+
Checks for some mk special parameters starting with mk. Especially mkparam, and uses this property to fill in
|
726
|
+
the functional_parameter attribute for the node.
|
727
|
+
|
728
|
+
@param node:
|
729
|
+
@param element:
|
730
|
+
@return:
|
731
|
+
"""
|
690
732
|
for prop in element.values:
|
691
733
|
lc = element.values.get(prop)
|
692
734
|
if prop.startswith("mk"):
|
@@ -701,8 +743,21 @@ class SVGProcessor:
|
|
701
743
|
node.functional_parameter = value
|
702
744
|
except (ValueError, SyntaxError):
|
703
745
|
pass
|
746
|
+
elif prop == "mkbcparam":
|
747
|
+
try:
|
748
|
+
value = ast.literal_eval(lc)
|
749
|
+
node.mkbcparam = value
|
750
|
+
except (ValueError, SyntaxError):
|
751
|
+
pass
|
704
752
|
|
705
753
|
def check_for_fill_attributes(self, node, element):
|
754
|
+
"""
|
755
|
+
Called for paths and poly lines. This checks for an attribute of `fill-rule` in the SVG and sets the MK equal.
|
756
|
+
|
757
|
+
@param node:
|
758
|
+
@param element:
|
759
|
+
@return:
|
760
|
+
"""
|
706
761
|
lc = element.values.get(SVG_ATTR_FILL_RULE)
|
707
762
|
if lc is not None:
|
708
763
|
nlc = Fillrule.FILLRULE_NONZERO
|
@@ -714,6 +769,14 @@ class SVGProcessor:
|
|
714
769
|
node.fillrule = nlc
|
715
770
|
|
716
771
|
def check_for_line_attributes(self, node, element):
|
772
|
+
"""
|
773
|
+
Called for many element types. This checks for the stroke-cap and line-join attributes in the svgelements
|
774
|
+
primitive and sets the node with the mk equal
|
775
|
+
|
776
|
+
@param node:
|
777
|
+
@param element:
|
778
|
+
@return:
|
779
|
+
"""
|
717
780
|
lc = element.values.get(SVG_ATTR_STROKE_CAP)
|
718
781
|
if lc is not None:
|
719
782
|
nlc = Linecap.CAP_ROUND
|
@@ -765,28 +828,18 @@ class SVGProcessor:
|
|
765
828
|
return True, abs(Path(element)).first_point
|
766
829
|
return False, None
|
767
830
|
|
768
|
-
def
|
769
|
-
|
770
|
-
|
771
|
-
return False
|
772
|
-
if candidate is parent_node:
|
773
|
-
return True
|
774
|
-
if candidate.parent is None:
|
775
|
-
return False
|
776
|
-
return is_child(candidate.parent, parent_node)
|
831
|
+
def get_tag_label(self, element):
|
832
|
+
"""
|
833
|
+
Gets the tag label from the element. This is usually an inkscape label.
|
777
834
|
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
835
|
+
Let's see whether we can get the label from an inkscape save
|
836
|
+
We only want the 'label' attribute from the current tag, so
|
837
|
+
we look at element.values["attributes"]
|
838
|
+
|
839
|
+
@param element:
|
840
|
+
@return:
|
841
|
+
"""
|
785
842
|
|
786
|
-
tag_label = None
|
787
|
-
# Let's see whether we can get the label from an inkscape save
|
788
|
-
# We only want the 'label' attribute from the current tag, so
|
789
|
-
# we look at element.values["attributes"]
|
790
843
|
if "attributes" in element.values:
|
791
844
|
local_dict = element.values["attributes"]
|
792
845
|
else:
|
@@ -802,20 +855,502 @@ class SVGProcessor:
|
|
802
855
|
tag_label = local_dict.get(ink_tag)
|
803
856
|
if tag_label == "":
|
804
857
|
tag_label = None
|
805
|
-
# print ("Found label: %s" %_label)
|
806
858
|
except (AttributeError, KeyError):
|
807
859
|
# Label might simply be "label"
|
860
|
+
pass
|
861
|
+
if tag_label is None:
|
808
862
|
tag_label = local_dict.get("label")
|
863
|
+
return tag_label
|
864
|
+
|
865
|
+
def _parse_text(self, element, ident, label, lock, context_node, e_list):
|
866
|
+
"""
|
867
|
+
Parses an SVGText object, into an `elem text` node.
|
868
|
+
|
869
|
+
@param element:
|
870
|
+
@param ident:
|
871
|
+
@param label:
|
872
|
+
@param lock:
|
873
|
+
@param context_node:
|
874
|
+
@param e_list:
|
875
|
+
@return:
|
876
|
+
"""
|
877
|
+
|
878
|
+
if element.text is None:
|
879
|
+
return
|
880
|
+
|
881
|
+
decor = element.values.get("text-decoration", "").lower()
|
882
|
+
node = context_node.add(
|
883
|
+
id=ident,
|
884
|
+
text=element.text,
|
885
|
+
x=element.x,
|
886
|
+
y=element.y,
|
887
|
+
font=element.values.get("font"),
|
888
|
+
anchor=element.values.get(SVG_ATTR_TEXT_ANCHOR),
|
889
|
+
baseline=element.values.get(
|
890
|
+
SVG_ATTR_TEXT_ALIGNMENT_BASELINE,
|
891
|
+
element.values.get(SVG_ATTR_TEXT_DOMINANT_BASELINE, "baseline"),
|
892
|
+
),
|
893
|
+
matrix=element.transform,
|
894
|
+
fill=element.fill,
|
895
|
+
stroke=element.stroke,
|
896
|
+
stroke_width=element.stroke_width,
|
897
|
+
stroke_scale=bool(
|
898
|
+
SVG_VALUE_NON_SCALING_STROKE
|
899
|
+
not in element.values.get(SVG_ATTR_VECTOR_EFFECT, "")
|
900
|
+
),
|
901
|
+
underline="underline" in decor,
|
902
|
+
strikethrough="line-through" in decor,
|
903
|
+
overline="overline" in decor,
|
904
|
+
texttransform=element.values.get("text-transform"),
|
905
|
+
type="elem text",
|
906
|
+
label=label,
|
907
|
+
settings=element.values,
|
908
|
+
)
|
909
|
+
e_list.append(node)
|
910
|
+
|
911
|
+
def _parse_path(self, element, ident, label, lock, context_node, e_list):
|
912
|
+
"""
|
913
|
+
Parses an SVG Path object.
|
914
|
+
|
915
|
+
There were a few versions of meerk40t where Path was used to store other save nodes. But, there is not
|
916
|
+
enough information to reconstruct those elements.
|
917
|
+
|
918
|
+
@param element:
|
919
|
+
@param ident:
|
920
|
+
@param label:
|
921
|
+
@param lock:
|
922
|
+
@param context_node:
|
923
|
+
@param e_list:
|
924
|
+
@return:
|
925
|
+
"""
|
926
|
+
if len(element) < 0:
|
927
|
+
return
|
928
|
+
|
929
|
+
if element.values.get("type") == "elem polyline":
|
930
|
+
# Type is polyline we should restore the node type if we have sufficient info to do so.
|
931
|
+
pass
|
932
|
+
if element.values.get("type") == "elem ellipse":
|
933
|
+
# There is not enough info to reconstruct this.
|
934
|
+
pass
|
935
|
+
if element.values.get("type") == "elem rect":
|
936
|
+
# There is not enough info to reconstruct this.
|
937
|
+
pass
|
938
|
+
if element.values.get("type") == "elem line":
|
939
|
+
pass
|
940
|
+
element.approximate_arcs_with_cubics()
|
941
|
+
node = context_node.add(
|
942
|
+
path=element, type="elem path", id=ident, label=label, lock=lock
|
943
|
+
)
|
944
|
+
self.check_for_line_attributes(node, element)
|
945
|
+
self.check_for_fill_attributes(node, element)
|
946
|
+
self.check_for_mk_path_attributes(node, element)
|
947
|
+
e_list.append(node)
|
948
|
+
|
949
|
+
def _parse_polyline(self, element, ident, label, lock, context_node, e_list):
|
950
|
+
"""
|
951
|
+
Parses svg Polyline and Polygon objects into `elem polyline` nodes.
|
952
|
+
|
953
|
+
@param element:
|
954
|
+
@param ident:
|
955
|
+
@param label:
|
956
|
+
@param lock:
|
957
|
+
@param context_node:
|
958
|
+
@param e_list:
|
959
|
+
@return:
|
960
|
+
"""
|
961
|
+
if element.is_degenerate():
|
962
|
+
return
|
963
|
+
node = context_node.add(
|
964
|
+
shape=element,
|
965
|
+
type="elem polyline",
|
966
|
+
id=ident,
|
967
|
+
label=label,
|
968
|
+
lock=lock,
|
969
|
+
)
|
970
|
+
self.check_for_line_attributes(node, element)
|
971
|
+
self.check_for_fill_attributes(node, element)
|
972
|
+
self.check_for_mk_path_attributes(node, element)
|
973
|
+
if self.precalc_bbox:
|
974
|
+
# bounds will be done here, paintbounds wont...
|
975
|
+
if element.transform.is_identity():
|
976
|
+
points = element.points
|
977
|
+
else:
|
978
|
+
points = list(
|
979
|
+
map(element.transform.point_in_matrix_space, element.points)
|
980
|
+
)
|
981
|
+
xmin = min(p.x for p in points if p is not None)
|
982
|
+
ymin = min(p.y for p in points if p is not None)
|
983
|
+
xmax = max(p.x for p in points if p is not None)
|
984
|
+
ymax = max(p.y for p in points if p is not None)
|
985
|
+
node._bounds = [
|
986
|
+
xmin,
|
987
|
+
ymin,
|
988
|
+
xmax,
|
989
|
+
ymax,
|
990
|
+
]
|
991
|
+
node._bounds_dirty = False
|
992
|
+
node.revalidate_points()
|
993
|
+
node._points_dirty = False
|
994
|
+
e_list.append(node)
|
995
|
+
|
996
|
+
def _parse_ellipse(self, element, ident, label, lock, context_node, e_list):
|
997
|
+
"""
|
998
|
+
Parses the SVG Circle, and Ellipse nodes into `elem ellipse` nodes.
|
999
|
+
|
1000
|
+
@param element:
|
1001
|
+
@param ident:
|
1002
|
+
@param label:
|
1003
|
+
@param lock:
|
1004
|
+
@param context_node:
|
1005
|
+
@param e_list:
|
1006
|
+
@return:
|
1007
|
+
"""
|
1008
|
+
if element.is_degenerate():
|
1009
|
+
return
|
1010
|
+
node = context_node.add(
|
1011
|
+
shape=element,
|
1012
|
+
type="elem ellipse",
|
1013
|
+
id=ident,
|
1014
|
+
label=label,
|
1015
|
+
lock=lock,
|
1016
|
+
)
|
1017
|
+
e_list.append(node)
|
1018
|
+
|
1019
|
+
def _parse_rect(self, element, ident, label, lock, context_node, e_list):
|
1020
|
+
"""
|
1021
|
+
Parse SVG Rect objects into `elem rect` objects.
|
1022
|
+
|
1023
|
+
@param element:
|
1024
|
+
@param ident:
|
1025
|
+
@param label:
|
1026
|
+
@param lock:
|
1027
|
+
@param context_node:
|
1028
|
+
@param e_list:
|
1029
|
+
@return:
|
1030
|
+
"""
|
1031
|
+
if element.is_degenerate():
|
1032
|
+
return
|
1033
|
+
node = context_node.add(
|
1034
|
+
shape=element, type="elem rect", id=ident, label=label, lock=lock
|
1035
|
+
)
|
1036
|
+
self.check_for_line_attributes(node, element)
|
1037
|
+
if self.precalc_bbox:
|
1038
|
+
# bounds will be done here, paintbounds wont...
|
1039
|
+
points = (
|
1040
|
+
Point(element.x, element.y),
|
1041
|
+
Point(element.x + element.width, element.y),
|
1042
|
+
Point(element.x + element.width, element.y + element.height),
|
1043
|
+
Point(element.x, element.y + element.height),
|
1044
|
+
)
|
1045
|
+
if not element.transform.is_identity():
|
1046
|
+
points = list(map(element.transform.point_in_matrix_space, points))
|
1047
|
+
xmin = min(p.x for p in points)
|
1048
|
+
ymin = min(p.y for p in points)
|
1049
|
+
xmax = max(p.x for p in points)
|
1050
|
+
ymax = max(p.y for p in points)
|
1051
|
+
node._bounds = [
|
1052
|
+
xmin,
|
1053
|
+
ymin,
|
1054
|
+
xmax,
|
1055
|
+
ymax,
|
1056
|
+
]
|
1057
|
+
node._bounds_dirty = False
|
1058
|
+
node.revalidate_points()
|
1059
|
+
node._points_dirty = False
|
1060
|
+
e_list.append(node)
|
1061
|
+
|
1062
|
+
def _parse_line(self, element, ident, label, lock, context_node, e_list):
|
1063
|
+
"""
|
1064
|
+
Parse SVG Line objects into `elem line`
|
1065
|
+
|
1066
|
+
@param element:
|
1067
|
+
@param ident:
|
1068
|
+
@param label:
|
1069
|
+
@param lock:
|
1070
|
+
@param context_node:
|
1071
|
+
@param e_list:
|
1072
|
+
@return:
|
1073
|
+
"""
|
1074
|
+
if element.is_degenerate():
|
1075
|
+
return
|
1076
|
+
node = context_node.add(
|
1077
|
+
shape=element, type="elem line", id=ident, label=label, lock=lock
|
1078
|
+
)
|
1079
|
+
self.check_for_line_attributes(node, element)
|
1080
|
+
if self.precalc_bbox:
|
1081
|
+
# bounds will be done here, paintbounds wont...
|
1082
|
+
points = (
|
1083
|
+
Point(element.x1, element.y1),
|
1084
|
+
Point(element.x2, element.y2),
|
1085
|
+
)
|
1086
|
+
if not element.transform.is_identity():
|
1087
|
+
points = list(map(element.transform.point_in_matrix_space, points))
|
1088
|
+
xmin = min(p.x for p in points)
|
1089
|
+
ymin = min(p.y for p in points)
|
1090
|
+
xmax = max(p.x for p in points)
|
1091
|
+
ymax = max(p.y for p in points)
|
1092
|
+
node._bounds = [
|
1093
|
+
xmin,
|
1094
|
+
ymin,
|
1095
|
+
xmax,
|
1096
|
+
ymax,
|
1097
|
+
]
|
1098
|
+
node._bounds_dirty = False
|
1099
|
+
node.revalidate_points()
|
1100
|
+
node._points_dirty = False
|
1101
|
+
e_list.append(node)
|
1102
|
+
|
1103
|
+
def _parse_image(self, element, ident, label, lock, context_node, e_list):
|
1104
|
+
"""
|
1105
|
+
Parse SVG Image objects into either `image raster` or `elem image` objects, potentially other classes.
|
1106
|
+
|
1107
|
+
@param element:
|
1108
|
+
@param ident:
|
1109
|
+
@param label:
|
1110
|
+
@param lock:
|
1111
|
+
@param context_node:
|
1112
|
+
@param e_list:
|
1113
|
+
@return:
|
1114
|
+
"""
|
1115
|
+
try:
|
1116
|
+
element.load(os.path.dirname(self.pathname))
|
1117
|
+
try:
|
1118
|
+
operations = ast.literal_eval(element.values["operations"])
|
1119
|
+
except (ValueError, SyntaxError, KeyError):
|
1120
|
+
operations = None
|
1121
|
+
|
1122
|
+
if element.image is not None:
|
1123
|
+
try:
|
1124
|
+
dpi = element.image.info["dpi"]
|
1125
|
+
except KeyError:
|
1126
|
+
dpi = None
|
1127
|
+
_dpi = 500
|
1128
|
+
if (
|
1129
|
+
isinstance(dpi, tuple)
|
1130
|
+
and len(dpi) >= 2
|
1131
|
+
and dpi[0] != 0
|
1132
|
+
and dpi[1] != 0
|
1133
|
+
):
|
1134
|
+
_dpi = round((float(dpi[0]) + float(dpi[1])) / 2, 0)
|
1135
|
+
_overscan = None
|
1136
|
+
try:
|
1137
|
+
_overscan = str(element.values.get("overscan"))
|
1138
|
+
except (ValueError, TypeError):
|
1139
|
+
pass
|
1140
|
+
_direction = None
|
1141
|
+
try:
|
1142
|
+
_direction = int(element.values.get("direction"))
|
1143
|
+
except (ValueError, TypeError):
|
1144
|
+
pass
|
1145
|
+
_invert = None
|
1146
|
+
try:
|
1147
|
+
_invert = bool(element.values.get("invert") == "True")
|
1148
|
+
except (ValueError, TypeError):
|
1149
|
+
pass
|
1150
|
+
_dither = None
|
1151
|
+
try:
|
1152
|
+
_dither = bool(element.values.get("dither") == "True")
|
1153
|
+
except (ValueError, TypeError):
|
1154
|
+
pass
|
1155
|
+
_dither_type = None
|
1156
|
+
try:
|
1157
|
+
_dither_type = element.values.get("dither_type")
|
1158
|
+
except (ValueError, TypeError):
|
1159
|
+
pass
|
1160
|
+
_red = None
|
1161
|
+
try:
|
1162
|
+
_red = float(element.values.get("red"))
|
1163
|
+
except (ValueError, TypeError):
|
1164
|
+
pass
|
1165
|
+
_green = None
|
1166
|
+
try:
|
1167
|
+
_green = float(element.values.get("green"))
|
1168
|
+
except (ValueError, TypeError):
|
1169
|
+
pass
|
1170
|
+
_blue = None
|
1171
|
+
try:
|
1172
|
+
_blue = float(element.values.get("blue"))
|
1173
|
+
except (ValueError, TypeError):
|
1174
|
+
pass
|
1175
|
+
_lightness = None
|
1176
|
+
try:
|
1177
|
+
_lightness = float(element.values.get("lightness"))
|
1178
|
+
except (ValueError, TypeError):
|
1179
|
+
pass
|
1180
|
+
node = context_node.add(
|
1181
|
+
image=element.image,
|
1182
|
+
matrix=element.transform,
|
1183
|
+
type="elem image",
|
1184
|
+
id=ident,
|
1185
|
+
overscan=_overscan,
|
1186
|
+
direction=_direction,
|
1187
|
+
dpi=_dpi,
|
1188
|
+
invert=_invert,
|
1189
|
+
dither=_dither,
|
1190
|
+
dither_type=_dither_type,
|
1191
|
+
red=_red,
|
1192
|
+
green=_green,
|
1193
|
+
blue=_blue,
|
1194
|
+
lightness=_lightness,
|
1195
|
+
label=label,
|
1196
|
+
operations=operations,
|
1197
|
+
lock=lock,
|
1198
|
+
)
|
1199
|
+
e_list.append(node)
|
1200
|
+
except OSError:
|
1201
|
+
pass
|
1202
|
+
|
1203
|
+
def _parse_element(self, element, ident, label, lock, context_node, e_list):
|
1204
|
+
"""
|
1205
|
+
SVGElement is type. Generic or unknown node type. These nodes do not have children, these are used in
|
1206
|
+
meerk40t contain notes and operations. Element type="elem point", and other points will also load with
|
1207
|
+
this code.
|
1208
|
+
|
1209
|
+
@param element:
|
1210
|
+
@param ident:
|
1211
|
+
@param label:
|
1212
|
+
@param lock:
|
1213
|
+
@param context_node:
|
1214
|
+
@param e_list:
|
1215
|
+
@return:
|
1216
|
+
"""
|
1217
|
+
|
1218
|
+
# Fix: we have mixed capitalisaton in full_ns and tag --> adjust
|
1219
|
+
tag = element.values.get(SVG_ATTR_TAG).lower()
|
1220
|
+
if tag is not None:
|
1221
|
+
# We remove the name space.
|
1222
|
+
full_ns = f"{{{MEERK40T_NAMESPACE.lower()}}}"
|
1223
|
+
if full_ns in tag:
|
1224
|
+
tag = tag.replace(full_ns, "")
|
1225
|
+
|
1226
|
+
# Check if note-type
|
1227
|
+
if tag == "note":
|
1228
|
+
self.elements.note = element.values.get(SVG_TAG_TEXT)
|
1229
|
+
self.elements.signal("note", self.pathname)
|
1230
|
+
return
|
1231
|
+
|
1232
|
+
node_type = element.values.get("type")
|
1233
|
+
if node_type is None:
|
1234
|
+
# Type is not given. Abort.
|
1235
|
+
return
|
1236
|
+
|
1237
|
+
if node_type == "op":
|
1238
|
+
# Meerk40t 0.7.x fallback node types.
|
1239
|
+
op_type = element.values.get("operation")
|
1240
|
+
if op_type is None:
|
1241
|
+
return
|
1242
|
+
node_type = f"op {op_type.lower()}"
|
1243
|
+
element.values["attributes"]["type"] = node_type
|
1244
|
+
|
1245
|
+
node_id = element.values.get("id")
|
1246
|
+
|
1247
|
+
# Get node dictionary.
|
1248
|
+
try:
|
1249
|
+
attrs = element.values["attributes"]
|
1250
|
+
except KeyError:
|
1251
|
+
attrs = element.values
|
1252
|
+
|
1253
|
+
# If type exists in the dictionary, delete it to avoid double attribute issues.
|
1254
|
+
try:
|
1255
|
+
del attrs["type"]
|
1256
|
+
except KeyError:
|
1257
|
+
pass
|
1258
|
+
|
1259
|
+
# Set dictionary types with proper classes.
|
1260
|
+
if "lock" in attrs:
|
1261
|
+
attrs["lock"] = lock
|
1262
|
+
if "transform" in element.values:
|
1263
|
+
# Uses chained transforms from primary context.
|
1264
|
+
attrs["matrix"] = Matrix(element.values["transform"])
|
1265
|
+
if "fill" in attrs:
|
1266
|
+
attrs["fill"] = Color(attrs["fill"])
|
1267
|
+
if "stroke" in attrs:
|
1268
|
+
attrs["stroke"] = Color(attrs["stroke"])
|
1269
|
+
|
1270
|
+
if tag == "operation":
|
1271
|
+
# Operation type node.
|
1272
|
+
if not self.load_operations:
|
1273
|
+
# We don't do that.
|
1274
|
+
return
|
1275
|
+
if not self.operations_replaced:
|
1276
|
+
self.operations_replaced = True
|
1277
|
+
|
1278
|
+
try:
|
1279
|
+
if node_type == "op hatch":
|
1280
|
+
# Special fallback operation, op hatch is an op engrave with an effect hatch within it.
|
1281
|
+
node_type = "op engrave"
|
1282
|
+
op = self.elements.op_branch.add(type=node_type, **attrs)
|
1283
|
+
effect = op.add(type="effect hatch", **attrs)
|
1284
|
+
else:
|
1285
|
+
op = self.elements.op_branch.add(type=node_type, **attrs)
|
1286
|
+
op._ref_load = element.values.get("references")
|
1287
|
+
|
1288
|
+
if op is None or not hasattr(op, "type") or op.type is None:
|
1289
|
+
return
|
1290
|
+
if hasattr(op, "validate"):
|
1291
|
+
op.validate()
|
1292
|
+
|
1293
|
+
op.id = node_id
|
1294
|
+
self.operation_list.append(op)
|
1295
|
+
except AttributeError:
|
1296
|
+
# This operation is invalid.
|
1297
|
+
return
|
1298
|
+
except ValueError:
|
1299
|
+
# This operation type failed to bootstrap.
|
1300
|
+
return
|
1301
|
+
|
1302
|
+
elif tag == "element":
|
1303
|
+
# Check if SVGElement: element
|
1304
|
+
if "settings" in attrs:
|
1305
|
+
del attrs[
|
1306
|
+
"settings"
|
1307
|
+
] # If settings was set, delete it, or it will mess things up
|
1308
|
+
elem = context_node.add(type=node_type, **attrs)
|
1309
|
+
try:
|
1310
|
+
elem.validate()
|
1311
|
+
except AttributeError:
|
1312
|
+
pass
|
1313
|
+
elem.id = node_id
|
1314
|
+
e_list.append(elem)
|
1315
|
+
|
1316
|
+
def parse(self, element, context_node, e_list, branch=None, uselabel=None):
|
1317
|
+
"""
|
1318
|
+
Parse does the bulk of the work. Given an element, here the base case is an SVG itself, we parse such that
|
1319
|
+
any groups will call and check all children recursively, updating the context_node, and passing each element
|
1320
|
+
to this same function.
|
1321
|
+
|
1322
|
+
|
1323
|
+
@param element: Element to parse.
|
1324
|
+
@param context_node: Current context parent we're writing to.
|
1325
|
+
@param e_list: elements list of all the nodes added by this function.
|
1326
|
+
@param branch: Branch we are currently adding elements to.
|
1327
|
+
@param uselabel:
|
1328
|
+
@return:
|
1329
|
+
"""
|
1330
|
+
|
1331
|
+
if element.values.get("visibility") == "hidden":
|
1332
|
+
if branch != "regmarks":
|
1333
|
+
self.parse(
|
1334
|
+
element,
|
1335
|
+
self.elements.reg_branch,
|
1336
|
+
self.regmark_list,
|
1337
|
+
branch="regmarks",
|
1338
|
+
)
|
1339
|
+
return
|
1340
|
+
|
1341
|
+
ident = element.id
|
1342
|
+
|
809
1343
|
if uselabel:
|
810
1344
|
_label = uselabel
|
811
1345
|
else:
|
812
|
-
_label =
|
1346
|
+
_label = self.get_tag_label(element)
|
813
1347
|
|
814
1348
|
_lock = None
|
815
1349
|
try:
|
816
1350
|
_lock = bool(element.values.get("lock") == "True")
|
817
1351
|
except (ValueError, TypeError):
|
818
1352
|
pass
|
1353
|
+
|
819
1354
|
is_dot, dot_point = SVGProcessor.is_dot(element)
|
820
1355
|
if is_dot:
|
821
1356
|
node = context_node.add(
|
@@ -829,392 +1364,95 @@ class SVGProcessor:
|
|
829
1364
|
)
|
830
1365
|
e_list.append(node)
|
831
1366
|
elif isinstance(element, SVGText):
|
832
|
-
|
833
|
-
return
|
834
|
-
|
835
|
-
decor = element.values.get("text-decoration", "").lower()
|
836
|
-
node = context_node.add(
|
837
|
-
id=ident,
|
838
|
-
text=element.text,
|
839
|
-
x=element.x,
|
840
|
-
y=element.y,
|
841
|
-
font=element.values.get("font"),
|
842
|
-
anchor=element.values.get(SVG_ATTR_TEXT_ANCHOR),
|
843
|
-
baseline=element.values.get(
|
844
|
-
SVG_ATTR_TEXT_ALIGNMENT_BASELINE,
|
845
|
-
element.values.get(SVG_ATTR_TEXT_DOMINANT_BASELINE, "baseline"),
|
846
|
-
),
|
847
|
-
matrix=element.transform,
|
848
|
-
fill=element.fill,
|
849
|
-
stroke=element.stroke,
|
850
|
-
stroke_width=element.stroke_width,
|
851
|
-
stroke_scale=bool(
|
852
|
-
SVG_VALUE_NON_SCALING_STROKE
|
853
|
-
not in element.values.get(SVG_ATTR_VECTOR_EFFECT, "")
|
854
|
-
),
|
855
|
-
underline="underline" in decor,
|
856
|
-
strikethrough="line-through" in decor,
|
857
|
-
overline="overline" in decor,
|
858
|
-
texttransform=element.values.get("text-transform"),
|
859
|
-
type="elem text",
|
860
|
-
label=_label,
|
861
|
-
settings=element.values,
|
862
|
-
)
|
863
|
-
e_list.append(node)
|
1367
|
+
self._parse_text(element, ident, _label, _lock, context_node, e_list)
|
864
1368
|
elif isinstance(element, Path):
|
865
|
-
|
866
|
-
if element.values.get("type") == "elem polyline":
|
867
|
-
# Type is polyline we should restore the node type if we have sufficient info to do so.
|
868
|
-
pass
|
869
|
-
if element.values.get("type") == "elem ellipse":
|
870
|
-
# There is not enough info to reconstruct this.
|
871
|
-
pass
|
872
|
-
if element.values.get("type") == "elem rect":
|
873
|
-
# There is not enough info to reconstruct this.
|
874
|
-
pass
|
875
|
-
if element.values.get("type") == "elem line":
|
876
|
-
pass
|
877
|
-
element.approximate_arcs_with_cubics()
|
878
|
-
node = context_node.add(
|
879
|
-
path=element, type="elem path", id=ident, label=_label, lock=_lock
|
880
|
-
)
|
881
|
-
self.check_for_line_attributes(node, element)
|
882
|
-
self.check_for_fill_attributes(node, element)
|
883
|
-
self.check_for_mk_path_attributes(node, element)
|
884
|
-
e_list.append(node)
|
1369
|
+
self._parse_path(element, ident, _label, _lock, context_node, e_list)
|
885
1370
|
elif isinstance(element, (Polygon, Polyline)):
|
886
|
-
|
887
|
-
return
|
888
|
-
node = context_node.add(
|
889
|
-
shape=element,
|
890
|
-
type="elem polyline",
|
891
|
-
id=ident,
|
892
|
-
label=_label,
|
893
|
-
lock=_lock,
|
894
|
-
)
|
895
|
-
self.check_for_line_attributes(node, element)
|
896
|
-
self.check_for_fill_attributes(node, element)
|
897
|
-
self.check_for_mk_path_attributes(node, element)
|
898
|
-
if self.precalc_bbox:
|
899
|
-
# bounds will be done here, paintbounds wont...
|
900
|
-
if element.transform.is_identity():
|
901
|
-
points = element.points
|
902
|
-
else:
|
903
|
-
points = list(
|
904
|
-
map(element.transform.point_in_matrix_space, element.points)
|
905
|
-
)
|
906
|
-
xmin = min(p.x for p in points if p is not None)
|
907
|
-
ymin = min(p.y for p in points if p is not None)
|
908
|
-
xmax = max(p.x for p in points if p is not None)
|
909
|
-
ymax = max(p.y for p in points if p is not None)
|
910
|
-
node._bounds = [
|
911
|
-
xmin,
|
912
|
-
ymin,
|
913
|
-
xmax,
|
914
|
-
ymax,
|
915
|
-
]
|
916
|
-
node._bounds_dirty = False
|
917
|
-
node.revalidate_points()
|
918
|
-
node._points_dirty = False
|
919
|
-
e_list.append(node)
|
1371
|
+
self._parse_polyline(element, ident, _label, _lock, context_node, e_list)
|
920
1372
|
elif isinstance(element, (Circle, Ellipse)):
|
921
|
-
|
922
|
-
return
|
923
|
-
node = context_node.add(
|
924
|
-
shape=element,
|
925
|
-
type="elem ellipse",
|
926
|
-
id=ident,
|
927
|
-
label=_label,
|
928
|
-
lock=_lock,
|
929
|
-
)
|
930
|
-
e_list.append(node)
|
1373
|
+
self._parse_ellipse(element, ident, _label, _lock, context_node, e_list)
|
931
1374
|
elif isinstance(element, Rect):
|
932
|
-
|
933
|
-
return
|
934
|
-
node = context_node.add(
|
935
|
-
shape=element, type="elem rect", id=ident, label=_label, lock=_lock
|
936
|
-
)
|
937
|
-
self.check_for_line_attributes(node, element)
|
938
|
-
if self.precalc_bbox:
|
939
|
-
# bounds will be done here, paintbounds wont...
|
940
|
-
points = (
|
941
|
-
Point(element.x, element.y),
|
942
|
-
Point(element.x + element.width, element.y),
|
943
|
-
Point(element.x + element.width, element.y + element.height),
|
944
|
-
Point(element.x, element.y + element.height),
|
945
|
-
)
|
946
|
-
if not element.transform.is_identity():
|
947
|
-
points = list(map(element.transform.point_in_matrix_space, points))
|
948
|
-
xmin = min(p.x for p in points)
|
949
|
-
ymin = min(p.y for p in points)
|
950
|
-
xmax = max(p.x for p in points)
|
951
|
-
ymax = max(p.y for p in points)
|
952
|
-
node._bounds = [
|
953
|
-
xmin,
|
954
|
-
ymin,
|
955
|
-
xmax,
|
956
|
-
ymax,
|
957
|
-
]
|
958
|
-
node._bounds_dirty = False
|
959
|
-
node.revalidate_points()
|
960
|
-
node._points_dirty = False
|
961
|
-
e_list.append(node)
|
1375
|
+
self._parse_rect(element, ident, _label, _lock, context_node, e_list)
|
962
1376
|
elif isinstance(element, SimpleLine):
|
963
|
-
|
964
|
-
return
|
965
|
-
node = context_node.add(
|
966
|
-
shape=element, type="elem line", id=ident, label=_label, lock=_lock
|
967
|
-
)
|
968
|
-
self.check_for_line_attributes(node, element)
|
969
|
-
if self.precalc_bbox:
|
970
|
-
# bounds will be done here, paintbounds wont...
|
971
|
-
points = (
|
972
|
-
Point(element.x1, element.y1),
|
973
|
-
Point(element.x2, element.y2),
|
974
|
-
)
|
975
|
-
if not element.transform.is_identity():
|
976
|
-
points = list(map(element.transform.point_in_matrix_space, points))
|
977
|
-
xmin = min(p.x for p in points)
|
978
|
-
ymin = min(p.y for p in points)
|
979
|
-
xmax = max(p.x for p in points)
|
980
|
-
ymax = max(p.y for p in points)
|
981
|
-
node._bounds = [
|
982
|
-
xmin,
|
983
|
-
ymin,
|
984
|
-
xmax,
|
985
|
-
ymax,
|
986
|
-
]
|
987
|
-
node._bounds_dirty = False
|
988
|
-
node.revalidate_points()
|
989
|
-
node._points_dirty = False
|
990
|
-
e_list.append(node)
|
1377
|
+
self._parse_line(element, ident, _label, _lock, context_node, e_list)
|
991
1378
|
elif isinstance(element, SVGImage):
|
992
|
-
|
993
|
-
element.load(os.path.dirname(self.pathname))
|
994
|
-
try:
|
995
|
-
operations = ast.literal_eval(element.values["operations"])
|
996
|
-
except (ValueError, SyntaxError, KeyError):
|
997
|
-
operations = None
|
998
|
-
|
999
|
-
if element.image is not None:
|
1000
|
-
try:
|
1001
|
-
dpi = element.image.info["dpi"]
|
1002
|
-
except KeyError:
|
1003
|
-
dpi = None
|
1004
|
-
_dpi = 500
|
1005
|
-
if (
|
1006
|
-
isinstance(dpi, tuple)
|
1007
|
-
and len(dpi) >= 2
|
1008
|
-
and dpi[0] != 0
|
1009
|
-
and dpi[1] != 0
|
1010
|
-
):
|
1011
|
-
_dpi = round((float(dpi[0]) + float(dpi[1])) / 2, 0)
|
1012
|
-
_overscan = None
|
1013
|
-
try:
|
1014
|
-
_overscan = str(element.values.get("overscan"))
|
1015
|
-
except (ValueError, TypeError):
|
1016
|
-
pass
|
1017
|
-
_direction = None
|
1018
|
-
try:
|
1019
|
-
_direction = int(element.values.get("direction"))
|
1020
|
-
except (ValueError, TypeError):
|
1021
|
-
pass
|
1022
|
-
_invert = None
|
1023
|
-
try:
|
1024
|
-
_invert = bool(element.values.get("invert") == "True")
|
1025
|
-
except (ValueError, TypeError):
|
1026
|
-
pass
|
1027
|
-
_dither = None
|
1028
|
-
try:
|
1029
|
-
_dither = bool(element.values.get("dither") == "True")
|
1030
|
-
except (ValueError, TypeError):
|
1031
|
-
pass
|
1032
|
-
_dither_type = None
|
1033
|
-
try:
|
1034
|
-
_dither_type = element.values.get("dither_type")
|
1035
|
-
except (ValueError, TypeError):
|
1036
|
-
pass
|
1037
|
-
_red = None
|
1038
|
-
try:
|
1039
|
-
_red = float(element.values.get("red"))
|
1040
|
-
except (ValueError, TypeError):
|
1041
|
-
pass
|
1042
|
-
_green = None
|
1043
|
-
try:
|
1044
|
-
_green = float(element.values.get("green"))
|
1045
|
-
except (ValueError, TypeError):
|
1046
|
-
pass
|
1047
|
-
_blue = None
|
1048
|
-
try:
|
1049
|
-
_blue = float(element.values.get("blue"))
|
1050
|
-
except (ValueError, TypeError):
|
1051
|
-
pass
|
1052
|
-
_lightness = None
|
1053
|
-
try:
|
1054
|
-
_lightness = float(element.values.get("lightness"))
|
1055
|
-
except (ValueError, TypeError):
|
1056
|
-
pass
|
1057
|
-
node = context_node.add(
|
1058
|
-
image=element.image,
|
1059
|
-
matrix=element.transform,
|
1060
|
-
type="elem image",
|
1061
|
-
id=ident,
|
1062
|
-
overscan=_overscan,
|
1063
|
-
direction=_direction,
|
1064
|
-
dpi=_dpi,
|
1065
|
-
invert=_invert,
|
1066
|
-
dither=_dither,
|
1067
|
-
dither_type=_dither_type,
|
1068
|
-
red=_red,
|
1069
|
-
green=_green,
|
1070
|
-
blue=_blue,
|
1071
|
-
lightness=_lightness,
|
1072
|
-
label=_label,
|
1073
|
-
operations=operations,
|
1074
|
-
lock=_lock,
|
1075
|
-
)
|
1076
|
-
e_list.append(node)
|
1077
|
-
except OSError:
|
1078
|
-
pass
|
1379
|
+
self._parse_image(element, ident, _label, _lock, context_node, e_list)
|
1079
1380
|
elif isinstance(element, SVG):
|
1080
|
-
# SVG is type of group, must
|
1381
|
+
# SVG is type of group, it must be processed before Group. Nothing special is done with the type.
|
1081
1382
|
if self.reverse:
|
1082
1383
|
for child in reversed(element):
|
1083
|
-
self.parse(child, context_node, e_list)
|
1384
|
+
self.parse(child, context_node, e_list, branch=branch)
|
1084
1385
|
else:
|
1085
1386
|
for child in element:
|
1086
|
-
self.parse(child, context_node, e_list)
|
1387
|
+
self.parse(child, context_node, e_list, branch=branch)
|
1087
1388
|
elif isinstance(element, Group):
|
1088
|
-
if _label == "regmarks" or ident == "regmarks":
|
1089
|
-
#
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1389
|
+
if branch != "regmarks" and (_label == "regmarks" or ident == "regmarks"):
|
1390
|
+
# Recurse at same level within regmarks.
|
1391
|
+
self.parse(
|
1392
|
+
element,
|
1393
|
+
self.elements.reg_branch,
|
1394
|
+
self.regmark_list,
|
1395
|
+
branch="regmarks",
|
1396
|
+
)
|
1397
|
+
return
|
1398
|
+
|
1399
|
+
# Load group with specific group attributes (if needed)
|
1400
|
+
e_dict = dict(element.values["attributes"])
|
1401
|
+
e_type = e_dict.get("type", "group")
|
1402
|
+
if branch != "operations" and (
|
1403
|
+
e_type.startswith("op ")
|
1404
|
+
or e_type.startswith("place ")
|
1405
|
+
or e_type.startswith("util ")
|
1406
|
+
):
|
1407
|
+
# This is an operations but we are not in operations context.
|
1408
|
+
if not self.load_operations:
|
1409
|
+
# We don't do that.
|
1410
|
+
return
|
1411
|
+
self.operations_replaced = True
|
1412
|
+
self.parse(
|
1413
|
+
element,
|
1414
|
+
self.elements.op_branch,
|
1415
|
+
self.operation_list,
|
1416
|
+
branch="operations",
|
1417
|
+
)
|
1418
|
+
return
|
1419
|
+
if "stroke" in e_dict:
|
1420
|
+
e_dict["stroke"] = Color(e_dict.get("stroke"))
|
1421
|
+
if "fill" in e_dict:
|
1422
|
+
e_dict["fill"] = Color(e_dict.get("fill"))
|
1423
|
+
for attr in ("type", "id", "label"):
|
1424
|
+
if attr in e_dict:
|
1425
|
+
del e_dict[attr]
|
1426
|
+
context_node = context_node.add(
|
1427
|
+
type=e_type, id=ident, label=_label, **e_dict
|
1428
|
+
)
|
1429
|
+
context_node._ref_load = element.values.get("references")
|
1430
|
+
e_list.append(context_node)
|
1431
|
+
if hasattr(context_node, "validate"):
|
1432
|
+
context_node.validate()
|
1114
1433
|
|
1115
1434
|
# recurse to children
|
1116
1435
|
if self.reverse:
|
1117
1436
|
for child in reversed(element):
|
1118
|
-
self.parse(child, context_node, e_list)
|
1437
|
+
self.parse(child, context_node, e_list, branch=branch)
|
1119
1438
|
else:
|
1120
1439
|
for child in element:
|
1121
|
-
self.parse(child, context_node, e_list)
|
1440
|
+
self.parse(child, context_node, e_list, branch=branch)
|
1122
1441
|
elif isinstance(element, Use):
|
1123
1442
|
# recurse to children, but do not subgroup elements.
|
1124
1443
|
# We still use the original label
|
1125
|
-
|
1126
1444
|
if self.reverse:
|
1127
1445
|
for child in reversed(element):
|
1128
|
-
self.parse(
|
1446
|
+
self.parse(
|
1447
|
+
child, context_node, e_list, branch=branch, uselabel=_label
|
1448
|
+
)
|
1129
1449
|
else:
|
1130
1450
|
for child in element:
|
1131
|
-
self.parse(
|
1451
|
+
self.parse(
|
1452
|
+
child, context_node, e_list, branch=branch, uselabel=_label
|
1453
|
+
)
|
1132
1454
|
else:
|
1133
|
-
|
1134
|
-
# Fix: we have mixed capitalisaton in full_ns and tag --> adjust
|
1135
|
-
tag = element.values.get(SVG_ATTR_TAG).lower()
|
1136
|
-
if tag is not None:
|
1137
|
-
# We remove the name space.
|
1138
|
-
full_ns = f"{{{MEERK40T_NAMESPACE.lower()}}}"
|
1139
|
-
if full_ns in tag:
|
1140
|
-
tag = tag.replace(full_ns, "")
|
1141
|
-
# Check if note-type
|
1142
|
-
if tag == "note":
|
1143
|
-
self.elements.note = element.values.get(SVG_TAG_TEXT)
|
1144
|
-
self.elements.signal("note", self.pathname)
|
1145
|
-
return
|
1146
|
-
node_type = element.values.get("type")
|
1147
|
-
if node_type == "op":
|
1148
|
-
# Meerk40t 0.7.x fallback node types.
|
1149
|
-
op_type = element.values.get("operation")
|
1150
|
-
if op_type is None:
|
1151
|
-
return
|
1152
|
-
node_type = f"op {op_type.lower()}"
|
1153
|
-
element.values["attributes"]["type"] = node_type
|
1154
|
-
|
1155
|
-
if node_type is None:
|
1156
|
-
# Type is not given. Abort.
|
1157
|
-
return
|
1158
|
-
|
1159
|
-
node_id = element.values.get("id")
|
1160
|
-
try:
|
1161
|
-
attrs = element.values["attributes"]
|
1162
|
-
except KeyError:
|
1163
|
-
attrs = element.values
|
1164
|
-
try:
|
1165
|
-
del attrs["type"]
|
1166
|
-
except KeyError:
|
1167
|
-
pass
|
1168
|
-
if "lock" in attrs:
|
1169
|
-
attrs["lock"] = _lock
|
1170
|
-
if "transform" in element.values:
|
1171
|
-
# Uses chained transforms from primary context.
|
1172
|
-
attrs["matrix"] = Matrix(element.values["transform"])
|
1173
|
-
if "fill" in attrs:
|
1174
|
-
attrs["fill"] = Color(attrs["fill"])
|
1175
|
-
if "stroke" in attrs:
|
1176
|
-
attrs["stroke"] = Color(attrs["stroke"])
|
1177
|
-
|
1178
|
-
if self.load_operations and tag == "operation":
|
1179
|
-
# Check if SVGElement: operation
|
1180
|
-
if not self.operations_replaced:
|
1181
|
-
self.operations_replaced = True
|
1182
|
-
|
1183
|
-
try:
|
1184
|
-
if node_type == "op hatch":
|
1185
|
-
# Special fallback operation, op hatch is an op engrave with an effect hatch within it.
|
1186
|
-
node_type = "op engrave"
|
1187
|
-
op = self.elements.op_branch.create(type=node_type, **attrs)
|
1188
|
-
op.add(type="effect hatch", **attrs)
|
1189
|
-
else:
|
1190
|
-
op = self.elements.op_branch.create(type=node_type, **attrs)
|
1191
|
-
if op is None or not hasattr(op, "type") or op.type is None:
|
1192
|
-
return
|
1193
|
-
if hasattr(op, "validate"):
|
1194
|
-
op.validate()
|
1195
|
-
op.id = node_id
|
1196
|
-
if context_node.type.startswith("effect"):
|
1197
|
-
op.append_child(context_node)
|
1198
|
-
self.operation_list.append(op)
|
1199
|
-
except AttributeError:
|
1200
|
-
# This operation is invalid.
|
1201
|
-
return
|
1202
|
-
except ValueError:
|
1203
|
-
# This operation type failed to bootstrap.
|
1204
|
-
return
|
1205
|
-
elif tag == "element":
|
1206
|
-
# Check if SVGElement: element
|
1207
|
-
if "settings" in attrs:
|
1208
|
-
del attrs[
|
1209
|
-
"settings"
|
1210
|
-
] # If settings was set, delete it, or it will mess things up
|
1211
|
-
elem = context_node.add(type=node_type, **attrs)
|
1212
|
-
try:
|
1213
|
-
elem.validate()
|
1214
|
-
except AttributeError:
|
1215
|
-
pass
|
1216
|
-
elem.id = node_id
|
1217
|
-
e_list.append(elem)
|
1455
|
+
self._parse_element(element, ident, _label, _lock, context_node, e_list)
|
1218
1456
|
|
1219
1457
|
|
1220
1458
|
class SVGLoader:
|