1563 lines
52 KiB
Python
1563 lines
52 KiB
Python
#! /usr/bin/python3 -sP
|
|
# 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 len(phrase) == 0:
|
|
return
|
|
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 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
|
|
|
|
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
|
|
|
|
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:
|
|
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)
|
|
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()
|