summaryrefslogtreecommitdiffstats
path: root/share/extensions/dxf_input.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /share/extensions/dxf_input.py
parentInitial commit. (diff)
downloadinkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz
inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'share/extensions/dxf_input.py')
-rw-r--r--share/extensions/dxf_input.py1636
1 files changed, 1636 insertions, 0 deletions
diff --git a/share/extensions/dxf_input.py b/share/extensions/dxf_input.py
new file mode 100644
index 0000000..34cdd58
--- /dev/null
+++ b/share/extensions/dxf_input.py
@@ -0,0 +1,1636 @@
+#!/usr/bin/env python
+# coding=utf-8
+#
+# Copyright (C) 2008-2009 Alvin Penner, penner@vaxxine.com
+# 2009, Christian Mayer, inkscape@christianmayer.de
+# 2020, MartinOwens, doctormo@geek-2.com
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+"""
+Input a DXF file >= (AutoCAD Release 13 == AC1012)
+"""
+
+import os
+import re
+import sys
+import math
+
+from collections import defaultdict
+from urllib.parse import quote
+from lxml import etree
+
+import inkex
+from inkex.localization import inkex_gettext as _
+
+global defs
+global block # 2021.6
+global layer
+global svg
+global scale
+global xmin
+global ymin
+global height
+global style_font3
+global style_direction
+
+COLORS = [
+ "PAD",
+ "#FF0000",
+ "#FFFF00",
+ "#00FF00",
+ "#00FFFF",
+ "#0000FF",
+ "#FF00FF",
+ "#000000",
+ "#808080",
+ "#C0C0C0",
+ "#FF0000",
+ "#FF7F7F",
+ "#CC0000",
+ "#CC6666",
+ "#990000",
+ "#994C4C",
+ "#7F0000",
+ "#7F3F3F",
+ "#4C0000",
+ "#4C2626",
+ "#FF3F00",
+ "#FF9F7F",
+ "#CC3300",
+ "#CC7F66",
+ "#992600",
+ "#995F4C",
+ "#7F1F00",
+ "#7F4F3F",
+ "#4C1300",
+ "#4C2F26",
+ "#FF7F00",
+ "#FFBF7F",
+ "#CC6600",
+ "#CC9966",
+ "#994C00",
+ "#99724C",
+ "#7F3F00",
+ "#7F5F3F",
+ "#4C2600",
+ "#4C3926",
+ "#FFBF00",
+ "#FFDF7F",
+ "#CC9900",
+ "#CCB266",
+ "#997200",
+ "#99854C",
+ "#7F5F00",
+ "#7F6F3F",
+ "#4C3900",
+ "#4C4226",
+ "#FFFF00",
+ "#FFFF7F",
+ "#CCCC00",
+ "#CCCC66",
+ "#989800",
+ "#98984C",
+ "#7F7F00",
+ "#7F7F3F",
+ "#4C4C00",
+ "#4C4C26",
+ "#BFFF00",
+ "#DFFF7F",
+ "#99CC00",
+ "#B2CC66",
+ "#729800",
+ "#85984C",
+ "#5F7F00",
+ "#6F7F3F",
+ "#394C00",
+ "#424C26",
+ "#7FFF00",
+ "#BFFF7F",
+ "#66CC00",
+ "#99CC66",
+ "#4C9800",
+ "#72984C",
+ "#3F7F00",
+ "#5F7F3F",
+ "#264C00",
+ "#394C26",
+ "#3FFF00",
+ "#9FFF7F",
+ "#33CC00",
+ "#7FCC66",
+ "#269800",
+ "#5F984C",
+ "#1F7F00",
+ "#4F7F3F",
+ "#134C00",
+ "#2F4C26",
+ "#00FF00",
+ "#7FFF7F",
+ "#00CC00",
+ "#66CC66",
+ "#009800",
+ "#4C984C",
+ "#007F00",
+ "#3F7F3F",
+ "#004C00",
+ "#264C26",
+ "#00FF3F",
+ "#7FFF9F",
+ "#00CC33",
+ "#66CC7F",
+ "#009826",
+ "#4C985F",
+ "#007F1F",
+ "#3F7F4F",
+ "#004C13",
+ "#264C2F",
+ "#00FF7F",
+ "#7FFFBF",
+ "#00CC66",
+ "#66CC99",
+ "#00984C",
+ "#4C9872",
+ "#007F3F",
+ "#3F7F5F",
+ "#004C26",
+ "#264C39",
+ "#00FFBF",
+ "#7FFFDF",
+ "#00CC99",
+ "#66CCB2",
+ "#009872",
+ "#4C9885",
+ "#007F5F",
+ "#3F7F6F",
+ "#004C39",
+ "#264C42",
+ "#00FFFF",
+ "#7FFFFF",
+ "#00CCCC",
+ "#66CCCC",
+ "#009898",
+ "#4C9898",
+ "#007F7F",
+ "#3F7F7F",
+ "#004C4C",
+ "#264C4C",
+ "#00BFFF",
+ "#7FDFFF",
+ "#0099CC",
+ "#66B2CC",
+ "#007298",
+ "#4C8598",
+ "#005F7F",
+ "#3F6F7F",
+ "#00394C",
+ "#26424C",
+ "#007FFF",
+ "#7FBFFF",
+ "#0066CC",
+ "#6699CC",
+ "#004C98",
+ "#4C7298",
+ "#003F7F",
+ "#3F5F7F",
+ "#00264C",
+ "#26394C",
+ "#003FFF",
+ "#7F9FFF",
+ "#0033CC",
+ "#667FCC",
+ "#002698",
+ "#4C5F98",
+ "#001F7F",
+ "#3F4F7F",
+ "#00134C",
+ "#262F4C",
+ "#0000FF",
+ "#7F7FFF",
+ "#0000CC",
+ "#6666CC",
+ "#000098",
+ "#4C4C98",
+ "#00007F",
+ "#3F3F7F",
+ "#00004C",
+ "#26264C",
+ "#3F00FF",
+ "#9F7FFF",
+ "#3300CC",
+ "#7F66CC",
+ "#260098",
+ "#5F4C98",
+ "#1F007F",
+ "#4F3F7F",
+ "#13004C",
+ "#2F264C",
+ "#7F00FF",
+ "#BF7FFF",
+ "#6600CC",
+ "#9966CC",
+ "#4C0098",
+ "#724C98",
+ "#3F007F",
+ "#5F3F7F",
+ "#26004C",
+ "#39264C",
+ "#BF00FF",
+ "#DF7FFF",
+ "#9900CC",
+ "#B266CC",
+ "#720098",
+ "#854C98",
+ "#5F007F",
+ "#6F3F7F",
+ "#39004C",
+ "#42264C",
+ "#FF00FF",
+ "#FF7FFF",
+ "#CC00CC",
+ "#CC66CC",
+ "#980098",
+ "#984C98",
+ "#7F007F",
+ "#7F3F7F",
+ "#4C004C",
+ "#4C264C",
+ "#FF00BF",
+ "#FF7FDF",
+ "#CC0099",
+ "#CC66B2",
+ "#980072",
+ "#984C85",
+ "#7F005F",
+ "#7F3F6F",
+ "#4C0039",
+ "#4C2642",
+ "#FF007F",
+ "#FF7FBF",
+ "#CC0066",
+ "#CC6699",
+ "#98004C",
+ "#984C72",
+ "#7F003F",
+ "#7F3F5F",
+ "#4C0026",
+ "#4C2639",
+ "#FF003F",
+ "#FF7F9F",
+ "#CC0033",
+ "#CC667F",
+ "#980026",
+ "#984C5F",
+ "#7F001F",
+ "#7F3F4F",
+ "#4C0013",
+ "#4C262F",
+ "#333333",
+ "#5B5B5B",
+ "#848484",
+ "#ADADAD",
+ "#D6D6D6",
+ "#FFFFFF",
+]
+
+
+def get_rgbcolor(dxfcolor, parent_color="#000000"):
+ """Returns hex color code corresponding to a color value
+
+ dxfcolor -- dxf code to convert to hex color code
+ 0 (BYBLOCK) and 256 (BYLAYER) use parent_color
+ No more differentiation is currently done
+ Negative values are ignored (specification
+ allows layer to be hidden here)
+ Negative values also use parent_color
+ parent_color -- hex color code from parent layer.
+ Use default color '#000000' if
+ parent layer color undefined.
+ """
+ rgbcolor = None
+ if dxfcolor in range(1, len(COLORS)):
+ rgbcolor = COLORS[dxfcolor]
+ if not rgbcolor:
+ rgbcolor = parent_color
+ return rgbcolor
+
+
+class ValueConstruct(defaultdict):
+ """Store values from the DXF and provide them as named attributes"""
+
+ values = {
+ "1": ("text", "default"),
+ "2": ("tag", "block_name"),
+ "3": ("mtext",),
+ "6": ("line_type",),
+ "7": ("text_style",),
+ "8": ("layer_name",),
+ "10": ("x1",),
+ "11": ("x2",),
+ "13": ("x3",),
+ "14": ("x4",),
+ "20": ("y1",),
+ "21": ("y2",),
+ "23": ("y3",),
+ "24": ("y4",),
+ "40": ("scale", "knots", "radius", "width_ratio"),
+ "41": ("ellipse_a1", "insert_scale_x", "mtext_width"),
+ "42": ("ellipse_a2", "bulge", "insert_scale_y"),
+ "50": ("angle",),
+ "51": ("angle2",),
+ "62": ("color",),
+ "70": ("fill", "flags"),
+ "71": ("attach_pos",),
+ "72": ("edge_type",),
+ "73": ("sweep",), # ccw
+ "92": ("path_type",),
+ "93": ("num_edges",),
+ "230": ("extrude",),
+ "370": ("line_weight",),
+ }
+ attrs = dict([(name, a) for a, b in values.items() for name in b])
+
+ def __init__(self):
+ super().__init__(list)
+
+ @classmethod
+ def is_valid(cls, key):
+ return key in cls.values
+
+ def __getattr__(self, attr):
+ is_list = attr.endswith("_list")
+ key = attr[:-5] if is_list else attr
+ if key in self.attrs:
+ ret = self[self.attrs[key]]
+ if not attr.endswith("_list"):
+ return ret[0]
+ return ret
+ if attr.startswith("has_"):
+ key = attr[4:]
+ if key in self.attrs:
+ return self.attrs[key] in self
+ raise AttributeError(f"Can't find dxf attribute '{key}' {attr}")
+
+ def __setattr__(self, attr, value):
+ if not attr in self.attrs:
+ raise AttributeError(f"Can't set bad dxf attribute '{attr}'")
+ if not isinstance(value, list):
+ value = [value]
+ self[self.attrs[attr]] = value
+
+ def adjust_coords(self, xmin, ymin, scale, extrude, height):
+ """Adjust the x,y coordinates to fit on the page"""
+ for xgrp in set(["10", "11", "13", "14"]) & set(self): # scale/reflect x values
+ for i in range(len(self[xgrp])):
+ self[xgrp][i] = scale * (extrude * self[xgrp][i] - xmin)
+ for ygrp in set(["20", "21", "23", "24"]) & set(self): # scale y values
+ for i in range(len(self[ygrp])):
+ self[ygrp][i] = height - scale * (self[ygrp][i] - ymin)
+
+
+export_viewport = False
+export_endsec = False
+
+
+def re_hex2unichar(m):
+ # return unichr(int(m.group(1), 16))
+ return chr(int(m.group(1), 16))
+
+
+def formatStyle(style):
+ return str(inkex.Style(style))
+
+
+def export_text(vals):
+ # mandatory group codes : (11, 12, 72, 73) (fit_x, fit_y, horizon, vertical)
+ # TODO: position to display at by (x2,y2) according to 72(horizon),73(vertical)
+ # groupcode 72:0(left),1(center),2(right),3(both side),4(middle),5(fit)
+ # grouocode 73:0(standard),1(floor),2(center),3(ceiling)
+ vals["71"].append(1) # attach=pos left in mtext
+ vals["70"].append(1) # text: flags=1
+ return export_mtext(vals)
+
+
+def export_mtext(vals):
+ # mandatory group codes : (1 or 3, 10, 20) (text, x, y)
+ # TODO: text-format: \Font; \W; \Q; \L..\l etc
+ if (vals.has_text or vals.has_mtext) and vals.has_x1 and vals.has_y1:
+ x = vals.x1
+ y = vals.y1
+ # optional group codes : (21, 40, 50) (direction, text height mm, text angle)
+ # optional group codes : 2: char style is defined at TABLES Section
+ size = 12 # default fontsize in px
+ if vals.has_scale:
+ size = scale * textscale * vals.scale
+
+ dx = dy = 0
+ if not vals.has_flags: # as mtext, putting in the box
+ dy = size
+
+ anchor = "start"
+ if vals.has_attach_pos:
+ if vals.attach_pos in (2, 5, 8):
+ anchor = "middle"
+ elif vals.attach_pos in (3, 6, 9):
+ anchor = "end"
+ if vals.attach_pos in (4, 5, 6):
+ dy = size / 2
+ # if vals.has_text_style and vals.text_style in style_font3 :
+ # attribs = {'x': '%f' % x, 'y': '%f' % y, 'style': 'font-size: %.3fpx; fill: %s; font-family: %s' \
+ # % (size, color, style_font3[vals.text_style])}
+ # else :
+ # attribs = {'x': '%f' % x, 'y': '%f' % y, 'style': 'font-size: %.3fpx; fill: %s; font-family: %s' % (size, color, options.font)}
+ attribs = {
+ "x": "%f" % x,
+ "y": "%f" % y,
+ "style": "font-size: %.3fpx; fill: %s; font-family: %s; text-anchor: %s"
+ % (size, color, options.font, anchor),
+ }
+
+ angle = 0 # default angle in degrees
+ bVertical = False
+ if vals.has_angle: # TEXT only
+ if vals.angle != 0:
+ angle = vals.angle
+ # attribs.update({'transform': 'rotate (%f %f %f)' % (-angle, x, y)})
+ elif vals.has_y2 and vals.has_x2:
+ # MTEXT
+ # recover original data
+ # (x,y)=(scale*(x-xmin), height-scale*(y-ymin)
+ orgx = vals.x2 / scale + xmin
+ orgy = -(vals.y2 - height) / scale + ymin
+ unit = math.sqrt(orgy * orgy + orgx * orgx)
+ if (unit < 1.01) and (unit > 0.99):
+ ang1 = math.atan2(orgy, orgx)
+ angle = 180 * ang1 / math.pi
+ # attribs.update({'transform': 'rotate (%f %f %f)' % (-angle, x, y)})
+
+ if vals.has_text_style and vals.text_style in style_direction:
+ if style_direction[vals.text_style] & 4:
+ # angle = -90
+ # attribs.update({'transform': 'rotate (%f %f %f)' % (-angle, x, y)})
+ bVertical = True
+ angle = 0
+ dx = size
+ attribs = {
+ "x": "%f" % x,
+ "y": "%f" % y,
+ "style": "font-size: %.3fpx; fill: %s; font-family: %s; text-anchor: %s; writing-mode: tb"
+ % (size, color, options.font, anchor),
+ }
+ if angle != 0:
+ attribs.update({"transform": "rotate (%f %f %f)" % (-angle, x, y)})
+
+ node = layer.add(inkex.TextElement(**attribs))
+ node.set("sodipodi:linespacing", "125%")
+ text = ""
+ if vals.has_mtext:
+ text = "".join(vals.mtext_list)
+ if vals.has_text:
+ text += vals.text
+ # folding long text
+ if vals.has_mtext_width and vals.has_scale:
+ if vals.mtext_width > 10.0 and vals.scale > 1.0:
+ charsperline = vals.mtext_width * 2 / vals.scale # or 1.6?
+ nochars = int(charsperline)
+ if len(text) > charsperline:
+ # plain text only no \P,{} better divide by space
+ if (text.find(r"\P") < 0) and (text.find(r"{") < 0):
+ pos = 0
+ while len(text) > pos:
+ text = text[:pos] + r"\P" + text[pos:]
+ pos += nochars + 2
+
+ text = mtext_normalize(text)
+ lines = 0
+ found = text.find(r"\P") # new line
+ while found > -1:
+ tspan = node.add(inkex.Tspan())
+ if bVertical:
+ tspan.set("y", "%f" % y)
+ if lines > 0:
+ tspan.set("dx", "%f" % size)
+ else:
+ tspan.set("sodipodi:role", "line")
+ if lines > 0:
+ tspan.set("x", x + dx)
+ else:
+ tspan.set("dx", "%f" % dx)
+ tspan.set("dy", "%f" % dy)
+ # tspan.text = text[:found]
+ text1 = text[:found]
+ mtext_separate(node, tspan, text1)
+ text = text[(found + 2) :]
+ found = text.find(r"\P")
+ lines += 1
+
+ tspan = node.add(inkex.Tspan())
+ if bVertical:
+ tspan.set("y", "%f" % y)
+ if lines > 0:
+ tspan.set("dx", "%f" % dx)
+ else:
+ tspan.set("sodipodi:role", "line")
+ if lines > 0:
+ tspan.set("x", x + dx)
+ else:
+ tspan.set("dx", "%f" % dx)
+ tspan.set("dy", "%f" % dy)
+ # tspan.text = text
+ text1 = text
+ mtext_separate(node, tspan, text1)
+
+
+def mtext_normalize(text):
+ # { \P } -> { }\P{ }
+ found = text.find(r"\P")
+ while found > -1:
+ nest = False
+ posL = text.rfind(r"{", 0, found)
+ posR = text.rfind(r"}", 0, found)
+ if posL > -1:
+ if posR == -1:
+ nest = True
+ else:
+ if posL > posR:
+ nest = True
+ if nest:
+ # paste }\P{
+ control = ""
+ if text[posL + 1] == "\\":
+ posC = text.find(r";", posL)
+ if posC != -1 and (posC - posL) < 20:
+ control = text[posL + 1 : posC + 1]
+ text = text[:found] + r"}\P{" + control + text[found + 2 :]
+ found = text.find(r"\P", found + 2)
+ return text
+
+
+def mtext_separate(node, tspan, text):
+ # sparate aaa{bbb}(ccc) -> aaa,bbb.ccc
+ tspanAdd = True
+ found = text.find(r"{")
+ while found > -1:
+ if found == 0:
+ found1 = text.find(r"}")
+ if found1 < 1:
+ break
+ text1 = text[:found1] # tspan
+ text1 = text1[found + 1 :]
+ if tspanAdd == False:
+ tspan = node.add(inkex.Tspan())
+ mtext_ctrl(tspan, text1)
+ # tspan.text = text1 +'+1'
+ tspanAdd = False
+ text = text[found1 + 1 :]
+ found = text.find(r"{")
+ else:
+ text1 = text[:found] # tspan
+ if tspanAdd == False:
+ tspan = node.add(inkex.Tspan())
+ mtext_ctrl(tspan, text1)
+ # tspan.text = text1 +'+2'
+ tspanAdd = False
+ text = text[found:]
+ found = 0
+
+ if len(text) > 0:
+ text1 = text
+ if tspanAdd == False:
+ tspan = node.add(inkex.Tspan())
+ mtext_ctrl(tspan, text1)
+ # tspan.text = text1 +'+3'
+ tspanAdd = False
+
+
+def mtext_ctrl(tspan, phrase):
+ if phrase[0] != "\\":
+ tspan.text = phrase
+ return
+ # if you'll add the function, you should remove the auto re.sub at setting group code:1
+ if phrase[1].upper() in ("C", "H", "T", "Q", "W", "A"):
+ # get the value
+ found = phrase.find(r";")
+ if found > 2:
+ cvalue = phrase[:found]
+ cvalue = cvalue[2:]
+ try:
+ value = float(cvalue)
+ except ValueError:
+ done = False
+ else:
+ done = True
+ if phrase[1].upper() == "C":
+ i = int(value)
+ color = get_rgbcolor(i)
+ tspan.set("style", "stroke: %s" % color)
+ elif phrase[1].upper() == "H":
+ value *= scale
+ tspan.set("style", "font-size: %.3fpx;" % value)
+ elif phrase[1].upper() == "T":
+ tspan.set("style", "letter-spacing: %f;" % value)
+ elif phrase[1].upper() == "A":
+ if value == 0:
+ tspan.set("dominant-baseline", "text-bottom")
+ elif value == 1:
+ tspan.set("dominant-baseline", "central")
+ elif value == 2:
+ tspan.set("dominant-baseline", "text-top")
+ tspan.text = phrase[found + 1 :]
+ else:
+ tspan.text = phrase
+ else:
+ if phrase[1].upper() == "F":
+ # get the value font-name & style & cut from text 2022.March
+ # \FArial|b0|i0|c0|p0; b:bold,i:italic,c:charset,ppitch
+ found = phrase.find(r";")
+ if found > 2:
+ cvalue = phrase[:found]
+ tspan.text = phrase[found + 1 :]
+ else:
+ tspan.text = phrase
+
+
+def export_point(vals, w):
+ # mandatory group codes : (10, 20) (x, y)
+ if vals.has_x1 and vals.has_y1:
+ if vals["70"]:
+ inkex.errormsg("$PDMODE is ignored. A point is displayed as normal.")
+ if options.gcodetoolspoints:
+ generate_gcodetools_point(vals.x1, vals.y1)
+ else:
+ generate_ellipse(vals.x1, vals.y1, w / 2, 0.0, 1.0, 0.0, 0.0)
+
+
+def export_line(vals):
+ """Draw a straight line from the dxf"""
+ # mandatory group codes : (10, 11, 20, 21) (x1, x2, y1, y2)
+ if vals.has_x1 and vals.has_x2 and vals.has_y1 and vals.has_y2:
+ path = inkex.PathElement()
+ path.style = style
+ path.path = "M %f,%f %f,%f" % (vals.x1, vals.y1, vals.x2, vals.y2)
+ layer.add(path)
+
+
+def export_solid(vals):
+ # arrows of dimension
+ # mandatory group codes : (10, 11, 12, 20, 21, 22) (x1, x2, x3, y1, y2, y3)
+ # TODO: 4th point
+ if (
+ vals.has_x1
+ and vals.has_x2
+ and vals.has_x3
+ and vals.has_y1
+ and vals.has_y2
+ and vals.has_y3
+ ):
+ path = inkex.PathElement()
+ path.style = style
+ path.path = "M %f,%f %f,%f %f,%f z" % (
+ vals.x1,
+ vals.y1,
+ vals.x2,
+ vals.y2,
+ vals.x3,
+ vals.y3,
+ )
+ layer.add(path)
+
+
+def export_spline(vals):
+ # see : http://www.mactech.com/articles/develop/issue_25/schneider.html
+ # mandatory group codes : (10, 20, 40, 70) (x[], y[], knots[], flags)
+ if (
+ vals.has_flags
+ and vals.has_knots
+ and vals.x1_list
+ and len(vals.x1_list) == len(vals.y1_list)
+ ):
+ knots = vals.knots_list
+ ctrls = len(vals.x1_list)
+ if ctrls > 3 and len(knots) == ctrls + 4: # cubic
+ if ctrls > 4:
+ for i in range(len(knots) - 5, 3, -1):
+ if knots[i] != knots[i - 1] and knots[i] != knots[i + 1]:
+ a0 = (knots[i] - knots[i - 2]) / (knots[i + 1] - knots[i - 2])
+ a1 = (knots[i] - knots[i - 1]) / (knots[i + 2] - knots[i - 1])
+ vals.x1_list.insert(
+ i - 1,
+ (1.0 - a1) * vals.x1_list[i - 2] + a1 * vals.x1_list[i - 1],
+ )
+ vals.y1_list.insert(
+ i - 1,
+ (1.0 - a1) * vals.y1_list[i - 2] + a1 * vals.y1_list[i - 1],
+ )
+ vals.x1_list[i - 2] = (1.0 - a0) * vals.x1_list[
+ i - 3
+ ] + a0 * vals.x1_list[i - 2]
+ vals.y1_list[i - 2] = (1.0 - a0) * vals.y1_list[
+ i - 3
+ ] + a0 * vals.y1_list[i - 2]
+ knots.insert(i, knots[i])
+ for i in range(len(knots) - 6, 3, -2):
+ if (
+ knots[i] != knots[i + 2]
+ and knots[i - 1] != knots[i + 1]
+ and knots[i - 2] != knots[i]
+ ):
+ a1 = (knots[i] - knots[i - 1]) / (knots[i + 2] - knots[i - 1])
+ vals.x1_list.insert(
+ i - 1,
+ (1.0 - a1) * vals.x1_list[i - 2] + a1 * vals.x1_list[i - 1],
+ )
+ vals.y1_list.insert(
+ i - 1,
+ (1.0 - a1) * vals.y1_list[i - 2] + a1 * vals.y1_list[i - 1],
+ )
+ ctrls = len(vals.x1_list)
+ path = "M %f,%f" % (vals.x1, vals.y1)
+ for i in range(0, (ctrls - 1) // 3):
+ path += " C %f,%f %f,%f %f,%f" % (
+ vals.x1_list[3 * i + 1],
+ vals.y1_list[3 * i + 1],
+ vals.x1_list[3 * i + 2],
+ vals.y1_list[3 * i + 2],
+ vals.x1_list[3 * i + 3],
+ vals.y1_list[3 * i + 3],
+ )
+ if vals.flags & 1: # closed path
+ path += " z"
+ attribs = {"d": path, "style": style}
+ etree.SubElement(layer, "path", attribs)
+ if ctrls == 3 and len(knots) == 6: # quadratic
+ path = "M %f,%f Q %f,%f %f,%f" % (
+ vals.x1,
+ vals.y1,
+ vals.x1_list[1],
+ vals.y1_list[1],
+ vals.x1_list[2],
+ vals.y1_list[2],
+ )
+ attribs = {"d": path, "style": style}
+ etree.SubElement(layer, "path", attribs)
+ if ctrls == 5 and len(knots) == 8: # spliced quadratic
+ path = "M %f,%f Q %f,%f %f,%f Q %f,%f %f,%f" % (
+ vals.x1,
+ vals.y1,
+ vals.x1_list[1],
+ vals.y1_list[1],
+ vals.x1_list[2],
+ vals.y1_list[2],
+ vals.x1_list[3],
+ vals.y1_list[3],
+ vals.x1_list[4],
+ vals.y1_list[4],
+ )
+ attribs = {"d": path, "style": style}
+ etree.SubElement(layer, "path", attribs)
+
+
+def export_circle(vals):
+ # mandatory group codes : (10, 20, 40) (x, y, radius)
+ if vals.has_x1 and vals.has_y1 and vals.has_radius:
+ generate_ellipse(vals.x1, vals.y1, scale * vals.radius, 0.0, 1.0, 0.0, 0.0)
+
+
+def export_arc(vals):
+ # mandatory group codes : (10, 20, 40, 50, 51) (x, y, radius, angle1, angle2)
+ if (
+ vals.has_x1
+ and vals.has_y1
+ and vals.has_radius
+ and vals.has_angle
+ and vals.has_angle2
+ ):
+ generate_ellipse(
+ vals.x1,
+ vals.y1,
+ scale * vals.radius,
+ 0.0,
+ 1.0,
+ vals.angle * math.pi / 180.0,
+ vals.angle2 * math.pi / 180.0,
+ )
+
+
+def export_ellipse(vals):
+ # mandatory group codes : (10, 11, 20, 21, 40, 41, 42) (xc, xm, yc, ym, width ratio, angle1, angle2)
+ if (
+ vals.has_x1
+ and vals.has_x2
+ and vals.has_y1
+ and vals.has_y2
+ and vals.has_width_ratio
+ and vals.has_ellipse_a1
+ and vals.has_ellipse_a2
+ ):
+ # generate_ellipse(vals.x1, vals.y1, scale*vals.x2, scale*vals.y2, vals.width_ratio, vals.ellipse_a1, vals.ellipse_a2)
+ # vals are through adjust_coords : recover proper value
+ # (x,y)=(scale*x-xmin, height-scale*y-ymin)
+ x2 = vals.x2 + xmin
+ y2 = vals.y2 + ymin - height
+ generate_ellipse(
+ vals.x1, vals.y1, x2, y2, vals.width_ratio, vals.ellipse_a1, vals.ellipse_a2
+ )
+
+
+def export_leader(vals):
+ # mandatory group codes : (10, 20) (x, y)
+ if vals.has_x1 and vals.has_y1:
+ if len(vals.x1_list) > 1 and len(vals.y1_list) == len(vals.x1_list):
+ path = "M %f,%f" % (vals.x1, vals.y1)
+ for i in range(1, len(vals.x1_list)):
+ path += " %f,%f" % (vals.x1_list[i], vals.y1_list[i])
+ attribs = {"d": path, "style": style}
+ etree.SubElement(layer, "path", attribs)
+
+
+def export_polyline(vals):
+ return export_lwpolyline(vals)
+
+
+def export_lwpolyline(vals):
+ # mandatory group codes : (10, 20, 70) (x, y, flags)
+ if vals.has_x1 and vals.has_y1 and vals.has_flags:
+ if len(vals.x1_list) > 1 and len(vals.y1_list) == len(vals.x1_list):
+ # optional group codes : (42) (bulge)
+ iseqs = 0
+ ibulge = 0
+ if vals.flags & 1: # closed path
+ seqs.append("20")
+ vals.x1_list.append(vals.x1)
+ vals.y1_list.append(vals.y1)
+ while seqs[iseqs] != "20":
+ iseqs += 1
+ path = "M %f,%f" % (vals.x1, vals.y1)
+ xold = vals.x1
+ yold = vals.y1
+ for i in range(1, len(vals.x1_list)):
+ bulge = 0
+ iseqs += 1
+ while seqs[iseqs] != "20":
+ if seqs[iseqs] == "42":
+ bulge = vals.bulge_list[ibulge]
+ ibulge += 1
+ iseqs += 1
+ if bulge:
+ sweep = 0 # sweep CCW
+ if bulge < 0:
+ sweep = 1 # sweep CW
+ bulge = -bulge
+ large = 0 # large-arc-flag
+ if bulge > 1:
+ large = 1
+ r = math.sqrt(
+ (vals.x1_list[i] - xold) ** 2 + (vals.y1_list[i] - yold) ** 2
+ )
+ r = 0.25 * r * (bulge + 1.0 / bulge)
+ path += " A %f,%f 0.0 %d %d %f,%f" % (
+ r,
+ r,
+ large,
+ sweep,
+ vals.x1_list[i],
+ vals.y1_list[i],
+ )
+ else:
+ path += " L %f,%f" % (vals.x1_list[i], vals.y1_list[i])
+ xold = vals.x1_list[i]
+ yold = vals.y1_list[i]
+ if vals.flags & 1: # closed path
+ path += " z"
+ attribs = {"d": path, "style": style}
+ etree.SubElement(layer, "path", attribs)
+
+
+def export_hatch(vals):
+ # mandatory group codes : (10, 20, 70, 72, 92, 93) (x, y, fill, Edge Type, Path Type, Number of edges)
+ # TODO: Hatching Pattern
+ if (
+ vals.has_x1
+ and vals.has_y1
+ and vals.has_fill
+ and vals.has_edge_type
+ and vals.has_path_type
+ and vals.has_num_edges
+ ):
+ if len(vals.x1_list) > 1 and len(vals.y1_list) == len(vals.x1_list):
+ # optional group codes : (11, 21, 40, 50, 51, 73) (x, y, r, angle1, angle2, CCW)
+ i10 = 1 # count start points
+ i11 = 0 # count line end points
+ i40 = 0 # count circles
+ i72 = 0 # count edge type flags
+ path = ""
+ for i in range(0, len(vals.num_edges_list)):
+ xc = vals.x1_list[i10]
+ yc = vals.y1_list[i10]
+ if vals.edge_type_list[i72] == 2: # arc
+ rm = scale * vals.radius_list[i40]
+ a1 = vals.angle_list[i40]
+ path += "M %f,%f " % (
+ xc + rm * math.cos(a1 * math.pi / 180.0),
+ yc + rm * math.sin(a1 * math.pi / 180.0),
+ )
+ else:
+ a1 = 0
+ path += "M %f,%f " % (xc, yc)
+ for j in range(0, vals.num_edges_list[i]):
+ if vals.path_type_list[i] & 2: # polyline
+ if j > 0:
+ path += "L %f,%f " % (vals.x1_list[i10], vals.y1_list[i10])
+ if j == vals.path_type_list[i] - 1:
+ i72 += 1
+ elif vals.edge_type_list[i72] == 2: # arc
+ xc = vals.x1_list[i10]
+ yc = vals.y1_list[i10]
+ rm = scale * vals.radius_list[i40]
+ a2 = vals.angle2_list[i40]
+ diff = (a2 - a1 + 360) % 360
+ sweep = 1 - vals.sweep_list[i40] # sweep CCW
+ large = 0 # large-arc-flag
+ if diff:
+ path += "A %f,%f 0.0 %d %d %f,%f " % (
+ rm,
+ rm,
+ large,
+ sweep,
+ xc + rm * math.cos(a2 * math.pi / 180.0),
+ yc + rm * math.sin(a2 * math.pi / 180.0),
+ )
+ else:
+ path += "A %f,%f 0.0 %d %d %f,%f " % (
+ rm,
+ rm,
+ large,
+ sweep,
+ xc + rm * math.cos((a1 + 180.0) * math.pi / 180.0),
+ yc + rm * math.sin((a1 + 180.0) * math.pi / 180.0),
+ )
+ path += "A %f,%f 0.0 %d %d %f,%f " % (
+ rm,
+ rm,
+ large,
+ sweep,
+ xc + rm * math.cos(a1 * math.pi / 180.0),
+ yc + rm * math.sin(a1 * math.pi / 180.0),
+ )
+ i40 += 1
+ i72 += 1
+ elif vals.edge_type_list[i72] == 1: # line
+ path += "L %f,%f " % (vals.x2_list[i11], vals.y2_list[i11])
+ i11 += 1
+ i72 += 1
+ i10 += 1
+ path += "z "
+ if vals.has_fill:
+ style = formatStyle({"fill": "%s" % color})
+ else:
+ style = formatStyle({"fill": "url(#Hatch)", "fill-opacity": "1.0"})
+ attribs = {"d": path, "style": style}
+ etree.SubElement(layer, "path", attribs)
+
+
+def export_dimension(vals):
+ # mandatory group codes : (10, 11, 13, 14, 20, 21, 23, 24) (x1..4, y1..4)
+ # block_name: dimension definition for 10mm
+ if vals.has_x1 and vals.has_x2 and vals.has_y1 and vals.has_y2:
+
+ if vals.has_block_name:
+ attribs = {
+ inkex.addNS("href", "xlink"): "#%s" % (vals.block_name)
+ } # not use quote because name *D2 etc. changed to %2AD2
+ tform = "translate(0 0)"
+ # if vals.has_angle :
+ # tform += ' rotate(%f,%f,%f)' % (vals.angle,vals.x4,vals.y4)
+ attribs.update({"transform": tform})
+ etree.SubElement(layer, "use", attribs)
+ else:
+ # TODO: improve logic when INSERT in BLOCK
+ dx = abs(vals.x1 - vals.x3)
+ dy = abs(vals.y1 - vals.y3)
+ if (vals.x1 == vals.x4) and dx > 0.00001:
+ d = dx / scale
+ dy = 0
+ path = "M %f,%f %f,%f" % (vals.x1, vals.y1, vals.x3, vals.y1)
+ elif (vals.y1 == vals.y4) and dy > 0.00001:
+ d = dy / scale
+ dx = 0
+ path = "M %f,%f %f,%f" % (vals.x1, vals.y1, vals.x1, vals.y3)
+ else:
+ return
+ attribs = {
+ "d": path,
+ "style": style
+ + "; marker-start: url(#DistanceX); marker-end: url(#DistanceX); stroke-width: 0.25px",
+ }
+ etree.SubElement(layer, "path", attribs)
+ x = vals.x2
+ y = vals.y2
+ size = 12 # default fontsize in px
+ if vals.has_mtext:
+ if vals.mtext in DIMTXT:
+ size = scale * textscale * DIMTXT[vals.mtext]
+ if size < 2:
+ size = 2
+ attribs = {
+ "x": "%f" % x,
+ "y": "%f" % y,
+ "style": "font-size: %.3fpx; fill: %s; font-family: %s; text-anchor: middle; text-align: center"
+ % (size, color, options.font),
+ }
+ if dx == 0:
+ attribs.update({"transform": "rotate (%f %f %f)" % (-90, x, y)})
+ node = etree.SubElement(layer, "text", attribs)
+ tspan = node.add(inkex.Tspan())
+ tspan.set("sodipodi:role", "line")
+ tspan.text = str(float("%.2f" % d))
+
+
+def export_insert(vals):
+ # mandatory group codes : (2, 10, 20) (block name, x, y)
+ # TODO: repeat by row and column
+ # (times,interval)= row(70,44), column(71,45)
+
+ if vals.has_block_name and vals.has_x1 and vals.has_y1:
+ # vals are through adjust_coords except block
+ # block (x,y)=(0,0) : (scale*x-xmin, height-scale*y-ymin)
+ # translate(move x units,move y units)
+ # 2021.6 translate..ok scale..x rotate X
+ # as scale, the line is wider ->same width -> you should fix
+ global height
+ cx = scale * xmin # transorm-origin:
+ cy = scale * ymin + height # center of rotation
+
+ x = vals.x1 + scale * xmin
+ y = vals.y1 - scale * ymin - height
+ ixscale = iyscale = 1
+ if vals.has_insert_scale_y:
+ ixscale = vals.insert_scale_x
+ if vals.has_insert_scale_y:
+ iyscale = vals.insert_scale_y
+ x += cx * (iyscale - 1)
+ y -= cy * (iyscale - 1)
+
+ elem = layer.add(inkex.Use())
+ elem.set(
+ inkex.addNS("href", "xlink"),
+ "#" + quote(vals.block_name.replace(" ", "_").encode("utf-8")),
+ )
+
+ # add style stroke-width=1px for reducing thick line
+ fwide = abs(0.5 / ixscale) # better to use w/ixscale
+ elem.style["stroke-width"] = "%.3fpx" % fwide
+
+ elem.transform.add_translate(x, y)
+ if vals.has_insert_scale_x and vals.has_insert_scale_y:
+ elem.transform.add_scale(ixscale, iyscale)
+ if vals.has_angle:
+ rotated_angle = vals.angle
+ if ixscale * iyscale > 0:
+ rotated_angle = 360 - rotated_angle
+ elem.transform.add_rotate(rotated_angle, -cx, cy)
+
+
+def export_block(vals):
+ # mandatory group codes : (2) (block name)
+ if vals.has_block_name:
+ global block
+ block = etree.SubElement(
+ defs, "symbol", {"id": vals.block_name.replace(" ", "_")}
+ )
+
+
+def export_endblk(vals):
+ global block
+ block = defs # initiallize with dummy
+
+
+def export_attdef(vals):
+ # mandatory group codes : (1, 2) (default, tag)
+ if vals.has_default and vals.has_tag:
+ vals.text_list.append(vals.tag)
+ export_mtext(vals)
+
+
+def generate_ellipse(xc, yc, xm, ym, w, a1, a2):
+ rm = math.sqrt(xm * xm + ym * ym)
+ a = -math.atan2(ym, xm) # x-axis-rotation
+ diff = (a2 - a1 + 2 * math.pi) % (2 * math.pi)
+ if abs(diff) > 0.0000001 and abs(diff - 2 * math.pi) > 0.0000001: # open arc
+ large = 0 # large-arc-flag
+ if diff > math.pi:
+ large = 1
+ xt = rm * math.cos(a1)
+ yt = w * rm * math.sin(a1)
+ x1 = xt * math.cos(a) - yt * math.sin(a)
+ y1 = xt * math.sin(a) + yt * math.cos(a)
+ xt = rm * math.cos(a2)
+ yt = w * rm * math.sin(a2)
+ x2 = xt * math.cos(a) - yt * math.sin(a)
+ y2 = xt * math.sin(a) + yt * math.cos(a)
+ path = "M %f,%f A %f,%f %f %d 0 %f,%f" % (
+ xc + x1,
+ yc - y1,
+ rm,
+ w * rm,
+ -180.0 * a / math.pi,
+ large,
+ xc + x2,
+ yc - y2,
+ )
+ else: # closed arc
+ path = "M %f,%f A %f,%f %f 0, 0 %f,%f A %f,%f %f 0, 0 %f,%f z" % (
+ xc + xm,
+ yc - ym,
+ rm,
+ w * rm,
+ -180.0 * a / math.pi,
+ xc - xm,
+ yc + ym,
+ rm,
+ w * rm,
+ -180.0 * a / math.pi,
+ xc + xm,
+ yc - ym,
+ )
+ attribs = {"d": path, "style": style}
+ etree.SubElement(layer, "path", attribs)
+
+
+def generate_gcodetools_point(xc, yc):
+ elem = layer.add(inkex.PathElement())
+ elem.style = "stroke:none;fill:#ff0000"
+ elem.set("inkscape:dxfpoint", "1")
+ elem.path = (
+ "m %s,%s 2.9375,-6.34375 0.8125,1.90625 6.84375,-6.84375 0,0 0.6875,0.6875 -6.84375,6.84375 1.90625,0.8125 z"
+ % (xc, yc)
+ )
+
+
+# define DXF Entities and specify which Group Codes to monitor
+
+
+class DxfInput(inkex.InputExtension):
+ def add_arguments(self, pars):
+ pars.add_argument("--tab", default="options")
+ pars.add_argument("--scalemethod", default="manual")
+ pars.add_argument("--scale", default="1.0")
+ pars.add_argument("--textscale", default="1.0")
+ pars.add_argument("--xmin", default="0.0")
+ pars.add_argument("--ymin", default="0.0")
+ pars.add_argument("--gcodetoolspoints", default=False, type=inkex.Boolean)
+ pars.add_argument("--encoding", dest="input_encode", default="latin_1")
+ pars.add_argument("--font", default="Arial")
+
+ def load(self, stream):
+ return stream
+
+ def effect(self):
+ global options
+ global defs
+ global entity
+ global seqs
+ global style
+ global layer
+ global scale
+ global textscale
+ global color
+ global extrude
+ global xmin
+ global ymin
+ global height
+ global DIMTXT
+ global block
+ global svg
+ global style_font3
+ global style_direction
+ global be_extrude
+
+ options = self.options
+
+ doc = self.get_template(width=210 * 96 / 25.4, height=297 * 96 / 25.4)
+ svg = doc.getroot()
+ defs = svg.defs
+ marker = etree.SubElement(
+ defs,
+ "marker",
+ {
+ "id": "DistanceX",
+ "orient": "auto",
+ "refX": "0.0",
+ "refY": "0.0",
+ "style": "overflow:visible",
+ },
+ )
+ etree.SubElement(
+ marker,
+ "path",
+ {
+ "d": "M 3,-3 L -3,3 M 0,-5 L 0,5",
+ "style": "stroke:#000000; stroke-width:0.5",
+ },
+ )
+ pattern = etree.SubElement(
+ defs,
+ "pattern",
+ {
+ "id": "Hatch",
+ "patternUnits": "userSpaceOnUse",
+ "width": "8",
+ "height": "8",
+ "x": "0",
+ "y": "0",
+ },
+ )
+ etree.SubElement(
+ pattern,
+ "path",
+ {
+ "d": "M8 4 l-4,4",
+ "stroke": "#000000",
+ "stroke-width": "0.25",
+ "linecap": "square",
+ },
+ )
+ etree.SubElement(
+ pattern,
+ "path",
+ {
+ "d": "M6 2 l-4,4",
+ "stroke": "#000000",
+ "stroke-width": "0.25",
+ "linecap": "square",
+ },
+ )
+ etree.SubElement(
+ pattern,
+ "path",
+ {
+ "d": "M4 0 l-4,4",
+ "stroke": "#000000",
+ "stroke-width": "0.25",
+ "linecap": "square",
+ },
+ )
+
+ def _get_line():
+ return self.document.readline().strip().decode(options.input_encode)
+
+ def get_line():
+ return _get_line(), _get_line()
+
+ def get_group(group):
+ line = get_line()
+ if line[0] == group:
+ return float(line[1])
+ return 0.0
+
+ xmax = xmin = ymin = 0.0
+ ltscale = 1.0 # $LTSCALE:global scale of line-style
+ height = 297.0 * 96.0 / 25.4 # default A4 height in pixels
+ measurement = 0 # default inches
+ flag = 0 # (0, 1, 2, 3, 4) = (none, LAYER, LTYPE, DIMTXT, STYLE)
+ layer_colors = {} # store colors by layer
+ layer_nodes = {} # store nodes by layer
+ linetypes = {} # store linetypes by name
+ DIMTXT = {} # store DIMENSION text sizes
+ # style_name = {} # style name
+ style_font3 = {} # style font 1byte
+ style_font4 = {} # style font 2byte
+ style_direction = {} # style display direction
+ be_extrude = False
+ line = get_line()
+
+ if line[0] == "AutoCAD Binary DXF":
+ inkex.errormsg(
+ _(
+ "Inkscape cannot read binary DXF files. \n"
+ "Please convert to ASCII format first."
+ )
+ + str(len(line[0]))
+ + " "
+ + str(len(line[1]))
+ )
+ self.document = doc
+ return
+
+ inENTITIES = False
+ style_name = "*"
+ layername = None
+ linename = None
+ stylename = None
+ style_name = None
+ errno = 0
+ pdmode_err = False
+ while line[0] and line[1] != "BLOCKS":
+ line = get_line()
+ if line[1] == "ENTITIES": # no BLOCK SECTION
+ inENTITIES = True
+ break
+ if line[1] == "$PDMODE" and not pdmode_err:
+ inkex.errormsg("$PDMODE is ignored. A point is displayed as normal.")
+ pdmode_err = True
+ if options.scalemethod == "file":
+ if line[1] == "$MEASUREMENT":
+ measurement = get_group("70")
+ elif options.scalemethod == "auto":
+ if line[1] == "$EXTMIN":
+ xmin = get_group("10")
+ ymin = get_group("20")
+ if line[1] == "$EXTMAX":
+ xmax = get_group("10")
+ if line[1] == "$LTSCALE":
+ ltscale = get_group("40")
+ if flag == 1 and line[0] == "2":
+ layername = line[1]
+ layer_nodes[layername] = svg.add(inkex.Layer.new(layername))
+ if flag == 2 and line[0] == "2":
+ linename = line[1]
+ linetypes[linename] = []
+ if flag == 3 and line[0] == "2":
+ stylename = line[1]
+ if flag == 4 and line[0] == "2":
+ style_name = line[1]
+ style_font3[style_name] = []
+ style_font4[style_name] = []
+ style_direction[style_name] = []
+ if line[0] == "2" and line[1] == "LAYER":
+ flag = 1
+ if line[0] == "2" and line[1] == "LTYPE":
+ flag = 2
+ if line[0] == "2" and line[1] == "DIMSTYLE":
+ flag = 3
+ if line[0] == "2" and line[1] == "STYLE":
+ flag = 4
+ if flag == 1 and line[0] == "62":
+ if layername is None:
+ errno = 1
+ break
+ layer_colors[layername] = int(line[1])
+ if flag == 2 and line[0] == "49":
+ if linename is None:
+ errno = 2
+ break
+ linetypes[linename].append(float(line[1]))
+ if flag == 3 and line[0] == "140":
+ if stylename is None:
+ errno = 3
+ break
+ DIMTXT[stylename] = float(line[1])
+ if flag == 4 and line[0] == "3":
+ if style_name is None:
+ errno = 4
+ break
+ style_font3[style_name].append(line[1])
+ if flag == 4 and line[0] == "4":
+ if style_name is None:
+ errno = 4
+ break
+ style_font4[style_name].append(line[1])
+ if flag == 4 and line[0] == "70": # not no of STYLE
+ if style_name != "*":
+ style_direction[style_name] = int(line[1])
+ if line[0] == "0" and line[1] == "ENDTAB":
+ flag = 0
+ if errno != 0:
+ if errno == 1:
+ errMsg = "LAYER"
+ elif errno == 2:
+ errMsg = "LTYPE"
+ elif errno == 3:
+ errMsg = "DIMSTYLE"
+ else: # errno == 4
+ errMsg = "STYLE"
+ inkex.errormsg(
+ "Import stopped. DXF is incorrect.\ngroup code 2 ("
+ + errMsg
+ + ") is missing"
+ )
+ self.document = doc
+ return
+
+ if options.scalemethod == "file":
+ scale = 25.4 # default inches
+ if measurement == 1.0:
+ scale = 1.0 # use mm
+ elif options.scalemethod == "auto":
+ scale = 1.0
+ if xmax > xmin:
+ scale = 210.0 / (xmax - xmin) # scale to A4 width
+ else:
+ scale = float(options.scale) # manual scale factor
+ xmin = float(options.xmin)
+ ymin = float(options.ymin)
+ svg.desc = "%s - scale = %f, origin = (%f, %f), method = %s" % (
+ os.path.basename(options.input_file),
+ scale,
+ xmin,
+ ymin,
+ options.scalemethod,
+ )
+ scale *= 96.0 / 25.4 # convert from mm to pixels
+ textscale = float(options.textscale)
+
+ if "0" not in layer_nodes:
+ layer_nodes["0"] = svg.add(inkex.Layer.new("0"))
+
+ layer_colors["0"] = 7
+
+ for linename in linetypes.keys(): # scale the dashed lines
+ linetype = ""
+ for length in linetypes[linename]:
+ if length == 0: # test for dot
+ linetype += " 0.5,"
+ else:
+ linetype += "%.4f," % math.fabs(length * scale * ltscale)
+ if linetype == "":
+ linetypes[linename] = "stroke-linecap: round"
+ else:
+ linetypes[linename] = "stroke-dasharray:" + linetype
+
+ entity = ""
+ block = defs # initiallize with dummy
+ while line[0] and (line[1] != "ENDSEC" or not inENTITIES):
+ line = get_line()
+ if line[1] == "ENTITIES":
+ inENTITIES = True
+ if entity and vals.is_valid(line[0]):
+ seqs.append(line[0]) # list of group codes
+ if line[0] in ("1", "2", "3", "6", "7", "8"): # text value
+ # TODO: if add funs of export_mtext, delete the line
+ val = line[1].replace(r"\~", " ")
+ # val = re.sub(r"\\A.*;", "", val)
+ # val = re.sub(r'\\H.*;', '', val)
+ val = re.sub(r"\^I", "", val)
+ val = re.sub(r"{\\L", "", val)
+ # val = re.sub(r'}', '', val) {\\C; '}' in mtext
+ val = re.sub(r"\\S.*;", "", val)
+ val = re.sub(r"\\W.*;", "", val)
+ val = val
+ val = re.sub(r"\\U\+([0-9A-Fa-f]{4})", re_hex2unichar, val)
+ elif line[0] in ("62", "70", "92", "93"):
+ val = int(line[1])
+ else: # unscaled float value
+ val = float(line[1])
+ vals[line[0]].append(val)
+ elif has_export(line[1]):
+ if has_export(entity):
+ if block != defs: # in a BLOCK
+ layer = block
+ elif vals.has_layer_name: # use Common Layer Name
+ if not vals.layer_name:
+ vals.layer_name = "0" # use default name
+ if vals.layer_name not in layer_nodes:
+ # attribs = {inkex.addNS('groupmode','inkscape') :
+ # 'layer', inkex.addNS('label','inkscape') : '%s' % vals.layer_name}
+ # layer_nodes[vals.layer_name] = etree.SubElement(doc.getroot(), 'g', attribs)
+ layer_nodes[vals.layer_name] = svg.add(
+ inkex.Layer.new(vals.layer_name)
+ )
+ layer = layer_nodes[vals.layer_name]
+ color = "#000000" # default color
+ if vals.has_layer_name:
+ if vals.layer_name in layer_colors:
+ color = get_rgbcolor(layer_colors[vals.layer_name], color)
+ if vals.has_color: # Common Color Number
+ color = get_rgbcolor(vals.color, color)
+ style = formatStyle({"stroke": "%s" % color, "fill": "none"})
+ w = 0.5 # default lineweight for POINT
+ if vals.has_line_weight: # Common Lineweight
+ if vals.line_weight > 0:
+ w = 96.0 / 25.4 * vals.line_weight / 100.0
+ w *= scale # real wide : line_weight /144 inch
+ if w < 0.5:
+ w = 0.5
+ if (
+ block == defs
+ ): # not in a BLOCK for INSERT except stroke-width 2021.july
+ style = formatStyle(
+ {
+ "stroke": "%s" % color,
+ "fill": "none",
+ "stroke-width": "%.3f" % w,
+ }
+ )
+ if vals.has_line_type: # Common Linetype
+ if vals.line_type in linetypes:
+ style += ";" + linetypes[vals.line_type]
+ extrude = 1.0
+ if vals.has_extrude:
+ if (entity != "LINE") and (entity != "POINT"):
+ extrude = float(vals.extrude)
+ if extrude < 1.0:
+ be_extrude = True
+
+ vals.adjust_coords(xmin, ymin, scale, extrude, height)
+
+ if extrude == -1.0: # reflect angles
+ if vals.has_angle and vals.has_angle2:
+ vals.angle2, vals.angle = (
+ 180.0 - vals.angle,
+ 180.0 - vals.angle2,
+ )
+ exporter = get_export(entity)
+ if exporter:
+ if entity == "POINT":
+ exporter(vals, w)
+ else:
+ exporter(vals)
+
+ if line[1] == "POLYLINE":
+ inVertexs = False
+ entity = "LWPOLYLINE"
+ vals = ValueConstruct()
+ seqs = []
+ flag70 = 0 # default closed-line or not
+ val8 = "0" # default layer name
+ val10 = 0 # x
+ val20 = 0 # y
+ val42 = 0 # bulge
+ valid = True
+ while line[0] and (line[1] != "SEQEND"):
+ line = get_line()
+ if line[1] == "VERTEX":
+ if inVertexs == True:
+ if valid:
+ seqs.append("10")
+ vals["10"].append(val10)
+ seqs.append("20")
+ vals["20"].append(val20)
+ seqs.append("42")
+ vals["42"].append(val42)
+ val42 = 0
+ inVertexs = True
+ valid = True
+ if inVertexs == False:
+ if line[0] == "6": # 6:line style
+ seqs.append(line[0])
+ vals[line[0]].append(line[1])
+ if line[0] == "8": # 8:layer
+ val8 = line[1]
+ if line[0] == "70": # flag
+ flag70 = int(line[1])
+ else:
+ if line[0] == "70":
+ if int(line[1]) == 16: # control point
+ valid = False
+ if line[0] in ("10", "20", "42"): # vertexs
+ val = float(line[1])
+ if line[0] == "10":
+ val10 = val
+ elif line[0] == "20":
+ val20 = val
+ else:
+ val42 = val
+ if valid:
+ seqs.append("8") # layer_name
+ vals["8"].append(val8)
+ seqs.append("10")
+ vals["10"].append(val10)
+ seqs.append("20")
+ vals["20"].append(val20)
+ seqs.append("42") # bulge
+ vals["42"].append(val42)
+ seqs.append("70") # closed line?
+ vals["70"].append(flag70)
+ continue
+
+ entity = line[1]
+ vals = ValueConstruct()
+ seqs = []
+
+ # for debug
+ # tree = etree.ElementTree(svg)
+ # tree.write('c:\Python\svgCH2.xml')
+ if be_extrude:
+ inkex.errormsg(
+ _(
+ "An object that has the extrude parameter set was detected. "
+ "The imported figure may be displayed incorrectly."
+ )
+ )
+ self.document = doc
+
+
+def get_export(opt):
+ return globals().get("export_" + opt.lower(), None)
+
+
+def has_export(opt):
+ return get_export(opt) is not None
+
+
+if __name__ == "__main__":
+ DxfInput().run()