Ejemplo n.º 1
0
    def _setup_tables(self, entities):
        section_head = next(entities)
        if section_head[0] != (0, 'SECTION') or section_head[1] != (2,
                                                                    'TABLES'):
            raise DXFStructureError(
                "Critical structure error in TABLES section.")

        table_entities = []
        table_name = None
        for entity in entities:
            if isinstance(entity, ExtendedTags):
                entity = entity.noclass
            if entity[0] == (0, 'TABLE'):
                table_entities = [entity]  # collect table head!
                if len(entity) < 2 or entity[1].code != 2:
                    raise DXFStructureError(
                        'DXFStructureError: missing required table name tag (2, name) at start of table.'
                    )
                table_name = entity[1].value
            elif entity[0] == (0, 'ENDTAB'):  # do not collect (0, 'ENDTAB')
                self._new_table(table_name, table_entities)
                table_entities = [
                ]  # collect entities outside of tables, but ignore it
                table_name = None
            else:  # collect table entries
                table_entities.append(entity)

        self._create_missing_tables()
Ejemplo n.º 2
0
 def load_table(self, tags: 'Tags') -> None:
     for handle, sort_handle in take2(tags):
         if handle.code != 331:
             raise DXFStructureError('Invalid handle code {}, expected 331'.format(handle.code))
         if sort_handle.code != 5:
             raise DXFStructureError('Invalid sort handle code {}, expected 5'.format(handle.code))
         self.table[handle.value] = sort_handle.value
Ejemplo n.º 3
0
 def load_mesh_data(self, tags: Iterable[DXFTag], version: int = 2):
     # group codes of version 1 and 2 differ, see DXF reference R2009
     src, target = mesh_group_codes(version)
     face_indices = {97, 98, 99}
     face: List[int] = []
     for code, value in tags:
         if code == src:
             self.source_vertices.append(value)
         elif code == target:
             self.target_vertices.append(value)
         elif code in face_indices:
             if code == 97 and len(face):
                 if len(face) != 3:
                     raise DXFStructureError(
                         f"GEODATA face definition error: invalid index "
                         f"count {len(face)}.")
                 self.faces.append(tuple(face))
                 face.clear()
             face.append(value)
     if face:  # collect last face
         self.faces.append(tuple(face))
     if len(self.source_vertices) != len(self.target_vertices):
         raise DXFStructureError(
             "GEODATA mesh definition error: source and target point count "
             "does not match.")
Ejemplo n.º 4
0
 def __init__(self, name: Filename):
     self.structure, self.sections = self._load_index(name)
     self.file: BinaryIO = open(name, mode='rb')
     if 'ENTITIES' not in self.sections:
         raise DXFStructureError('ENTITIES section not found.')
     if self.structure.version > 'AC1009' and 'OBJECTS' not in self.sections:
         raise DXFStructureError('OBJECTS section not found.')
Ejemplo n.º 5
0
 def __init__(self, name: Filename, errors: str = "surrogateescape"):
     self.structure, self.sections = self._load_index(str(name))
     self.errors = errors
     self.file: BinaryIO = open(name, mode="rb")
     if "ENTITIES" not in self.sections:
         raise DXFStructureError("ENTITIES section not found.")
     if self.structure.version > "AC1009" and "OBJECTS" not in self.sections:
         raise DXFStructureError("OBJECTS section not found.")
Ejemplo n.º 6
0
def explode_block_reference(block_ref: 'Insert',
                            target_layout: 'BaseLayout') -> EntityQuery:
    """
    Explode a block reference into single DXF entities.

    Transforms the block entities into the required WCS location by applying the block reference
    attributes `insert`, `extrusion`, `rotation` and the scaling values `xscale`, `yscale` and `zscale`.
    Multiple inserts by row and column attributes is not supported.

    Returns an EntityQuery() container with all exploded DXF entities.

    Args:
        block_ref: Block reference entity (INSERT)
        target_layout: explicit target layout for exploded DXF entities

    .. warning::

        **Non uniform scaling** lead to incorrect results for text entities (TEXT, MTEXT, ATTRIB) and
        some other entities like ELLIPSE, SHAPE, HATCH with arc or ellipse path segments and
        POLYLINE/LWPOLYLINE with arc segments.

    (internal API)

    """
    if target_layout is None:
        raise DXFStructureError('Target layout is None.')

    if block_ref.doc is None:
        raise DXFStructureError(
            'Block reference has to be assigned to a DXF document.')

    entitydb = block_ref.doc.entitydb
    if entitydb is None:
        raise DXFStructureError(
            'Exploding a block reference requires an entity database.')

    entities = []

    for entity in virtual_block_reference_entities(block_ref):
        entitydb.add(entity)
        target_layout.add_entity(entity)
        entities.append(entity)

    # Process attached ATTRIB entities:
    for attrib in block_ref.attribs:
        # Attached ATTRIB entities are already located in the WCS
        target_layout.add_entity(attrib)
        entities.append(attrib)

    # Unlink attributes else they would be destroyed by deleting the block reference.
    block_ref.attribs = []
    source_layout = block_ref.get_layout()
    if source_layout is not None:
        # Remove and destroy exploded INSERT if assigned to a layout
        source_layout.delete_entity(block_ref)
    else:
        entitydb.delete_entity(block_ref)
    return EntityQuery(entities)
Ejemplo n.º 7
0
 def load_table(self, tags: "Tags") -> None:
     for handle, sort_handle in take2(tags):
         if handle.code != 331:
             raise DXFStructureError(
                 f"Invalid handle code {handle.code}, expected 331"
             )
         if sort_handle.code != 5:
             raise DXFStructureError(
                 f"Invalid sort handle code {handle.code}, expected 5"
             )
         self.table[handle.value] = sort_handle.value
Ejemplo n.º 8
0
def explode_entity(entity: 'DXFGraphic',
                   target_layout: 'BaseLayout' = None) -> 'EntityQuery':
    """
    Explode parts of an entity as primitives into target layout, if target layout is ``None``,
    the target layout is the layout of the source entity.

    Returns an :class:`~ezdxf.query.EntityQuery` container with all DXF parts.

    Args:
        entity: DXF entity to explode, has to have a :meth:`virtual_entities()` method
        target_layout: target layout for DXF parts, ``None`` for same layout as source entity

    .. versionadded:: 0.12

    (internal API)

    """
    dxftype = entity.dxftype()

    if not hasattr(entity, 'virtual_entities'):
        raise DXFTypeError(f'Can not explode entity {dxftype}.')

    if entity.doc is None:
        raise DXFStructureError(
            f'{dxftype} has to be assigned to a DXF document.')

    entitydb = entity.doc.entitydb
    if entitydb is None:
        raise DXFStructureError(
            f'Exploding {dxftype} requires an entity database.')

    if target_layout is None:
        target_layout = entity.get_layout()
        if target_layout is None:
            raise DXFStructureError(
                f'{dxftype} without layout assigment, specify target layout.')

    entities = []

    for e in entity.virtual_entities():
        entitydb.add(e)
        target_layout.add_entity(e)
        entities.append(e)

    source_layout = entity.get_layout()
    if source_layout is not None:
        source_layout.delete_entity(entity)
    else:
        entitydb.delete_entity(entity)
    return EntityQuery(entities)
Ejemplo n.º 9
0
 def _get_matrix(self, code: int) -> 'Matrix44':
     subclass = self.tags.subclasses[1]  # always 2nd subclass
     try:
         return matrix_accessors.get_matrix(subclass, code)
     except DXFStructureError:
         raise DXFStructureError(
             'Invalid transformation matrix in entity ' + self.__str__())
Ejemplo n.º 10
0
 def load(self, entities: Iterator['DXFEntity']) -> None:
     """ Loading interface. (internal API)"""
     self._head = next(entities)
     if self._head.dxftype() != 'TABLE':
         raise DXFStructureError("Critical structure error in TABLES section.")
     for table_entry in entities:
         self._append(table_entry)
Ejemplo n.º 11
0
 def from_tags(cls, tags: Tags) -> 'ViewportData':
     vp_data = cls()
     try:
         vp_data.view_target_point = tags[4].value
         vp_data.view_direction_vector = tags[5].value
         vp_data.view_twist_angle = tags[6].value
         vp_data.view_height = tags[7].value
         vp_data.view_center_point = tags[8].value, tags[9].value
         vp_data.perspective_lens_length = tags[10].value
         vp_data.front_clip_plane_z_value = tags[11].value
         vp_data.back_clip_plane_z_value = tags[12].value
         vp_data.view_mode = tags[13].value
         vp_data.circle_zoom = tags[14].value
         vp_data.fast_zoom = tags[15].value
         vp_data.ucs_icon = tags[16].value
         vp_data.snap = tags[17].value
         vp_data.grid = tags[18].value
         vp_data.snap_style = tags[19].value
         vp_data.snap_isopair = tags[20].value
         vp_data.snap_angle = tags[21].value
         vp_data.snap_base_point = tags[22].value, tags[23].value
         vp_data.snap_spacing = tags[24].value, tags[25].value
         vp_data.grid_spacing = tags[26].value, tags[27].value
         vp_data.hidden_plot = tags[28].value
     except IndexError:  # internal exception
         raise DXFStructureError("Invalid viewport entity - missing data")
     vp_data.frozen_layers = [frozen_layer_name.value for frozen_layer_name in tags[30:-2]]
     return vp_data
Ejemplo n.º 12
0
 def setup_rootdict(self) -> Dictionary:
     """ Create a root dictionary. Has to be the first object in the objects section. (internal API) """
     if len(self):
         raise DXFStructureError("Can not create root dictionary in none empty objects section.")
     logger.debug('Creating ROOT dictionary.')
     # root directory has no owner
     return self.add_dictionary(owner='0')
Ejemplo n.º 13
0
    def _build(self, tags: Iterator[DXFTag]) -> None:
        section_tag = next(tags)
        name_tag = next(tags)

        if section_tag != (0, 'SECTION') or name_tag != (2, 'HEADER'):
            raise 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
Ejemplo n.º 14
0
    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 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
Ejemplo n.º 15
0
 def __getattr__(self, key):
     try:
         return self._sections[Sections.key(key)]
     except KeyError:  # internal exception
         # DXFStructureError because a requested section is not present, maybe a typo, but usual a hint for an
         # invalid DXF file.
         raise DXFStructureError('{} section not found'.format(key.upper()))
Ejemplo n.º 16
0
 def from_tags(cls, tags: Tags):
     assert tags is not None
     # Expected DXF structure:
     # [(102, '{ACAD_XDICTIONARY', (360, handle), (102, '}')]
     if len(tags) != 3 or tags[1].code != XDICT_HANDLE_CODE:
         raise DXFStructureError("ACAD_XDICTIONARY error.")
     return cls(tags[1].value)
Ejemplo n.º 17
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))
Ejemplo n.º 18
0
    def explode(self, target_layout: "BaseLayout" = None) -> "EntityQuery":
        """Explode block reference entities into target layout, if target
        layout is ``None``, the target layout is the layout of the block
        reference. This method destroys the source block reference entity.

        Transforms the block entities into the required :ref:`WCS` location by
        applying the block reference attributes `insert`, `extrusion`,
        `rotation` and the scaling values `xscale`, `yscale` and `zscale`.

        Attached ATTRIB entities are converted to TEXT entities, this is the
        behavior of the BURST command of the AutoCAD Express Tools.

        Returns an :class:`~ezdxf.query.EntityQuery` container with all
        "exploded" DXF entities.

        .. warning::

            **Non uniform scaling** may lead to incorrect results for text
            entities (TEXT, MTEXT, ATTRIB) and maybe some other entities.

        Args:
            target_layout: target layout for exploded entities, ``None`` for
                same layout as source entity.

        """
        if target_layout is None:
            target_layout = self.get_layout()
            if target_layout is None:
                raise DXFStructureError(
                    "INSERT without layout assignment, specify target layout.")
        return explode_block_reference(self, target_layout=target_layout)
Ejemplo n.º 19
0
 def get_viewport_data(self) -> 'ViewportData':
     try:
         extended_dxf_data = self.tags.get_xdata('ACAD')
     except DXFValueError:
         DXFStructureError("Invalid viewport entity - missing data")
     else:
         return ViewportData.from_tags(extended_dxf_data)
Ejemplo n.º 20
0
    def _load(self, entities: List['DXFEntity']) -> None:
        section_head = entities[0]  # type: DXFTagStorage
        if section_head.dxftype() != 'SECTION' or section_head.base_class[1] != (2, 'TABLES'):
            raise DXFStructureError("Critical structure error in TABLES section.")
        del entities[0]  # delete first entity (0, SECTION)

        table_records = []
        table_name = None
        for entity in entities:
            if entity.dxftype() == 'TABLE':
                if len(table_records):
                    # TABLE entity without preceding ENDTAB entity, should we care?
                    logger.debug('Ignore missing ENDTAB entity in table "{}".'.format(table_name))
                    self._load_table(table_name, table_records)
                table_name = entity.dxf.name
                table_records = [entity]  # collect table head
            elif entity.dxftype() == 'ENDTAB':  # do not collect (0, 'ENDTAB')
                self._load_table(table_name, table_records)
                table_records = []  # collect entities outside of tables, but ignore it
            else:  # collect table entries
                table_records.append(entity)

        if len(table_records):
            # last ENDTAB entity is missing, should we care?
            logger.debug('Ignore missing ENDTAB entity in table "{}".'.format(table_name))
            self._load_table(table_name, table_records)
Ejemplo n.º 21
0
 def load_dxf_attribs(self,
                      processor: SubclassProcessor = None
                      ) -> 'DXFNamespace':
     dxf = super().load_dxf_attribs(processor)
     if processor:
         try:
             tags = processor.subclasses[1]
         except IndexError:
             raise DXFStructureError(
                 'Missing subclass AcDbXrecord in XRecord (#{})'.format(
                     dxf.handle))
         start_index = 1
         if len(tags) > 1:
             # first tag is group code 280, but not for DXF R13/R14
             # for testing doc may be None, but then doc also can not be R13/R14 - ezdxf does not create R13/R14
             if self.doc is None or self.doc.dxfversion >= DXF2000:
                 code, value = tags[1]
                 if code == 280:
                     dxf.cloning = value
                     start_index = 2
                 else:  # just log recoverable error
                     logger.info(
                         'XRecord (#{}): expected group code 280 as first tag in AcDbXrecord'
                         .format(dxf.handle))
         self.tags = Tags(tags[start_index:])
     return dxf
Ejemplo n.º 22
0
    def explode(self, target_layout: 'BaseLayout' = None, non_uniform_scaling=False) -> 'EntityQuery':
        """
        Explode block reference entities into target layout, if target layout is ``None``, the target layout is the
        layout of the block reference.

        Transforms the block entities into the required :ref:`WCS` location by applying the block reference
        attributes `insert`, `extrusion`, `rotation` and the scaling values `xscale`, `yscale` and `zscale`.
        Multiple inserts by row and column attributes is not supported.

        Returns an :class:`~ezdxf.query.EntityQuery` container with all "exploded" DXF entities.

        .. warning::

            **Non uniform scaling** lead to incorrect results for text entities (TEXT, MTEXT, ATTRIB) and
            some other entities like ELLIPSE, SHAPE, HATCH with arc or ellipse path segments and and
            POLYLINE/LWPOLYLINE with arc segments.

        Args:
            target_layout: target layout for exploded entities, ``None`` for same layout as source entity.
            non_uniform_scaling: enable non uniform scaling if ``True``, see warning

        .. versionadded:: 0.12
            experimental feature

        """
        if target_layout is None:
            target_layout = self.get_layout()
            if target_layout is None:
                raise DXFStructureError('INSERT without layout assigment, specify target layout.')

        if non_uniform_scaling is False and not self.has_uniform_scaling:
            return EntityQuery()

        return explode_block_reference(self, target_layout=target_layout)
Ejemplo n.º 23
0
def explode_entity(entity: "DXFGraphic",
                   target_layout: "BaseLayout" = None) -> "EntityQuery":
    """Explode parts of an entity as primitives into target layout, if target
    layout is ``None``, the target layout is the layout of the source entity.

    Returns an :class:`~ezdxf.query.EntityQuery` container with all DXF parts.

    Args:
        entity: DXF entity to explode, has to have a :meth:`virtual_entities()`
            method
        target_layout: target layout for DXF parts, ``None`` for same layout as
            source entity

    (internal API)

    """
    dxftype = entity.dxftype()
    virtual_entities = getattr(entity, "virtual_entities")
    if virtual_entities is None or dxftype in EXCLUDE_FROM_EXPLODE:
        raise DXFTypeError(f"Can not explode entity {dxftype}.")

    if entity.doc is None:
        raise DXFStructureError(
            f"{dxftype} has to be assigned to a DXF document.")

    entitydb = entity.doc.entitydb
    if entitydb is None:
        raise DXFStructureError(
            f"Exploding {dxftype} requires an entity database.")

    if target_layout is None:
        target_layout = entity.get_layout()
        if target_layout is None:
            raise DXFStructureError(
                f"{dxftype} without layout assignment, specify target layout.")

    entities = []
    for e in virtual_entities():
        target_layout.add_entity(e)
        entities.append(e)

    source_layout = entity.get_layout()
    if source_layout is not None:
        source_layout.delete_entity(entity)
    else:
        entitydb.delete_entity(entity)
    return EntityQuery(entities)
Ejemplo n.º 24
0
 def _get_matrix(self, code: int) -> 'Matrix44':
     subclass = self.tags.subclasses[
         4]  # always 5th subclass, Surface has no transform matrix, but inherited classes
     try:
         return matrix_accessors.get_matrix(subclass, code)
     except DXFStructureError:
         raise DXFStructureError(
             'Invalid transformation matrix in entity ' + self.__str__())
Ejemplo n.º 25
0
    def _build(self, entities: Iterator['DXFObject']) -> None:
        section_head = next(entities)  # type: DXFTagStorage

        if section_head.dxftype() != 'SECTION' or section_head.base_class[1] != (2, 'OBJECTS'):
            raise DXFStructureError("Critical structure error in 'OBJECTS' section.")

        for entity in entities:
            self._entity_space.add(entity)
Ejemplo n.º 26
0
    def _build(self, entities: Iterator[Tags]) -> None:
        section_head = next(entities)
        if section_head[0] != (0, 'SECTION') or section_head[1] != (2, 'ACDSDATA'):
            raise DXFStructureError("Critical structure error in ACDSDATA section.")

        self.section_info = section_head
        for entity in entities:
            self._append_entity(AcDsData(entity))  # tags have no subclasses
Ejemplo n.º 27
0
    def load(self, entities: Iterator[DXFEntity]) -> None:
        section_head = next(entities)  # type: DXFTagStorage

        if section_head.dxftype() != 'SECTION' or section_head.base_class[1] != (2, 'CLASSES'):
            raise DXFStructureError("Critical structure error in CLASSES section.")

        for cls_entity in entities:
            self.register(cast(DXFClass, cls_entity))
Ejemplo n.º 28
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())
Ejemplo n.º 29
0
 def process_vertices():
     try:
         vertex_count_index = tags.tag_index(92)
     except DXFValueError:
         raise DXFStructureError(COUNT_ERROR_MSG.format(handle, 'vertex'))
     vertices = create_vertex_array(tags, vertex_count_index + 1)
     # replace vertex count tag and all vertex tags by MeshVertexArray()
     end_index = vertex_count_index + 1 + len(vertices)
     tags[vertex_count_index:end_index] = [vertices]
Ejemplo n.º 30
0
 def process_creases():
     try:
         crease_count_index = tags.tag_index(95)
     except DXFValueError:
         raise DXFStructureError(COUNT_ERROR_MSG.format(handle, 'crease'))
     else:
         creases = create_crease_array(tags, crease_count_index + 1)
         # replace crease count tag and all crease tags by CreaseArray()
         end_index = crease_count_index + 1 + len(creases.value)
         tags[crease_count_index:end_index] = [creases]