def __init__(self, doc: "Drawing", entities: Iterable[Tags] = None): self.doc = doc self.entities: List[AcDsEntity] = [] self.section_info = Tags() if entities is not None: self.load_tags(iter(entities))
def add(self, appid: str, tags: Iterable) -> None: data = Tags(dxftag(code, value) for code, value in tags) if data[0] != (XDATA_MARKER, appid): data.insert(0, dxftag(XDATA_MARKER, appid)) self._add(data)
def test_group_tags_poyline_with_xdata(): tags = Tags.from_text(POLYLINE_WITH_INVALID_XDATA) assert len(tags) == 49 tags2 = list(filter_invalid_xdata_group_codes(tags)) assert len(tags2) == 41
def test_emtpy_tags(self): tags = Tags() collected_tags = tags.collect_consecutive_tags([0, 1, 2, 3, 4]) assert 0 == len(collected_tags)
def __init__(self, type_, value, font='STANDARD'): self.type = type_ self.value = value self.font = font self.tags = Tags()
def test_find_first_value_error(self): tags = Tags.from_text(TESTFINDALL) with pytest.raises(DXFValueError): tags.tag_index(1)
def test_strip_tags(self, tags): tags.remove_tags(codes=(0, )) result = Tags.strip(tags, codes=(0, )) assert 5 == len(result) assert isinstance(result, Tags)
def test_skip_empty_subclass(xtags3): xtags3.subclasses[1] = Tags() # create empty subclass subclass2 = xtags3.get_subclass('AcDbText') assert (100, 'AcDbText') == subclass2[0]
def export_entity(self, tagwriter: 'TagWriter') -> None: super().export_entity(tagwriter) tagwriter.write_tag2(SUBCLASS_MARKER, acdb_xrecord.name) tagwriter.write_tag2(280, self.dxf.cloning) tagwriter.write_tags(Tags(totags(self.tags)))
def fast_load_dxfattribs(self, dxf: DXFNamespace, group_code_mapping: Dict[int, Union[str, List]], subclass: Union[int, str, Tags], *, recover=False, log=True) -> Tags: """ Load DXF attributes into the DXF namespace and returns the unprocessed tags without leading subclass marker(100, AcDb...). Bypasses the DXF attribute validity checks. Args: dxf: entity DXF namespace group_code_mapping: group code to DXF attribute name mapping, callback attributes have to be marked with a leading "*" subclass: subclass by index, by name or as Tags() recover: recover graphic attributes log: enable/disable logging of unprocessed tags """ if self.r12: tags = self.subclasses[0] else: if isinstance(subclass, int): tags = self.subclass_by_index(subclass) elif isinstance(subclass, str): tags = self.find_subclass(subclass) else: tags = subclass unprocessed_tags = Tags() if tags is None or len(tags) == 0: return unprocessed_tags processed_names = set() # Localize attributes: get_attrib_name = group_code_mapping.get append_unprocessed_tag = unprocessed_tags.append unprotected_set_attrib = dxf.unprotected_set mark_attrib_as_processed = processed_names.add # Ignore (100, "AcDb...") or (0, "ENTITY") tag in case of DXF R12 start = 1 if tags[0].code in (0, 100) else 0 for tag in tags[start:]: name = get_attrib_name(tag.code) if isinstance(name, list): # process group code duplicates: names = name # If all existing attrib names are used, treat this tag # like an unprocessed tag. name = None # The attribute names are processed in the order of their # definition: for name_ in names: if name_ not in processed_names: name = name_ mark_attrib_as_processed(name_) break if name: # Ignore callback attributes and group codes explicit marked # as "*IGNORE": if name[0] != '*': unprotected_set_attrib(name, cast_value(tag.code, tag.value)) else: append_unprocessed_tag(tag) if self.r12: # R12 has always unprocessed tags, because there are all tags in one # subclass and one subclass definition never covers all tags e.g. # handle is processed in DXFEntity, so it is an unprocessed tag in # AcDbEntity. return unprocessed_tags # Only DXF R13+ if recover and len(unprocessed_tags): unprocessed_tags = recover_graphic_attributes( unprocessed_tags, dxf) if len(unprocessed_tags) and log: # First tag is the subclass specifier (100, "AcDb...") name = tags[0].value self.log_unprocessed_tags(tags, subclass=name, handle=dxf.get('handle')) return unprocessed_tags
def test_init_with_tags(): tags = Tags.from_text(XTAGS1) xtags = ExtendedTags(tags) assert 3 == len(xtags.subclasses) assert 1 == len(xtags.xdata)
def get_handle(tags: Tags): try: return tags.get_handle() except ValueError: return "0"
def test_replace_handle_5(self): tags = Tags.from_text(TESTHANDLE5) tags.replace_handle("AA") assert "AA" == tags.get_handle()
def test_get_handle_5(self): tags = Tags.from_text(TESTHANDLE5) assert "F5" == tags.get_handle()
def test_find_all(self): tags = Tags.from_text(TESTFINDALL) assert 3 == len(tags.find_all(0))
def _copy_data(self, entity: 'VBAProject') -> None: entity.tags = Tags(entity.tags)
def test_tag_index(self): tags = Tags.from_text(TESTFINDALL) index = tags.tag_index(0) assert 0 == index index = tags.tag_index(0, index + 1) assert 1 == index
def __init__(self): super().__init__() self.tags = Tags()
def test_replace_handle_105(self): tags = Tags.from_text(TESTHANDLE105) tags.replace_handle('AA') assert 'AA' == tags.get_handle()
def _copy_data(self, entity: 'XRecord') -> None: entity.tags = Tags(entity.tags)
def tags(self): return Tags.from_text(COLLECT_1)
class HeaderSection: MIN_HEADER_TAGS = Tags.from_text(MIN_HEADER_TEXT) name = 'HEADER' def __init__(self): self.hdrvars: Dict[str, 'HeaderVar'] = OrderedDict() self.custom_vars = CustomVars() @classmethod def load(cls, tags: Iterator[DXFTag] = None) -> 'HeaderSection': """ Constructor to generate header variables loaded from DXF files (untrusted environment). Args: tags: DXF tags as Tags() or ExtendedTags() (internal API) """ if tags is None: # create default header # files without a header have the default version: R12 return cls.new(dxfversion=const.DXF12) section = cls() section.load_tags(iter(tags)) return section @classmethod def new(cls, dxfversion=const.LATEST_DXF_VERSION) -> 'HeaderSection': section = HeaderSection() section.hdrvars = default_vars() section['$ACADVER'] = dxfversion return section def load_tags(self, tags: Iterator[DXFTag]) -> None: """ Constructor to generate header variables loaded from DXF files (untrusted environment). Args: tags: DXF tags as Tags() or ExtendedTags() """ tags = tags or self.MIN_HEADER_TAGS section_tag = next(tags) name_tag = next(tags) if section_tag != (0, 'SECTION') or name_tag != (2, 'HEADER'): raise const.DXFStructureError( "Critical structure error in HEADER section.") groups = group_tags(header_validator(tags), splitcode=9) custom_property_stack = [] # collect $CUSTOMPROPERTY/TAG for group in groups: name = group[0].value value = group[1] if name in ('$CUSTOMPROPERTYTAG', '$CUSTOMPROPERTY'): custom_property_stack.append(value.value) else: self.hdrvars[name] = HeaderVar(value) custom_property_stack.reverse() while len(custom_property_stack): try: self.custom_vars.append(tag=custom_property_stack.pop(), value=custom_property_stack.pop()) except IndexError: # internal exception break @classmethod def from_text(cls, text: str) -> 'HeaderSection': """ Load constructor from text for testing """ return cls.load(Tags.from_text(text)) def _headervar_factory(self, key: str, value: Any) -> DXFTag: if key in HEADER_VAR_MAP: factory = HEADER_VAR_MAP[key].factory return factory(value) else: raise const.DXFKeyError(f"Invalid header variable {key}.") def __len__(self) -> int: """ Returns count of header variables. """ return len(self.hdrvars) def __contains__(self, key) -> bool: """ Returns ``True`` if header variable `key` exist. """ return key in self.hdrvars def varnames(self) -> KeysView: """ Returns an iterable of all header variable names. """ return self.hdrvars.keys() def export_dxf(self, tagwriter: 'TagWriter') -> None: """ Exports header section as DXF tags. (internal API) """ def _write(name: str, value: Any) -> None: if value.value is None: logger.info(f"did not write header var {name}, value is None.") return tagwriter.write_tag2(9, name) group_code = version_specific_group_code(name, dxfversion) # fix invalid group codes if group_code != value.code: value = HeaderVar((group_code, value.value)) tagwriter.write_str(str(value)) dxfversion = tagwriter.dxfversion write_handles = tagwriter.write_handles tagwriter.write_str(" 0\nSECTION\n 2\nHEADER\n") save = self['$ACADVER'] self['$ACADVER'] = dxfversion for name, value in header_vars_by_priority(self.hdrvars, dxfversion): if not write_handles and name == '$HANDSEED': continue # skip $HANDSEED _write(name, value) if name == '$LASTSAVEDBY': # ugly hack, but necessary for AutoCAD self.custom_vars.write(tagwriter) self['$ACADVER'] = save tagwriter.write_str(" 0\nENDSEC\n") def get(self, key: str, default: Any = None) -> Any: """ Returns value of header variable `key` if exist, else the `default` value. """ if key in self.hdrvars: return self.__getitem__(key) else: return default def __getitem__(self, key: str) -> Any: """ Get header variable `key` by index operator like: :code:`drawing.header['$ACADVER']` """ try: return self.hdrvars[key].value except KeyError: # map exception raise const.DXFKeyError(str(key)) def __setitem__(self, key: str, value: Any) -> None: """ Set header variable `key` to `value` by index operator like: :code:`drawing.header['$ANGDIR'] = 1` """ try: tags = self._headervar_factory(key, value) except (IndexError, ValueError): raise const.DXFValueError(str(value)) self.hdrvars[key] = HeaderVar(tags) def __delitem__(self, key: str) -> None: """ Delete header variable `key` by index operator like: :code:`del drawing.header['$ANGDIR']` """ try: del self.hdrvars[key] except KeyError: # map exception raise const.DXFKeyError(str(key))
def tags(self): return Tags.from_text(TEST_TAGREADER)
def from_text(cls, text: str) -> 'HeaderSection': """ Load constructor from text for testing """ return cls.load(Tags.from_text(text))
def _set_path_tags(self, vertices: Iterable['Vertex']): boundary = [DXFVertex(11, value) for value in vertices] subclasstags = Tags(tag for tag in self.tags.subclasses[2] if tag.code != 11) # filter out existing path tags subclasstags.extend(boundary) self.tags.subclasses[2] = subclasstags
def test_point_count_management(self): mline = MLine() mline.load_vertices(Tags.from_text(VTX_2)) assert len(mline.vertices) == 2 assert len(mline) == 2 assert mline.dxf.count == 2, 'should be a callback to __len__()'
def __init__(self, doc: 'Drawing' = None): super().__init__(doc) # todo: MLEADER implementation self.tags = Tags()
def test_get_handle_105(self): tags = Tags.from_text(TESTHANDLE105) assert 'F105' == tags.get_handle()
def test_invalid_group_code_raises_value_error(): tags = Tags.from_text("5\ninvalid\n") with pytest.raises(const.DXFValueError): parse_items(tags)
def _write_header(self, tagwriter: "TagWriter") -> None: tagwriter.write_tags(Tags([self._dxftype, self.flags]))