def tag_compiler(tags: Tags) -> Iterable[DXFTag]: """Special tag compiler for the DXF browser. This compiler should never fail and always return printable tags: - invalid point coordinates are returned as float("nan") - invalid ints are returned as string - invalid floats are returned as string """ def to_float(v: str) -> float: try: return float(v) except ValueError: return float("NaN") count = len(tags) index = 0 while index < count: code, value = tags[index] if code in POINT_CODES: try: y_code, y_value = tags[index + 1] except IndexError: # x-coord as last tag yield DXFTag(code, to_float(value)) return if y_code != code + 10: # not an y-coord? yield DXFTag(code, to_float(value)) # x-coord as single tag index += 1 continue try: z_code, z_value = tags[index + 2] except IndexError: # no z-coord exist z_code = 0 z_value = 0 point: Sequence[float] if z_code == code + 20: # is a z-coord? point = (to_float(value), to_float(y_value), to_float(z_value)) index += 3 else: # a valid 2d point(x, y) point = (to_float(value), to_float(y_value)) index += 2 yield DXFVertex(code, point) else: # a single tag try: if code == 0: value = value.strip() yield DXFTag(code, TYPE_TABLE.get(code, str)(value)) except ValueError: yield DXFTag(code, str(value)) # just as string index += 1
def byte_tag_compiler( tags: Iterable[DXFTag], encoding=const.DEFAULT_ENCODING, messages: List = None, errors: str = 'surrogateescape', ) -> Iterable[DXFTag]: """ Compiles DXF tag values imported by bytes_loader() into Python types. Raises DXFStructureError() for invalid float values and invalid coordinate values. Expects DXF coordinates written in x, y[, z] order, see function :func:`safe_tag_loader` for usage with applied repair filters. Args: tags: DXF tag generator, yielding tag values as bytes like bytes_loader() encoding: text encoding messages: list to store error messages errors: specify decoding error handler - "surrogateescape" to preserve possible binary data (default) - "ignore" to use the replacement char U+FFFD "\ufffd" for invalid data - "strict" to raise an :class:`UnicodeDecodeError` exception for invalid data Raises: DXFStructureError: Found invalid DXF tag or unexpected coordinate order. """ def error_msg(tag): code = tag.code value = tag.value.decode(encoding) return f'Invalid tag ({code}, "{value}") near line: {line}.' if messages is None: messages = [] tags = iter(tags) undo_tag = None line = 0 while True: try: if undo_tag is not None: x = undo_tag undo_tag = None else: x = next(tags) line += 2 code = x.code if code in POINT_CODES: y = next(tags) # y coordinate is mandatory line += 2 # e.g. y-code for x-code=10 is 20 if y.code != code + 10: raise const.DXFStructureError( f"Missing required y-coordinate near line: {line}.") # optional z coordinate z = next(tags) line += 2 try: # is it a z-coordinate like (30, 0.0) for base x-code=10 if z.code == code + 20: try: point = (float(x.value), float(y.value), float(z.value)) except ValueError: # search for any float values point = (_search_float(x.value), _search_float(y.value), _search_float(z.value)) else: try: point = (float(x.value), float(y.value)) except ValueError: # seach for any float values point = ( _search_float(x.value), _search_float(y.value), ) undo_tag = z except ValueError: raise const.DXFStructureError( f'Invalid floating point values near line: {line}.') yield DXFVertex(code, point) elif code in BINARY_DATA: # maybe pre compiled in low level tagger (binary DXF) if isinstance(x, DXFBinaryTag): tag = x else: try: tag = DXFBinaryTag.from_string(code, x.value) except ValueError: raise const.DXFStructureError( f'Invalid binary data near line: {line}.') yield tag else: # just a single tag type_ = TYPE_TABLE.get(code, str) value: bytes = x.value if type_ is str: if code == 0: # remove white space from structure tags value = x.value.strip().upper() try: # 2 stages to document decoding errors str_ = value.decode(encoding, errors='strict') except UnicodeDecodeError: str_ = value.decode(encoding, errors=errors) messages.append( (AuditError.DECODING_ERROR, f'Fixed unicode decoding error near line {line}')) # Convert DXF unicode notation "\U+xxxx" to unicode, # but exclude structure tags (code>0): if code and has_dxf_unicode(str_): str_ = decode_dxf_unicode(str_) yield DXFTag(code, str_) else: try: # fast path for int and float yield DXFTag(code, type_(value)) except ValueError: # slow path - e.g. ProE stores int values as floats :(( if type_ is int: try: yield DXFTag(code, _search_int(x.value)) except ValueError: raise const.DXFStructureError(error_msg(x)) elif type_ is float: try: yield DXFTag(code, _search_float(x.value)) except ValueError: raise const.DXFStructureError(error_msg(x)) else: raise const.DXFStructureError(error_msg(x)) except StopIteration: return
def byte_tag_compiler(tags: Iterable[DXFTag], encoding=const.DEFAULT_ENCODING) -> Iterable[DXFTag]: """ Compiles DXF tag values imported by bytes_loader() into Python types. Raises DXFStructureError() for invalid float values and invalid coordinate values. Expects DXF coordinates written in x, y[, z] order, see function :func:`safe_tag_loader` for usage with applied repair filters. Args: tags: DXF tag generator, yielding tag values as bytes like bytes_loader() encoding: text encoding Raises: DXFStructureError: Found invalid DXF tag or unexpected coordinate order. """ def error_msg(tag): code = tag.code value = tag.value.decode(encoding) return f'Invalid tag ({code}, "{value}") near line: {line}.' tags = iter(tags) undo_tag = None line = 0 while True: try: if undo_tag is not None: x = undo_tag undo_tag = None else: x = next(tags) line += 2 code = x.code if code in POINT_CODES: y = next(tags) # y coordinate is mandatory line += 2 # e.g. y-code for x-code=10 is 20 if y.code != code + 10: raise const.DXFStructureError( f"Missing required y-coordinate near line: {line}.") # optional z coordinate z = next(tags) line += 2 try: # is it a z-coordinate like (30, 0.0) for base x-code=10 if z.code == code + 20: point = (float(x.value), float(y.value), float(z.value)) else: point = (float(x.value), float(y.value)) undo_tag = z except ValueError: raise const.DXFStructureError( f'Invalid floating point values near line: {line}.') yield DXFVertex(code, point) elif code in BINARY_DATA: # maybe pre compiled in low level tagger (binary DXF) if isinstance(x, DXFBinaryTag): tag = x else: try: tag = DXFBinaryTag.from_string(code, x.value) except ValueError: raise const.DXFStructureError( f'Invalid binary data near line: {line}.') yield tag else: # just a single tag type_ = TYPE_TABLE.get(code, str) value: bytes = x.value if type_ is str: if code == 0: # remove white space from structure tags value = x.value.strip() yield DXFTag(code, value.decode(encoding)) else: try: # fast path for int and float yield DXFTag(code, type_(value)) except ValueError: # slow path - ProE stores int values as floats :(( if type_ is int: try: yield DXFTag(code, int(float(x.value))) except ValueError: raise const.DXFStructureError(error_msg(x)) else: raise const.DXFStructureError(error_msg(x)) except StopIteration: return