def __init__(self): from ezdxf.entitydb import EntitySpace super().__init__() # Store entities in the block_record instead of BlockLayout and Layout, # because BLOCK_RECORD is also the hard owner of all the entities. self.entity_space = EntitySpace() self.block: Optional[Block] = None self.endblk: Optional[EndBlk] = None # stores also the block layout structure self.block_layout: Optional[BlockLayout] = None
def __init__(self, doc: 'Drawing' = None): from ezdxf.entitydb import EntitySpace super().__init__(doc) # Store entities in the block_record instead of BlockLayout and Layout, because BLOCK_RECORD is also the hard # owner of all the entities. self.entity_space = EntitySpace() self.block = None # type: Block self.endblk = None # type: EndBlk # stores also the block layout structure self.block_layout = None # type: BlockLayout
class BlockRecord(DXFEntity): """ DXF BLOCK_RECORD table entity BLOCK_RECORD is the hard owner of all entities in BLOCK definitions, this means owner tag of entities is handle of BLOCK_RECORD. """ DXFTYPE = 'BLOCK_RECORD' DXFATTRIBS = DXFAttributes(base_class, acdb_symbol_table_record, acdb_blockrec) def __init__(self): from ezdxf.entitydb import EntitySpace super().__init__() # Store entities in the block_record instead of BlockLayout and Layout, # because BLOCK_RECORD is also the hard owner of all the entities. self.entity_space = EntitySpace() self.block: Optional[Block] = None self.endblk: Optional[EndBlk] = None # stores also the block layout structure self.block_layout: Optional[BlockLayout] = None def set_block(self, block: 'Block', endblk: 'EndBlk'): self.block = block self.endblk = endblk self.block.dxf.owner = self.dxf.handle self.endblk.dxf.owner = self.dxf.handle def set_entity_space(self, entity_space: 'EntitySpace') -> None: self.entity_space = entity_space def rename(self, name: str) -> None: self.dxf.name = name self.block.dxf.name = name def load_dxf_attribs(self, processor: SubclassProcessor = None ) -> 'DXFNamespace': dxf = super().load_dxf_attribs(processor) if processor: processor.fast_load_dxfattribs(dxf, acdb_blockrec_group_codes, 2) return dxf def export_entity(self, tagwriter: 'TagWriter') -> None: super().export_entity(tagwriter) if tagwriter.dxfversion == DXF12: raise DXFInternalEzdxfError('Exporting BLOCK_RECORDS for DXF R12.') tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table_record.name) tagwriter.write_tag2(SUBCLASS_MARKER, acdb_blockrec.name) self.dxf.export_dxf_attribs(tagwriter, [ 'name', 'layout', 'units', 'explode', 'scale', ]) def export_block_definition(self, tagwriter: 'TagWriter') -> None: """ Exports BLOCK, than all DXF entities and at last the ENDBLK entity, except for *Model_space and *Paper_Pacer, their entities are stored in the entities section. """ if self.block_layout is not None: self.block_layout.update_block_flags() self.block.export_dxf(tagwriter) if not (self.is_modelspace or self.is_active_paperspace): self.entity_space.export_dxf(tagwriter) self.endblk.export_dxf(tagwriter) def destroy(self): """ Destroy associated data: - BLOCK - ENDBLK - all entities stored in this block definition Does not destroy the linked LAYOUT entity, this is the domain of the :class:`Layouts` object, which also should initiate the destruction of 'this' BLOCK_RECORD. """ if not self.is_alive: return self.block.destroy() self.endblk.destroy() for entity in self.entity_space: entity.destroy() # remove attributes to find invalid access after death del self.block del self.endblk del self.block_layout super().destroy() @property def is_active_paperspace(self) -> bool: """ ``True`` if is "active" paperspace layout. """ return self.dxf.name.lower() == '*paper_space' @property def is_any_paperspace(self) -> bool: """ ``True`` if is any kind of paperspace layout. """ return self.dxf.name.lower().startswith('*paper_space') @property def is_modelspace(self) -> bool: """ ``True`` if is the modelspace layout. """ return self.dxf.name.lower() == '*model_space' @property def is_any_layout(self) -> bool: """ ``True`` if is any kind of modelspace or paperspace layout. """ return self.is_modelspace or self.is_any_paperspace @property def is_block_layout(self) -> bool: """ ``True`` if not any kind of modelspace or paperspace layout, just a regular block definition. """ return not self.is_any_layout def add_entity(self, entity: 'DXFGraphic') -> None: """ Add an existing DXF entity to BLOCK_RECORD. Args: entity: :class:`DXFGraphic` """ # assign layout try: entity.set_owner(self.dxf.handle, paperspace=int(self.is_any_paperspace)) except AttributeError: logger.debug(f'Unexpected entity {str(entity)}') self.entity_space.add(entity) def unlink_entity(self, entity: 'DXFGraphic') -> None: """ Unlink `entity` from BLOCK_RECORD. Removes `entity` just from entity space but not from the drawing database. Args: entity: :class:`DXFGraphic` """ if entity.is_alive: self.entity_space.remove(entity) try: entity.set_owner(None) except AttributeError: pass # unsupported entities as DXFTagStorage def delete_entity(self, entity: 'DXFGraphic') -> None: """ Delete `entity` from BLOCK_RECORD entity space and drawing database. Args: entity: :class:`DXFGraphic` """ self.unlink_entity(entity) # 1. unlink from entity space entity.destroy()
class BlockRecord(DXFEntity): """ DXF BLOCK_RECORD table entity BLOCK_RECORD is the hard owner of all entities in BLOCK definitions, this means owner tag of entities is handle of BLOCK_RECORD. """ DXFTYPE = 'BLOCK_RECORD' DXFATTRIBS = DXFAttributes(base_class, acdb_symbol_table_record, acdb_blockrec) def __init__(self, doc: 'Drawing' = None): from ezdxf.entitydb import EntitySpace super().__init__(doc) # Store entities in the block_record instead of BlockLayout and Layout, because BLOCK_RECORD is also the hard # owner of all the entities. self.entity_space = EntitySpace() self.block = None # type: Block self.endblk = None # type: EndBlk # stores also the block layout structure self.block_layout = None # type: BlockLayout def set_block(self, block: 'Block', endblk: 'EndBlk'): self.block = block self.endblk = endblk self.block.dxf.owner = self.dxf.handle self.endblk.dxf.owner = self.dxf.handle def set_entity_space(self, entity_space: 'EntitySpace') -> None: self.entity_space = entity_space def rename(self, name: str) -> None: self.dxf.name = name self.block.dxf.name = name self.block.dxf.name2 = name def load_dxf_attribs(self, processor: SubclassProcessor = None) -> 'DXFNamespace': dxf = super().load_dxf_attribs(processor) if processor: tags = processor.load_dxfattribs_into_namespace(dxf, acdb_blockrec) if len(tags) and False: # deliberately disabled processor.log_unprocessed_tags(tags, subclass=acdb_blockrec.name) return dxf def export_entity(self, tagwriter: 'TagWriter') -> None: super().export_entity(tagwriter) # AcDbEntity export is done by parent class if tagwriter.dxfversion == DXF12: raise DXFInternalEzdxfError('Exporting BLOCK_RECORDS for DXF R12.') tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table_record.name) tagwriter.write_tag2(SUBCLASS_MARKER, acdb_blockrec.name) self.dxf.export_dxf_attribs(tagwriter, ['name', 'layout', 'units', 'explode', 'scale']) def export_block_definition(self, tagwriter: 'TagWriter') -> None: """ Exports BLOCK, than all DXF entities and at last the ENDBLK entity, except for *Model_space and *Paper_Pacer, their entities are stored in the entities section. """ self.block.export_dxf(tagwriter) if not (self.is_modelspace or self.is_active_paperspace): self.entity_space.export_dxf(tagwriter) self.endblk.export_dxf(tagwriter) def destroy(self): """ Destroy associated data: - BLOCK - ENDBLK - all entities stored in this block definition Does not destroy the linked LAYOUT entity, this is the domain of the :class:`Layouts` object, which also should initiate the destruction of 'this' BLOCK_RECORD. """ db = self.entitydb db.delete_entity(self.block) db.delete_entity(self.endblk) for entity in self.entity_space: db.delete_entity(entity) # remove attributes to find invalid access after death del self.block del self.endblk del self.block_layout super().destroy() @property def is_active_paperspace(self) -> bool: """ True if is "active" layout. """ return self.dxf.name.lower() == '*paper_space' @property def is_any_paperspace(self) -> bool: """ True if is any kind of paperspace layout. """ return self.dxf.name.lower().startswith('*paper_space') @property def is_modelspace(self) -> bool: """ True if is modelspace layout. """ return self.dxf.name.lower() == '*model_space' @property def is_any_layout(self) -> bool: """ True if is any kind of modelspace or paperspace layout. """ return self.is_modelspace or self.is_any_paperspace @property def is_block_layout(self) -> bool: """ True if not any kind of modelspace or paperspace layout, just a regular block definition. """ return not self.is_any_layout def add_entity(self, entity: 'DXFGraphic') -> None: """ Add an existing DXF entity to BLOCK_RECORD. Args: entity: :class:`DXFGraphic` """ # assign layout if hasattr(entity, 'set_owner'): entity.set_owner(self.dxf.handle, paperspace=int(self.is_any_paperspace)) else: logger.debug('Unexpected entity {}'.format(entity)) self.entity_space.add(entity) def unlink_entity(self, entity: 'DXFGraphic') -> None: """ Unlink `entity` from BLOCK_RECORD. Removes `entity` just from entity space but not from the drawing database. Args: entity: :class:`DXFGraphic` """ self.entity_space.remove(entity) entity.dxf.paperspace = -1 # set invalid paper space entity.dxf.owner = None def delete_entity(self, entity: 'DXFGraphic') -> None: """ Delete `entity` from BLOCK_RECORD entity space and drawing database. Args: entity: :class:`DXFGraphic` """ self.unlink_entity(entity) # 1. unlink from entity space self.entitydb.delete_entity(entity) # 2. delete from drawing database
class BlockRecord(DXFEntity): """DXF BLOCK_RECORD table entity BLOCK_RECORD is the hard owner of all entities in BLOCK definitions, this means owner tag of entities is handle of BLOCK_RECORD. """ DXFTYPE = "BLOCK_RECORD" DXFATTRIBS = DXFAttributes(base_class, acdb_symbol_table_record, acdb_blockrec) def __init__(self): from ezdxf.entitydb import EntitySpace super().__init__() # Store entities in the block_record instead of BlockLayout and Layout, # because BLOCK_RECORD is also the hard owner of all the entities. self.entity_space = EntitySpace() self.block: Optional["Block"] = None self.endblk: Optional["EndBlk"] = None # stores also the block layout structure self.block_layout: Optional[BlockLayout] = None def set_block(self, block: "Block", endblk: "EndBlk"): self.block = block self.endblk = endblk self.block.dxf.owner = self.dxf.handle self.endblk.dxf.owner = self.dxf.handle def set_entity_space(self, entity_space: "EntitySpace") -> None: self.entity_space = entity_space def rename(self, name: str) -> None: assert self.block is not None self.dxf.name = name self.block.dxf.name = name def load_dxf_attribs(self, processor: SubclassProcessor = None ) -> "DXFNamespace": dxf = super().load_dxf_attribs(processor) if processor: processor.simple_dxfattribs_loader( dxf, acdb_blockrec_group_codes) # type: ignore return dxf def export_entity(self, tagwriter: "TagWriter") -> None: super().export_entity(tagwriter) if tagwriter.dxfversion == DXF12: raise DXFInternalEzdxfError("Exporting BLOCK_RECORDS for DXF R12.") tagwriter.write_tag2(SUBCLASS_MARKER, acdb_symbol_table_record.name) tagwriter.write_tag2(SUBCLASS_MARKER, acdb_blockrec.name) self.dxf.export_dxf_attribs( tagwriter, [ "name", "layout", "units", "explode", "scale", ], ) def export_block_definition(self, tagwriter: "TagWriter") -> None: """Exports BLOCK, than all DXF entities and at last the ENDBLK entity, except for *Model_space and *Paper_Pacer, their entities are stored in the entities section. """ assert self.block is not None assert self.endblk is not None if self.block_layout is not None: self.block_layout.update_block_flags() self.block.export_dxf(tagwriter) if not (self.is_modelspace or self.is_active_paperspace): self.entity_space.export_dxf(tagwriter) self.endblk.export_dxf(tagwriter) def destroy(self): """Destroy associated data: - BLOCK - ENDBLK - all entities stored in this block definition Does not destroy the linked LAYOUT entity, this is the domain of the :class:`Layouts` object, which also should initiate the destruction of 'this' BLOCK_RECORD. """ if not self.is_alive: return self.block.destroy() self.endblk.destroy() for entity in self.entity_space: entity.destroy() # remove attributes to find invalid access after death del self.block del self.endblk del self.block_layout super().destroy() @property def is_active_paperspace(self) -> bool: """``True`` if is "active" paperspace layout.""" return self.dxf.name.lower() == "*paper_space" @property def is_any_paperspace(self) -> bool: """``True`` if is any kind of paperspace layout.""" return self.dxf.name.lower().startswith("*paper_space") @property def is_modelspace(self) -> bool: """``True`` if is the modelspace layout.""" return self.dxf.name.lower() == "*model_space" @property def is_any_layout(self) -> bool: """``True`` if is any kind of modelspace or paperspace layout.""" return self.is_modelspace or self.is_any_paperspace @property def is_block_layout(self) -> bool: """``True`` if not any kind of modelspace or paperspace layout, just a regular block definition. """ return not self.is_any_layout @property def is_xref(self) -> bool: """``True`` if represents an XREF (external reference) or XREF_OVERLAY.""" if self.block is not None: return bool(self.block.dxf.flags & 12) return False def add_entity(self, entity: "DXFGraphic") -> None: """Add an existing DXF entity to BLOCK_RECORD. Args: entity: :class:`DXFGraphic` """ # assign layout try: entity.set_owner(self.dxf.handle, paperspace=int(self.is_any_paperspace)) except AttributeError: logger.debug( f"Unexpected DXF entity {str(entity)} in {str(self.block)}") # Add unexpected entities also to the entity space - auditor should fix # errors! self.entity_space.add(entity) def unlink_entity(self, entity: "DXFGraphic") -> None: """Unlink `entity` from BLOCK_RECORD. Removes `entity` just from entity space but not from the drawing database. Args: entity: :class:`DXFGraphic` """ if entity.is_alive: self.entity_space.remove(entity) try: entity.set_owner(None) except AttributeError: pass # unsupported entities as DXFTagStorage def delete_entity(self, entity: "DXFGraphic") -> None: """Delete `entity` from BLOCK_RECORD entity space and drawing database. Args: entity: :class:`DXFGraphic` """ self.unlink_entity(entity) # 1. unlink from entity space entity.destroy() def audit(self, auditor: "Auditor") -> None: """Validity check. (internal API)""" if not self.is_alive: return super().audit(auditor) entitydb = auditor.entitydb rec_name = self.dxf.name trash = [] for entity in self.entity_space: if entity.is_alive: check = entitydb.get(entity.dxf.handle) if check is not entity: # Different entity stored in the database for this handle, # scenario #604: # - document has entities without handles (invalid for DXF R13+) # - $HANDSEED is not the next usable handle (dubious, causes error #1) # - entity gets an already used handle (loading error #1) # - entity overwrites existing entity or will be # overwritten by an entity loaded afterwards (loading error #2) trash.append(entity) auditor.fixed_error( code=AuditError.REMOVED_ENTITY_FROM_BLOCK_RECORD, message=f"Removed invalid database entry {str(entity)}" f' from BLOCK_RECORD "{rec_name}".', ) for e in trash: self.entity_space.remove(e)