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
Exemple #2
0
 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
Exemple #3
0
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
Exemple #4
0
    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))
Exemple #5
0
 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())
Exemple #6
0
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'
Exemple #9
0
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
Exemple #10
0
    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
Exemple #11
0
    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'))
Exemple #13
0
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')
Exemple #14
0
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
Exemple #15
0
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)
Exemple #18
0
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))
Exemple #20
0
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