def load_section_dict(self, sections: List[List[DXFTag]]) -> None: """ Merge sections of same type. """ def add_section(name: str, tags) -> None: if name in section_dict: section_dict[name].extend(tags[2:]) else: section_dict[name] = tags def _build_section_dict(d: dict) -> None: for name, section in d.items(): if name in const.MANAGED_SECTIONS: self.section_dict[name] = list(group_tags(section, 0)) orphans = sections.pop() section_dict = dict() for section in sections: code, name = section[1] if code == 2: add_section(name, section) else: # invalid section name tag e.g. (2, "HEADER") logger.warning( 'DXF structure error: missing section name tag, ignore ' 'section.') header = section_dict.setdefault('HEADER', [ DXFTag(0, 'SECTION'), DXFTag(2, 'HEADER'), ]) self.rescue_orphaned_header_vars(header, orphans) _build_section_dict(section_dict)
def acad_mtext_defined_height_xdata(self) -> Tags: return Tags([ DXFTag(1000, "ACAD_MTEXT_DEFINED_HEIGHT_BEGIN"), DXFTag(1070, 46), DXFTag(1040, self.defined_height), DXFTag(1000, "ACAD_MTEXT_DEFINED_HEIGHT_END"), ])
def dxftags(self) -> List[DXFTag]: # count = count of tags not faces! yield DXFTag(93, self.tag_count()) for face in self.value: yield DXFTag(90, len(face)) for index in face: yield DXFTag(90, index)
def rebuild_tables(self, tables: List[Tags]) -> List[Tags]: """ Rebuild TABLES section. """ def append_table(name: str): if name not in content: return head = heads.get(name) if head: tables.append(head) else: # The new table head gets a valid handle from Auditor. tables.append(Tags([DXFTag(0, 'TABLE'), DXFTag(2, name)])) tables.extend(content[name]) tables.append(Tags([DXFTag(0, 'ENDTAB')])) heads = dict() content = defaultdict(list) valid_tables = set(const.TABLE_NAMES_ACAD_ORDER) for entry in tables: name = entry[0].value.upper() if name == 'TABLE': try: table_name = entry[1].value.upper() except (IndexError, AttributeError): pass else: heads[table_name] = entry elif name in valid_tables: content[name].append(entry) tables = [Tags([DXFTag(0, 'SECTION'), DXFTag(2, 'TABLES')])] for name in const.TABLE_NAMES_ACAD_ORDER: append_table(name) return tables
def convert_text_lines_to_tags(text_lines): for line in text_lines: yield DXFTag(1, line[:255]) if len(line) > 255: yield DXFTag( 3, line[255:] ) # tail (max. 255 chars), what if line > 510 chars???
def test_tags_to_text_lines_short_lines(self): tags = [ DXFTag(1, "123"), DXFTag(1, "456"), DXFTag(1, "789") ] expected = ["123", "456", "789"] self.assertEqual(expected, list(convert_tags_to_text_lines(tags)))
def acad_mtext_columns_xdata(self) -> Tags: tags = Tags([ DXFTag(1000, "ACAD_MTEXT_COLUMNS_BEGIN"), DXFTag(1070, 47), DXFTag(1070, self.count), # incl. main MTEXT ]) tags.extend( # writes only (count - 1) handles! DXFTag(1005, handle) for handle in self.mtext_handles()) tags.append(DXFTag(1000, "ACAD_MTEXT_COLUMNS_END")) return tags
def dxftags(self): return [ DXFTag(72, 2), # edge type DXFVertex(10, self.center), DXFTag(40, self.radius), DXFTag(50, self.start_angle), DXFTag(51, self.end_angle), DXFTag(73, self.is_counter_clockwise), ]
def _seek_to_section(self, name: str) -> int: prev_tag = (0, None) self.file.seek(0) find = DXFTag(2, name) section = DXFTag(0, 'SECTION') for tag in low_level_tagger(self.file): if tag == find and prev_tag == section: break prev_tag = tag return self.file.tell()
def dxftags(self) -> Iterable[DXFTag]: for point in self: x, y, start_width, end_width, bulge = point yield DXFVertex(self.VERTEX_CODE, (x, y)) if start_width: yield DXFTag(self.START_WIDTH_CODE, start_width) if end_width: yield DXFTag(self.END_WIDTH_CODE, end_width) if bulge: yield DXFTag(self.BULGE_CODE, bulge)
def dxftags(self): tags = [ DXFTag(92, int(self.path_type_flags)), DXFTag(93, len(self.edges)) ] for edge in self.edges: tags.extend(edge.dxftags()) tags.extend( build_source_boundary_object_tags(self.source_boundary_objects)) return tags
def dxftags(self): return [ DXFTag(72, 3), # edge type DXFVertex(10, self.center), DXFVertex(11, self.major_axis), DXFTag(40, self.ratio), DXFTag(50, self.start_angle), DXFTag(51, self.end_angle), DXFTag(73, self.is_counter_clockwise) ]
def test_tags_to_text_lines_long_lines(self): line = "0123456789" * 30 tags = [ DXFTag(1, line[:255]), DXFTag(3, line[255:]), DXFTag(1, line[:255]), DXFTag(3, line[255:]), ] expected = [line, line] self.assertEqual(expected, list(convert_tags_to_text_lines(tags)))
def dxftags(self) -> List[DXFTag]: tags = [ DXFTag(72, 4), # edge type DXFTag(94, int(self.degree)), DXFTag(73, int(self.rational)), DXFTag(74, int(self.periodic)), DXFTag(95, len(self.knot_values)), # number of knots DXFTag(96, len(self.control_points)), # number of control points ] # build knot values list # knot values have to be present and valid, otherwise AutoCAD crashes if len(self.knot_values): tags.extend([DXFTag(40, float(value)) for value in self.knot_values]) else: raise DXFValueError("SplineEdge: missing required knot values") # build control points # control points have to be present and valid, otherwise AutoCAD crashes tags.extend([DXFVertex(10, (float(value[0]), float(value[1]))) for value in self.control_points]) # build weights list, optional tags.extend([DXFTag(42, float(value)) for value in self.weights]) # build fit points # fit points have to be present and valid, otherwise AutoCAD crashes # edit 2016-12-20: this is not true - there are cases with no fit points and without crashing AutoCAD tags.append(DXFTag(97, len(self.fit_points))) tags.extend([DXFVertex(11, (float(value[0]), float(value[1]))) for value in self.fit_points]) tags.append(DXFVertex(12, (float(self.start_tangent[0]), float(self.start_tangent[1])))) tags.append(DXFVertex(13, (float(self.end_tangent[0]), float(self.end_tangent[1])))) return tags
def dxftags(self) -> Iterable[DXFTag]: for point in self: x, y, start_width, end_width, bulge = point yield DXFVertex(self.VERTEX_CODE, (x, y)) if start_width or end_width: # Export always start- and end width together, # required for BricsCAD but not AutoCAD! yield DXFTag(self.START_WIDTH_CODE, start_width) yield DXFTag(self.END_WIDTH_CODE, end_width) if bulge: yield DXFTag(self.BULGE_CODE, bulge)
def bgcolor(self, rgb: 'RGB') -> None: color_value = rgb2int(rgb) | -0b111110000000000000000000000000 # it's magic try: xdata_bgcolor = self.tags.get_xdata('HATCHBACKGROUNDCOLOR') except DXFValueError: # no xdata for background color found self.tags.xdata.append(Tags([ DXFTag(1001, 'HATCHBACKGROUNDCOLOR'), DXFTag(1071, color_value), ])) else: xdata_bgcolor.set_first(DXFTag(1071, color_value))
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 append_table(name: str): if name not in content: return head = heads.get(name) if head: tables.append(head) else: # The new table head gets a valid handle from Auditor. tables.append(Tags([DXFTag(0, 'TABLE'), DXFTag(2, name)])) tables.extend(content[name]) tables.append(Tags([DXFTag(0, 'ENDTAB')]))
def append(self, handle: str, sort_handle: str) -> None: """ Append redraw association (handle, sort_handle). Args: handle: DXF entity handle (uppercase hex value without leading '0x') sort_handle: sort handle (uppercase hex value without leading '0x') """ subclass = self.sortentstable_subclass subclass.append(DXFTag(331, handle)) subclass.append(DXFTag(5, sort_handle))
def load_section_dict(self, sections: List[List[DXFTag]]) -> None: """Merge sections of same type.""" def add_section(name: str, tags) -> None: if name in section_dict: section_dict[name].extend(tags[2:]) else: section_dict[name] = tags def _build_section_dict(d: Dict) -> None: for name, section in d.items(): if name in const.MANAGED_SECTIONS: self.section_dict[name] = list(group_tags(section, 0)) def _remove_unsupported_sections(d: Dict): for name in ("CLASSES", "OBJECTS", "ACDSDATA"): if name in d: del d[name] self.fixes.append(( AuditError.REMOVED_UNSUPPORTED_SECTION, f"Removed unsupported {name} section for DXF R12.", )) # Last section could be orphaned tags: orphans = sections.pop() if orphans and orphans[0] == (0, "SECTION"): # The last section contains not the orphaned tags: sections.append(orphans) orphans = [] section_dict: "SectionDict" = dict() for section in sections: code, name = section[1] if code == 2: add_section(name, section) else: # invalid section name tag e.g. (2, "HEADER") self.fixes.append(( AuditError.MISSING_SECTION_NAME_TAG, "DXF structure error: missing section name tag, ignore section.", )) header = section_dict.setdefault( "HEADER", [ DXFTag(0, "SECTION"), # type: ignore DXFTag(2, "HEADER"), # type: ignore ], ) self.rescue_orphaned_header_vars(header, orphans) # type: ignore self.dxfversion = _detect_dxf_version(header) if self.dxfversion <= const.DXF12: _remove_unsupported_sections(section_dict) _build_section_dict(section_dict)
def _setup_pattern(self, pattern, length): complex_line_type = True if isstring(pattern) else False if complex_line_type: # a .lin like line type definition string self._setup_complex_pattern(pattern, length) else: # pattern: [2.0, 1.25, -0.25, 0.25, -0.25] - 1. element is total pattern length # pattern elements: >0 line, <0 gap, =0 point subclass = self.tags.get_subclass('AcDbLinetypeTableRecord') subclass.append(DXFTag(73, len(pattern) - 1)) subclass.append(DXFTag(40, float(pattern[0]))) for element in pattern[1:]: subclass.append(DXFTag(49, float(element))) subclass.append(DXFTag(74, 0))
def test_equality(): assert (1, "text") == DXFTag(1, "text"), "should be equal to tuple" # Python 2.7 Issue assert ((1, "text") != DXFTag(1, "text")) is False assert (1, "text") != DXFTag(1, "text2"), "should be not equal to tuple" assert DXFTag(1, "text") == (1, "text"), "should be equal to tuple" assert DXFTag(1, "text") != (1, "text2"), "should be not equal to tuple" assert DXFTag(1, "text") == DXFTag(1, "text") assert DXFTag(1, "text") != DXFTag(1, "text2")
def test_equality(): assert (1, 'text') == DXFTag(1, 'text'), 'should be equal to tuple' # Python 2.7 Issue assert ((1, 'text') != DXFTag(1, 'text')) is False assert (1, 'text') != DXFTag(1, 'text2'), 'should be not equal to tuple' assert DXFTag(1, 'text') == (1, 'text'), 'should be equal to tuple' assert DXFTag(1, 'text') != (1, 'text2'), 'should be not equal to tuple' assert DXFTag(1, 'text') == DXFTag(1, 'text') assert DXFTag(1, 'text') != DXFTag(1, 'text2')
def rebuild_tables(self, tables: List[Tags]) -> List[Tags]: """Rebuild TABLES section.""" # Note: the recover module does not report invalid placed table entries, # it just recovers them. The "normal" loading process ignore these # misplaced table entries and logs a warning. def append_table(name: str): if name not in content: return head = heads.get(name) if head: tables.append(head) else: # The new table head gets a valid handle from Auditor. tables.append(Tags([DXFTag(0, "TABLE"), DXFTag(2, name)])) tables.extend(content[name]) tables.append(Tags([DXFTag(0, "ENDTAB")])) heads = dict() content = defaultdict(list) valid_tables = set(const.TABLE_NAMES_ACAD_ORDER) for entry in tables: name = entry[0].value.upper() if name == "TABLE": try: table_name = entry[1].value.upper() except (IndexError, AttributeError): pass else: heads[table_name] = entry elif name in valid_tables: content[name].append(entry) tables = [Tags([DXFTag(0, "SECTION"), DXFTag(2, "TABLES")])] names = list(const.TABLE_NAMES_ACAD_ORDER) if self.dxfversion <= const.DXF12: # Ignore BLOCK_RECORD table names.remove("BLOCK_RECORD") if "BLOCK_RECORD" in content: self.fixes.append(( AuditError.REMOVED_UNSUPPORTED_TABLE, f"Removed unsupported BLOCK_RECORD table for DXF R12.", )) for name in names: append_table(name) return tables
def _setup_complex_pattern(self, pattern, length): tokens = lin_compiler(pattern) subclass = self.tags.get_subclass('AcDbLinetypeTableRecord') subclass.append(DXFTag(73, 0)) # temp length of 0 subclass.append(DXFTag(40, length)) count = 0 for token in tokens: if isinstance(token, DXFTag): if subclass[-1].code == 49: # useless 74 only after 49 :)) subclass.append(DXFTag(74, 0)) subclass.append(token) count += 1 else: # TEXT or SHAPE subclass.extend(token.complex_ltype_tags(self.drawing)) subclass.append(DXFTag(74, 0)) # useless 74 at the end :)) subclass.update(DXFTag(73, count))
def bytes_loader(stream: BinaryIO) -> Iterable[DXFTag]: """ Yields :class:``DXFTag`` objects from a bytes `stream` (untrusted external source), skips all comment tags (group code == 999). ``DXFTag.code`` is always an ``int`` and ``DXFTag.value`` is always a raw bytes value without line endings. Works with file system streams and :class:`BytesIO` streams. Raises: DXFStructureError: Found invalid group code. """ line = 1 readline = stream.readline while True: code = readline() # ByteIO(): empty strings indicates EOF - does not raise an exception if code: try: code = int(code) except ValueError: code = code.decode(errors='ignore') raise const.DXFStructureError( f'Invalid group code "{code}" at line {line}.') else: return value = readline() # ByteIO(): empty strings indicates EOF if value: if code != 999: yield DXFTag(code, value.rstrip(b'\r\n')) line += 2 else: return
def _update_meta_data(self) -> None: count = len(self) if self._drawing.dxfversion > 'AC1009': subclass = self._table_header.get_subclass('AcDbSymbolTable') else: subclass = self._table_header.noclass subclass.set_first(DXFTag(70, count))
def _setup_pattern(self, pattern: Union[Iterable[float], str], length: float) -> None: complex_line_type = True if isinstance(pattern, str) else False if complex_line_type: # a .lin like line type definition string tags = self._setup_complex_pattern(pattern, length) else: # pattern: [2.0, 1.25, -0.25, 0.25, -0.25] - 1. element is total # pattern length pattern elements: >0 line, <0 gap, =0 point tags = Tags([ DXFTag(72, 65), # letter 'A' DXFTag(73, len(pattern) - 1), DXFTag(40, float(pattern[0])), ]) for element in pattern[1:]: tags.append(DXFTag(49, float(element))) tags.append(DXFTag(74, 0)) self.pattern_tags = LinetypePattern(tags)
def dxftags(self): if len(self.lines): tags = [DXFTag(78, len(self.lines))] for line in self.lines: tags.extend(line.dxftags()) else: tags = [] return tags
def new_extension_dict(self): """ Creates and assigns a new extensions dictionary. Link to an existing extension dictionary will be lost. """ xdict = self.drawing.objects.add_dictionary(owner=self.dxf.handle) self.set_app_data(ACAD_XDICTIONARY, [DXFTag(360, xdict.dxf.handle)]) return xdict