Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'io_import_dxf/dxfgrabber/pytags.py')
-rwxr-xr-xio_import_dxf/dxfgrabber/pytags.py385
1 files changed, 385 insertions, 0 deletions
diff --git a/io_import_dxf/dxfgrabber/pytags.py b/io_import_dxf/dxfgrabber/pytags.py
new file mode 100755
index 00000000..d8d0479b
--- /dev/null
+++ b/io_import_dxf/dxfgrabber/pytags.py
@@ -0,0 +1,385 @@
+# Purpose: tag reader
+# Created: 21.07.2012, taken from my ezdxf project
+# Copyright (C) 2012, Manfred Moitzi
+# License: MIT License
+from __future__ import unicode_literals
+__author__ = "mozman <mozman@gmx.at>"
+
+from io import StringIO
+from collections import namedtuple
+from itertools import chain, islice
+from . import tostr
+
+
+DXFTag = namedtuple('DXFTag', 'code value')
+NONE_TAG = DXFTag(999999, 'NONE')
+APP_DATA_MARKER = 102
+SUBCLASS_MARKER = 100
+XDATA_MARKER = 1001
+
+
+class DXFStructureError(Exception):
+ pass
+
+
+def point_tuple(value):
+ return tuple(float(f) for f in value)
+
+
+POINT_CODES = frozenset(chain(range(10, 20), (210, ), range(110, 113), range(1010, 1020)))
+
+
+def is_point_tag(tag):
+ return tag[0] in POINT_CODES
+
+
+class TagIterator(object):
+ def __init__(self, textfile, assure_3d_coords=False):
+ self.textfile = textfile
+ self.readline = textfile.readline
+ self.undo = False
+ self.last_tag = NONE_TAG
+ self.undo_coord = None
+ self.eof = False
+ self.assure_3d_coords = assure_3d_coords
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ def undo_tag():
+ self.undo = False
+ tag = self.last_tag
+ return tag
+
+ def read_next_tag():
+ try:
+ code = int(self.readline())
+ value = self.readline().rstrip('\n')
+ except UnicodeDecodeError:
+ raise # because UnicodeDecodeError() is a subclass of ValueError()
+ except (EOFError, ValueError):
+ raise StopIteration()
+ return code, value
+
+ def read_point(code_x, value_x):
+ try:
+ code_y, value_y = read_next_tag() # 2. coordinate is always necessary
+ except StopIteration:
+ code_y = 0 # -> DXF structure error in following if-statement
+
+ if code_y != code_x + 10:
+ raise DXFStructureError("invalid 2D/3D point found")
+
+ value_y = float(value_y)
+ try:
+ code_z, value_z = read_next_tag()
+ except StopIteration: # 2D point at end of file
+ self.eof = True # store reaching end of file
+ if self.assure_3d_coords:
+ value = (value_x, value_y, 0.)
+ else:
+ value = (value_x, value_y)
+ else:
+ if code_z != code_x + 20: # not a Z coordinate -> 2D point
+ self.undo_coord = (code_z, value_z)
+ if self.assure_3d_coords:
+ value = (value_x, value_y, 0.)
+ else:
+ value = (value_x, value_y)
+ else: # is a 3D point
+ value = (value_x, value_y, float(value_z))
+ return value
+
+ def next_tag():
+ code = 999
+ while code == 999: # skip comments
+ if self.undo_coord is not None:
+ code, value = self.undo_coord
+ self.undo_coord = None
+ else:
+ code, value = read_next_tag()
+
+ if code in POINT_CODES: # 2D or 3D point
+ value = read_point(code, float(value)) # returns a tuple of floats, no casting needed
+ else:
+ value = cast_tag_value(code, value)
+ self.last_tag = DXFTag(code, value)
+ return self.last_tag
+
+ if self.eof: # stored end of file
+ raise StopIteration()
+
+ if self.undo:
+ return undo_tag()
+ else:
+ return next_tag()
+ # for Python 2.7
+ next = __next__
+
+ def undo_tag(self):
+ if not self.undo:
+ self.undo = True
+ else:
+ raise ValueError('No tag to undo')
+
+
+class StringIterator(TagIterator):
+ def __init__(self, dxfcontent):
+ super(StringIterator, self).__init__(StringIO(dxfcontent))
+
+
+class TagCaster:
+ def __init__(self):
+ self._cast = self._build()
+
+ def _build(self):
+ table = {}
+ for caster, codes in TYPES:
+ for code in codes:
+ table[code] = caster
+ return table
+
+ def cast(self, tag):
+ code, value = tag
+ typecaster = self._cast.get(code, tostr)
+ try:
+ value = typecaster(value)
+ except ValueError:
+ if typecaster is int: # convert float to int
+ value = int(float(value))
+ else:
+ raise
+ return DXFTag(code, value)
+
+ def cast_value(self, code, value):
+ typecaster = self._cast.get(code, tostr)
+ try:
+ return typecaster(value)
+ except ValueError:
+ if typecaster is int: # convert float to int
+ return int(float(value))
+ else:
+ raise
+
+TYPES = [
+ (tostr, range(0, 10)),
+ (point_tuple, range(10, 20)),
+ (float, range(20, 60)),
+ (int, range(60, 100)),
+ (tostr, range(100, 106)),
+ (point_tuple, range(110, 113)),
+ (float, range(113, 150)),
+ (int, range(170, 180)),
+ (point_tuple, [210]),
+ (float, range(211, 240)),
+ (int, range(270, 290)),
+ (int, range(290, 300)), # bool 1=True 0=False
+ (tostr, range(300, 370)),
+ (int, range(370, 390)),
+ (tostr, range(390, 400)),
+ (int, range(400, 410)),
+ (tostr, range(410, 420)),
+ (int, range(420, 430)),
+ (tostr, range(430, 440)),
+ (int, range(440, 460)),
+ (float, range(460, 470)),
+ (tostr, range(470, 480)),
+ (tostr, range(480, 482)),
+ (tostr, range(999, 1010)),
+ (point_tuple, range(1010, 1020)),
+ (float, range(1020, 1060)),
+ (int, range(1060, 1072)),
+]
+
+_TagCaster = TagCaster()
+cast_tag = _TagCaster.cast
+cast_tag_value = _TagCaster.cast_value
+
+
+class Tags(list):
+ """ DXFTag() chunk as flat list. """
+ def find_all(self, code):
+ """ Returns a list of DXFTag(code, ...). """
+ return [tag for tag in self if tag.code == code]
+
+ def tag_index(self, code, start=0, end=None):
+ """ Return first index of DXFTag(code, ...). """
+ if end is None:
+ end = len(self)
+ for index, tag in enumerate(islice(self, start, end)):
+ if tag.code == code:
+ return start+index
+ raise ValueError(code)
+
+ def get_value(self, code):
+ for tag in self:
+ if tag.code == code:
+ return tag.value
+ raise ValueError(code)
+
+ @staticmethod
+ def from_text(text):
+ return Tags(StringIterator(text))
+
+ def get_type(self):
+ return self.__getitem__(0).value
+
+
+class TagGroups(list):
+ """
+ Group of tags starting with a SplitTag and ending before the next SplitTag.
+
+ A SplitTag is a tag with code == splitcode, like (0, 'SECTION') for splitcode=0.
+
+ """
+ def __init__(self, tags, split_code=0):
+ super(TagGroups, self).__init__()
+ self._buildgroups(tags, split_code)
+
+ def _buildgroups(self, tags, split_code):
+ def push_group():
+ if len(group) > 0:
+ self.append(group)
+
+ def start_tag(itags):
+ tag = next(itags)
+ while tag.code != split_code:
+ tag = next(itags)
+ return tag
+
+ itags = iter(tags)
+ group = Tags([start_tag(itags)])
+
+ for tag in itags:
+ if tag.code == split_code:
+ push_group()
+ group = Tags([tag])
+ else:
+ group.append(tag)
+ push_group()
+
+ def get_name(self, index):
+ return self[index][0].value
+
+ @staticmethod
+ def from_text(text, split_code=0):
+ return TagGroups(Tags.from_text(text), split_code)
+
+
+class ClassifiedTags:
+ """ Manage Subclasses, AppData and Extended Data """
+
+ def __init__(self, iterable=None):
+ self.appdata = list() # code == 102, keys are "{<arbitrary name>", values are Tags()
+ self.subclasses = list() # code == 100, keys are "subclassname", values are Tags()
+ self.xdata = list() # code >= 1000, keys are "APPNAME", values are Tags()
+ if iterable is not None:
+ self._setup(iterable)
+
+ @property
+ def noclass(self):
+ return self.subclasses[0]
+
+ def _setup(self, iterable):
+ tagstream = iter(iterable)
+
+ def collect_subclass(start_tag):
+ """ a subclass can contain appdata, but not xdata, ends with
+ SUBCLASSMARKER or XDATACODE.
+ """
+ data = Tags() if start_tag is None else Tags([start_tag])
+ try:
+ while True:
+ tag = next(tagstream)
+ if tag.code == APP_DATA_MARKER and tag.value[0] == '{':
+ app_data_pos = len(self.appdata)
+ data.append(DXFTag(tag.code, app_data_pos))
+ collect_appdata(tag)
+ elif tag.code in (SUBCLASS_MARKER, XDATA_MARKER):
+ self.subclasses.append(data)
+ return tag
+ else:
+ data.append(tag)
+ except StopIteration:
+ pass
+ self.subclasses.append(data)
+ return NONE_TAG
+
+ def collect_appdata(starttag):
+ """ appdata, can not contain xdata or subclasses """
+ data = Tags([starttag])
+ while True:
+ try:
+ tag = next(tagstream)
+ except StopIteration:
+ raise DXFStructureError("Missing closing DXFTag(102, '}') for appdata structure.")
+ data.append(tag)
+ if tag.code == APP_DATA_MARKER:
+ break
+ self.appdata.append(data)
+
+ def collect_xdata(starttag):
+ """ xdata are always at the end of the entity and can not contain
+ appdata or subclasses
+ """
+ data = Tags([starttag])
+ try:
+ while True:
+ tag = next(tagstream)
+ if tag.code == XDATA_MARKER:
+ self.xdata.append(data)
+ return tag
+ else:
+ data.append(tag)
+ except StopIteration:
+ pass
+ self.xdata.append(data)
+ return NONE_TAG
+
+ tag = collect_subclass(None) # preceding tags without a subclass
+ while tag.code == SUBCLASS_MARKER:
+ tag = collect_subclass(tag)
+ while tag.code == XDATA_MARKER:
+ tag = collect_xdata(tag)
+
+ if tag is not NONE_TAG:
+ raise DXFStructureError("Unexpected tag '%r' at end of entity." % tag)
+
+ def __iter__(self):
+ for subclass in self.subclasses:
+ for tag in subclass:
+ if tag.code == APP_DATA_MARKER and isinstance(tag.value, int):
+ for subtag in self.appdata[tag.value]:
+ yield subtag
+ else:
+ yield tag
+
+ for xdata in self.xdata:
+ for tag in xdata:
+ yield tag
+
+ def get_subclass(self, name):
+ for subclass in self.subclasses:
+ if len(subclass) and subclass[0].value == name:
+ return subclass
+ raise KeyError("Subclass '%s' does not exist." % name)
+
+ def get_xdata(self, appid):
+ for xdata in self.xdata:
+ if xdata[0].value == appid:
+ return xdata
+ raise ValueError("No extended data for APPID '%s'" % appid)
+
+ def get_appdata(self, name):
+ for appdata in self.appdata:
+ if appdata[0].value == name:
+ return appdata
+ raise ValueError("Application defined group '%s' does not exist." % name)
+
+ def get_type(self):
+ return self.noclass[0].value
+
+ @staticmethod
+ def from_text(text):
+ return ClassifiedTags(StringIterator(text))