add a BytesLoader() and an on the fly encoding detector
# Copyright (c) 2020, Manfred Moitzi
# License: MIT License
-from typing import TYPE_CHECKING, BinaryIO
+from typing import TYPE_CHECKING, BinaryIO, Iterable
+from ezdxf.lldxf import const
+from ezdxf.lldxf.types import DXFTag
+from ezdxf.tools.codepage import toencoding
if TYPE_CHECKING:
- from ezdxf.eztypes import Drawing
+ from ezdxf.eztypes import Drawing, Tags
__all__ = ['read', 'readfile']
@@ -51,6 +54,72 @@ def readfile(filename: str) -> 'Drawing':
pass
-class SoftTagLoader:
- """ An error tolerant DXF tag loader. """
- pass
+DWGCODEPAGE = '$DWGCODEPAGE'
+ACADVER = '$ACADVER'
+
+
+class BytesLoader:
+ def __init__(self, stream: BinaryIO):
+ self._stream = stream
+ self._encoding = 'cp1252'
+ self._line = 1
+
+ @property
+ def encoding(self) -> str:
+ return self._encoding
+
+ @encoding.setter
+ def encoding(self, encoding: str):
+ self._encoding = encoding
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ try:
+ code = self._stream.readline()
+ value = self._stream.readline()
+ except EOFError:
+ raise StopIteration
+ if code == b'' and value == b'':
+ raise StopIteration
+ try:
+ code = int(code)
+ except ValueError:
+ raise const.DXFStructureError(
+ f'Invalid group code "{code}" at line {self._line}.')
+ value = value.rstrip(b'\r\n') # remove all kind of line endings
+ value = value.decode(self._encoding)
+ self._line += 2
+ return DXFTag(code, value)
+
+
+def encoding_detector(tags: BytesLoader) -> Iterable['DXFTag']:
+ next_tag = None
+ encoding = None
+ version = None
+ done = False
+ for tag in iter(tags):
+ if done:
+ yield tag
+ continue
+ code, value = tag
+ if code == 9:
+ if value == DWGCODEPAGE:
+ next_tag = DWGCODEPAGE # e.g. (3, "ANSI_1252")
+ elif value == ACADVER:
+ next_tag = ACADVER # e.g. (1, "AC1012")
+ elif code == 3 and next_tag == DWGCODEPAGE:
+ encoding = toencoding(value)
+ next_tag = None
+ elif code == 1 and next_tag == ACADVER:
+ version = value
+ if version >= const.DXF2007:
+ encoding = 'utf8'
+ next_tag = None
+
+ if encoding and version:
+ tags.encoding = encoding
+ done = True
+
+ yield tag
