def test_xrecord_with_long_closing_tag(): tags = ExtendedTags(Tags.from_text(XRECORD_APP_DATA_LONG_CLOSING_TAG)) assert tags.dxftype() == "XRECORD" # real app data just exists only in the base class, app data marker in AcDbXrecord are just tags, interpreted by # the associated application assert len(tags.appdata) == 1 assert len(tags.subclasses[1]) == 35
def entity(self, tags: Union['ExtendedTags', 'Tags']) -> 'DXFEntity': if not isinstance(tags, ExtendedTags): tags = ExtendedTags(tags) dxftype = tags.dxftype() class_ = ENTITY_CLASSES.get(dxftype, DEFAULT_CLASS) entity = class_.load(tags, self.doc) return entity.cast() if hasattr(entity, 'cast') else entity
def test_poyline_with_xdata(): xdata = XData( ExtendedTags( filter_invalid_xdata_group_codes( Tags.from_text(POLYLINE_WITH_INVALID_XDATA))).xdata) assert len(xdata) == 2 assert len(xdata.get("AVE_ENTITY_MATERIAL")) == 27
def _build(self, entities: Iterator[DXFTag]) -> None: section_head = next(entities) if section_head[0] != (0, 'SECTION') or section_head[1] != (2, 'CLASSES'): raise DXFStructureError("Critical structure error in CLASSES section.") for class_tags in entities: self.register(ExtendedTags(class_tags))
def _build_table_entries(self, entities: Iterator[Tags]) -> None: table_head = next(entities) if table_head[0].value != 'TABLE': raise DXFStructureError("Critical structure error in TABLES section.") self._dxfname = table_head[1].value self._table_header = ExtendedTags(table_head) # do not store the table head in the entity database for table_entry in entities: self._append_entry_handle(table_entry.get_handle())
def test_write_dxf_r12(): viewport = TEST_CLASS.from_text(ENTITY_R12) collector = TagCollector(dxfversion=DXF12, optional=True) viewport.export_dxf(collector) xdata = ExtendedTags(DXFTag(c, v) for c, v in collector.tags).xdata[0] assert xdata[0] == (1001, 'ACAD') assert xdata[1] == (1000, 'MVIEW') assert xdata[2] == (1002, '{') assert xdata[-1] == (1002, '}')
def test_empty_section(): sec = load_section(EMPTYSEC, "CLASSES") cls_entities = [factory.load(ExtendedTags(e)) for e in sec] section = ClassesSection(None, iter(cls_entities)) stream = StringIO() section.export_dxf(TagWriter(stream)) result = stream.getvalue() stream.close() assert EMPTYSEC == result
def test_xrecord_with_group_code_102(): tags = ExtendedTags(Tags.from_text(XRECORD_WITH_GROUP_CODE_102)) assert tags.dxftype() == 'XRECORD' assert len(tags.appdata) == 1 assert tags.noclass[2] == (102, 0) # 0 == index in appdata list assert tags.appdata[0][0] == (102, '{ACAD_REACTORS') xrecord = tags.get_subclass('AcDbXrecord') assert xrecord[2] == (102, 'ACAD_ROUNDTRIP_PRE2007_TABLESTYLE') assert len(list(tags)) * 2 + 1 == len(XRECORD_WITH_GROUP_CODE_102.split('\n')) # +1 == appending '\n'
def modelspace(filename: str, types: Iterable[str] = None) -> Iterable[DXFGraphic]: """ Iterate over all modelspace entities as :class:`DXFGraphic` objects of a seekable file. Use this function to 'quick' iterate over modelspace entities of a DXF file, filtering DXF types may speed up things if many entity types will be skipped. Args: filename: filename of a seekable DXF file types: DXF types like ``['LINE', '3DFACE']`` which should be returned, ``None`` returns all supported types. """ info = dxf_file_info(filename) prev_code: int = -1 prev_value: str = '' entities = False requested_types = _requested_types(types) with open(filename, mode='rt', encoding=info.encoding) as fp: tagger = low_level_tagger(fp) queued: Optional[DXFEntity] = None tags: List[DXFTag] = [] factory = EntityFactory() linked_entity = entity_linker() for tag in tag_compiler(tagger): code = tag.code value = tag.value if entities: if code == 0 and value == 'ENDSEC': if queued: yield queued return if code == 0: if len(tags) and tags[0].value in requested_types: entity = factory.entity(ExtendedTags(tags)) if not linked_entity( entity) and entity.dxf.paperspace == 0: if queued: # queue one entity for collecting linked entities (VERTEX, ATTRIB) yield queued queued = entity tags = [tag] else: tags.append(tag) continue # if entities - nothing else matters elif code == 2 and prev_code == 0 and prev_value == 'SECTION': entities = (value == 'ENTITIES') prev_code = code prev_value = value
def load(cls: Type[T], tags: Union[ExtendedTags, Tags], doc: 'Drawing' = None) -> T: """ Constructor to generate entities loaded from DXF files (untrusted environment) Args: tags: DXF tags as Tags() or ExtendedTags() doc: DXF Document (internal API) """ if not isinstance(tags, ExtendedTags): tags = ExtendedTags(tags) entity = cls(doc) # bare minimum setup entity.load_tags(tags) return entity
def modelspace(self) -> Iterable[DXFGraphic]: """ Returns an iterator for all supported DXF entities in the modelspace. These entities are regular :class:`~ezdxf.entities.DXFGraphic` objects but without a valid document assigned. It is **not** possible to add these entities to other `ezdxf` documents. It is only possible to recreate the objects by factory functions base on attributes of the source entity. For MESH, POLYMESH and POLYFACE it is possible to use the :class:`~ezdxf.render.MeshTransformer` class to render (recreate) this objects as new entities in another document. """ if self._fp_entities_section is None: self._fp_entities_section = self._seek_to_section('ENTITIES') self.file.seek(self._fp_entities_section) tags = [] factory = EntityFactory() polyline: Optional[Polyline] = None for tag in tag_compiler(low_level_tagger(self.file)): if tag.code == 0: # start new entity if len(tags): xtags = ExtendedTags(tags) dxftype = xtags.dxftype() if dxftype in SUPPORTED_DXF_TYPES: entity = factory.entity(xtags) if dxftype == 'SEQEND': if polyline is not None: polyline.seqend = entity yield polyline polyline = None # suppress all other SEQEND entities -> ATTRIB elif dxftype == 'VERTEX' and polyline is not None: # vertices without POLYLINE are DXF structure errors, but here just ignore it. polyline.vertices.append(entity) elif dxftype == 'POLYLINE': polyline = entity else: # POLYLINE without SEQEND is a DXF structure error, but here just ignore it. # By using this add-on be sure to get valid DXF files. polyline = None yield entity if tag == (0, 'ENDSEC'): break tags = [tag] else: tags.append(tag)
def test_group_code_1000_outside_XDATA(): tags = ExtendedTags(Tags.from_text(BLOCKBASEPOINTPARAMETER_CVIL_3D_2018)) assert tags.dxftype() == 'BLOCKBASEPOINTPARAMETER' assert len(tags.subclasses) == 6 block_base_point_parameter = tags.get_subclass('AcDbBlockBasepointParameter') assert len(block_base_point_parameter) == 3 assert block_base_point_parameter[0] == (100, 'AcDbBlockBasepointParameter') assert block_base_point_parameter[1] == (1011, (0., 0., 0.)) assert block_base_point_parameter[2] == (1012, (0., 0., 0.)) block_element = tags.get_subclass('AcDbBlockElement') assert block_element[4] == (1071, 0) stream = StringIO() tagwriter = TagWriter(stream) tagwriter.write_tags(tags) lines = stream.getvalue() stream.close() assert len(lines.split('\n')) == len(BLOCKBASEPOINTPARAMETER_CVIL_3D_2018.split('\n'))
def test_xrecord_with_long_closing_tag(): tags = ExtendedTags(Tags.from_text(XRECORD_APP_DATA_LONG_CLOSING_TAG)) assert tags.dxftype() == 'XRECORD' assert len(tags.appdata) == 5 attr_rec = tags.appdata[4] assert attr_rec[0] == (102, '{ATTRRECORD') assert attr_rec[1] == (341, '2FD') assert len(list(tags)) * 2 + 1 == len( XRECORD_APP_DATA_LONG_CLOSING_TAG.split('\n')) # +1 == appending '\n' # test USUAL_102_TAG_INSIDE_APP_DATA attr_rec = tags.appdata[1] assert attr_rec[0] == (102, '{ATTRRECORD') assert attr_rec[1] == (341, '2FA') assert attr_rec[2] == (102, 'USUAL_102_TAG_INSIDE_APP_DATA') # test USUAL_102_TAG_OUTSIDE_APP_DATA xrecord = tags.get_subclass('AcDbXrecord') assert xrecord[4] == (102, 'USUAL_102_TAG_OUTSIDE_APP_DATA')
def single_pass_modelspace( stream: BinaryIO, types: Iterable[str] = None, errors: str = "surrogateescape", ) -> Iterable[DXFGraphic]: """Iterate over all modelspace entities as :class:`DXFGraphic` objects in one single pass. Use this function to 'quick' iterate over modelspace entities of a **not** seekable binary DXF stream, filtering DXF types may speed up things if many entity types will be skipped. Args: stream: (not seekable) binary DXF stream types: DXF types like ``['LINE', '3DFACE']`` which should be returned, ``None`` returns all supported types. 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: Invalid or incomplete DXF file UnicodeDecodeError: if `errors` is "strict" and a decoding error occurs """ fetch_header_var: Optional[str] = None encoding = "cp1252" version = "AC1009" prev_code: int = -1 prev_value: str = "" entities = False requested_types = _requested_types(types) for code, value in binary_tagger(stream): if code == 0 and value == b"ENDSEC": break elif code == 2 and prev_code == 0 and value != b"HEADER": # (0, SECTION), (2, name) # First section is not the HEADER section entities = value == b"ENTITIES" break elif code == 9 and value == b"$DWGCODEPAGE": fetch_header_var = "ENCODING" elif code == 9 and value == b"$ACADVER": fetch_header_var = "VERSION" elif fetch_header_var == "ENCODING": encoding = toencoding(value.decode()) fetch_header_var = None elif fetch_header_var == "VERSION": version = value.decode() fetch_header_var = None prev_code = code if version >= "AC1021": encoding = "utf-8" queued: Optional[DXFGraphic] = None tags: List[DXFTag] = [] linked_entity = entity_linker() for tag in tag_compiler(binary_tagger(stream, encoding, errors)): code = tag.code value = tag.value if entities: if code == 0 and value == "ENDSEC": if queued: yield queued return if code == 0: if len(tags) and tags[0].value in requested_types: entity = cast(DXFGraphic, factory.load(ExtendedTags(tags))) if not linked_entity( entity) and entity.dxf.paperspace == 0: # queue one entity for collecting linked entities: # VERTEX, ATTRIB if queued: yield queued queued = entity tags = [tag] else: tags.append(tag) continue # if entities - nothing else matters elif code == 2 and prev_code == 0 and prev_value == "SECTION": entities = value == "ENTITIES" prev_code = code prev_value = value
def modelspace( filename: Filename, types: Iterable[str] = None, errors: str = "surrogateescape", ) -> Iterable[DXFGraphic]: """Iterate over all modelspace entities as :class:`DXFGraphic` objects of a seekable file. Use this function to iterate "quick" over modelspace entities of a DXF file, filtering DXF types may speed up things if many entity types will be skipped. Args: filename: filename of a seekable DXF file types: DXF types like ``['LINE', '3DFACE']`` which should be returned, ``None`` returns all supported types. 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: invalid or incomplete DXF file UnicodeDecodeError: if `errors` is "strict" and a decoding error occurs """ info = dxf_file_info(str(filename)) prev_code: int = -1 prev_value: Any = "" entities = False requested_types = _requested_types(types) with open(filename, mode="rt", encoding=info.encoding, errors=errors) as fp: tagger = ascii_tags_loader(fp) queued: Optional[DXFEntity] = None tags: List[DXFTag] = [] linked_entity = entity_linker() for tag in tag_compiler(tagger): code = tag.code value = tag.value if entities: if code == 0: if len(tags) and tags[0].value in requested_types: entity = factory.load(ExtendedTags(tags)) if (not linked_entity(entity) and entity.dxf.paperspace == 0): # queue one entity for collecting linked entities: # VERTEX, ATTRIB if queued: yield queued # type: ignore queued = entity tags = [tag] else: tags.append(tag) if code == 0 and value == "ENDSEC": if queued: yield queued # type: ignore return continue # if entities - nothing else matters elif code == 2 and prev_code == 0 and prev_value == "SECTION": entities = value == "ENTITIES" prev_code = code prev_value = value
def test_init_one_tag(): xtags = ExtendedTags([DXFTag(0, "SECTION")]) assert xtags.noclass[0] == (0, "SECTION")
def test_init_with_tags(): tags = Tags.from_text(XTAGS1) xtags = ExtendedTags(tags) assert 3 == len(xtags.subclasses) assert 1 == len(xtags.xdata)
def test_repair_leica_disto_files(): tags = ExtendedTags( filter_subclass_marker(Tags.from_text(LEICA_DISTO_TAGS))) assert 9 == len(tags.noclass) assert 1 == len(tags.subclasses)
def section(): sec = load_section(TESTCLASSES, "CLASSES") cls_entities = [factory.load(ExtendedTags(e)) for e in sec] return ClassesSection(None, iter(cls_entities))
def single_pass_modelspace(stream: BinaryIO, types: Iterable[str] = None) -> Iterable[DXFGraphic]: """ Iterate over all modelspace entities as :class:`DXFGraphic` objects in one single pass. Use this function to 'quick' iterate over modelspace entities of a **not** seekable binary DXF stream, filtering DXF types may speed up things if many entity types will be skipped. Args: stream: (not seekable) binary DXF stream types: DXF types like ``['LINE', '3DFACE']`` which should be returned, ``None`` returns all supported types. """ fetch_header_var: Optional[str] = None encoding = 'cp1252' version = 'AC1009' prev_code: int = -1 prev_value: str = '' entities = False requested_types = _requested_types(types) for code, value in binary_tagger(stream): if code == 0 and value == b'ENDSEC': break elif code == 2 and prev_code == 0 and value != b'HEADER': # (0, SECTION), (2, name) # First section is not the HEADER section entities = (value == b'ENTITIES') break elif code == 9 and value == b'$DWGCODEPAGE': fetch_header_var = 'ENCODING' elif code == 9 and value == b'$ACADVER': fetch_header_var = 'VERSION' elif fetch_header_var == 'ENCODING': encoding = toencoding(value.decode()) fetch_header_var = None elif fetch_header_var == 'VERSION': version = value.decode() fetch_header_var = None prev_code = code if version >= 'AC1021': encoding = 'utf-8' queued: Optional[DXFEntity] = None tags: List[DXFTag] = [] factory = EntityFactory() linked_entity = entity_linker() for tag in tag_compiler(binary_tagger(stream, encoding)): code = tag.code value = tag.value if entities: if code == 0 and value == 'ENDSEC': if queued: yield queued return if code == 0: if len(tags) and tags[0].value in requested_types: entity = factory.entity(ExtendedTags(tags)) if not linked_entity(entity) and entity.dxf.paperspace == 0: if queued: # queue one entity for collecting linked entities (VERTEX, ATTRIB) yield queued queued = entity tags = [tag] else: tags.append(tag) continue # if entities - nothing else matters elif code == 2 and prev_code == 0 and prev_value == 'SECTION': entities = (value == 'ENTITIES') prev_code = code prev_value = value
def load_tags_fast(cls, subclass, data): ns = DXFNamespace(entity=cls()) mapping = group_code_mapping(subclass) proc = SubclassProcessor(ExtendedTags(tags=data)) unprocessed_tags = proc.fast_load_dxfattribs(ns, mapping, 0) return ns, unprocessed_tags