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()
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
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.")
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.')
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.")
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)
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
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)
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__())
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)
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
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')
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
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
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()))
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)
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 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)
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)
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)
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
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)
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)
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__())
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)
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
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))
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 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]
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]