Beispiel #1
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
Beispiel #2
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)
Beispiel #3
0
    def load_entities(self, start: int, requested_types: Iterable[str] = None) -> Iterable[DXFGraphic]:
        def to_str(data: bytes) -> str:
            return data.decode(self.encoding).replace('\r\n', '\n')

        factory = EntityFactory()
        index = start
        entry = self.structure.index[index]
        self.file.seek(entry.location)
        while entry.value != 'ENDSEC':
            index += 1
            next_entry = self.structure.index[index]
            size = next_entry.location - entry.location
            data = self.file.read(size)
            if entry.value in requested_types:
                xtags = ExtendedTags.from_text(to_str(data))
                yield factory.entity(xtags)
            entry = next_entry
Beispiel #4
0
    def __init__(self, dxfversion=DXF2013):
        self.entitydb = EntityDB()
        self.dxffactory = EntityFactory(self)
        self.tracker = Tracker()
        target_dxfversion = dxfversion.upper()
        self._dxfversion: str = acad_release_to_dxf_version.get(
            target_dxfversion, target_dxfversion)
        if self._dxfversion not in versions_supported_by_new:
            raise DXFVersionError(
                f'Unsupported DXF version "{self.dxfversion}".')
        # Store original dxf version if loaded (and maybe converted R13/14) from file.
        self._loaded_dxfversion: Optional[str] = None
        self.encoding: str = 'cp1252'  # read/write
        self.filename: Optional[str] = None

        # named objects dictionary
        self.rootdict: 'Dictionary' = None

        # DXF sections
        self.header: HeaderSection = None
        self.classes: ClassesSection = None
        self.tables: TablesSection = None
        self.blocks: BlocksSection = None
        self.entities: EntitySection = None
        self.objects: ObjectsSection = None

        # DXF R2013 and later
        self.acdsdata: AcDsDataSection = None

        self.stored_sections = []
        self.layouts: Layouts = None
        self.groups: GroupCollection = None
        self.materials: MaterialCollection = None
        self.mleader_styles: MLeaderStyleCollection = None
        self.mline_styles: MLineStyleCollection = None
        self._acad_compatible = True  # will generated DXF file compatible with AutoCAD
        self._dimension_renderer = DimensionRenderer(
        )  # set DIMENSION rendering engine
        self._acad_incompatibility_reason = set(
        )  # avoid multiple warnings for same reason
        # Don't create any new entities here:
        # New created handles could collide with handles loaded from DXF file.
        assert len(self.entitydb) == 0
Beispiel #5
0
    def __init__(self, dxfversion=DXF2013):
        self.entitydb = EntityDB()
        self.dxffactory = EntityFactory(self)
        self.tracker = Tracker()  # still required

        # Targeted DXF version, but drawing could be exported as another DXF version.
        # If target version is set, it is possible to warn user, if they try to use unsupported features, where they
        # use it and not at exporting, where the location of the code who created that features is not known.
        target_dxfversion = dxfversion.upper()
        self._dxfversion = acad_release_to_dxf_version.get(
            target_dxfversion, target_dxfversion)
        self._loaded_dxfversion = None  # if loaded from file, store original dxf version
        self.encoding = 'cp1252'
        self.filename = None  # type: str # read/write

        # named objects dictionary
        self.rootdict = None  # type: Dictionary

        # DXF sections
        self.header = None  # type: HeaderSection
        self.classes = None  # type: ClassesSection
        self.tables = None  # type: TablesSection
        self.blocks = None  # type: BlocksSection
        self.entities = None  # type: EntitySection
        self.objects = None  # type: ObjectsSection

        # DXF R2013 and later
        self.acdsdata = None  # type: AcDsDataSection

        self.stored_sections = []
        self.layouts = None  # type: Layouts
        self.groups = None  # type: GroupCollection  # read only
        self.materials = None  # type: MaterialCollection # read only
        self.mleader_styles = None  # type: MLeaderStyleCollection # read only
        self.mline_styles = None  # type: MLineStyleCollection # read only
        self._acad_compatible = True  # will generated DXF file compatible with AutoCAD
        self._dimension_renderer = DimensionRenderer(
        )  # set DIMENSION rendering engine
        self._acad_incompatibility_reason = set(
        )  # avoid multiple warnings for same reason
        # Don't create any new entities here:
        # New created handles could collide with handles loaded from DXF file.
        assert len(self.entitydb) == 0
Beispiel #6
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