def set_document( self, document: Drawing, auditor: Auditor, *, layout: str = "Model", overall_scaling_factor: float = 1.0, ): error_count = len(auditor.errors) if error_count > 0: ret = qw.QMessageBox.question( self, "Found DXF Errors", f'Found {error_count} errors in file "{document.filename}"\n' f"Load file anyway? ", ) if ret == qw.QMessageBox.No: auditor.print_error_report(auditor.errors) return self.doc = document self._render_context = RenderContext(document) self._reset_backend(scale=overall_scaling_factor) self._visible_layers = None self._current_layout = None self._populate_layouts() self._populate_layer_list() self.draw_layout(layout) self.setWindowTitle("CAD Viewer - " + str(document.filename))
def test_audit_invalid_major_axis(self, msp): ellipse = msp.add_ellipse((0, 0), (1, 0)) # can only happen for loaded DXF files ellipse.dxf.__dict__["major_axis"] = Vec3(0, 0, 0) # hack auditor = Auditor(ellipse.doc) ellipse.audit(auditor) auditor.empty_trashcan() assert len(auditor.fixes) == 1 assert ellipse.is_alive is False, "invalid ellipse should be deleted"
def test_audit_fix_invalid_root_dict_owner(): doc = ezdxf.new() rootdict = doc.rootdict auditor = Auditor(doc) rootdict.dxf.owner = "FF" # set invalid owner auditor.run() assert len(auditor.fixes) == 1 assert auditor.fixes[0].code == AuditError.INVALID_OWNER_HANDLE assert rootdict.dxf.owner == "0"
def audit(self) -> Auditor: """Checks document integrity and fixes all fixable problems, not fixable problems are stored in :attr:`Auditor.errors`. If you are messing around with internal structures, call this method before saving to be sure to export valid DXF documents, but be aware this is a long running task. """ auditor = Auditor(self) auditor.run() return auditor
def _fix_entry_handles(self, auditor: Auditor): # Why: see duplicate handle issue #604 entitydb = self.entitydb for entry in self: entity = entitydb.get(entry.dxf.handle) if entity is not entry: # duplicate handle usage # This can break entities referring to this entity, but at # least the DXF readable entry.dxf.handle = entitydb.next_handle() self.entitydb.add(entry) auditor.fixed_error( code=AuditError.INVALID_TABLE_HANDLE, message=f"Fixed invalid table entry handle in {entry}", )
def run(start): if start > 0: start -= 1 names = list(chain(glob.glob(DIR1), glob.glob(DIR2))) names = names[start:] count = 0 for filename in names: count += 1 print("processing: {}/{} file: {}".format(count+start, len(names)+start, filename)) doc = ezdxf.readfile(filename, legacy_mode=LEGACY_MODE) auditor = Auditor(doc) if len(auditor): auditor.print_error_report(auditor.errors)
def read(stream: BinaryIO, errors: str = 'surrogateescape') -> Tuple['Drawing', 'Auditor']: """ Read a DXF document from a binary-stream similar to :func:`ezdxf.read`, but this function will detect the text encoding automatically and repair as much flaws as possible, runs the required audit process afterwards and returns the DXF document and the :class:`Auditor`. Args: stream: data stream to load in binary read mode 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: for invalid or corrupted DXF structures UnicodeDecodeError: if `errors` is "strict" and a decoding error occurs """ from ezdxf.document import Drawing recover_tool = Recover.run(stream, errors=errors) doc = Drawing() doc._load_section_dict(recover_tool.section_dict) auditor = Auditor(doc) for code, msg in recover_tool.errors: auditor.add_error(code, msg) for code, msg in recover_tool.fixes: auditor.fixed_error(code, msg) auditor.run() return doc, auditor
def audit(self, auditor: Auditor) -> None: """Audit and repair BLOCKS section. .. important:: Do not delete entities while auditing process, because this would alter the entity database while iterating, instead use:: auditor.trash(entity) to delete invalid entities after auditing automatically. """ assert self.doc is auditor.doc, "Auditor for different DXF document." for block_record in self.block_records: assert isinstance(block_record, BlockRecord) for entity in block_record.entity_space: if not is_graphic_entity(entity): auditor.fixed_error( code=AuditError.REMOVED_INVALID_GRAPHIC_ENTITY, message=f"Removed invalid DXF entity {str(entity)} from" f" BLOCK '{block_record.dxf.name}'.", ) auditor.trash(entity) elif isinstance(entity, Attrib): # ATTRIB can only exist as an attached entity of the INSERT # entity! auditor.fixed_error( code=AuditError.REMOVED_STANDALONE_ATTRIB_ENTITY, message=f"Removed standalone {str(entity)} entity from" f" BLOCK '{block_record.dxf.name}'.", ) auditor.trash(entity)
def test_audit_restores_deleted_owner_tag(): doc = ezdxf.new() d = doc.objects.add_dictionary() d.dxf.discard("owner") auditor = Auditor(doc) d.audit(auditor) assert d.dxf.owner == doc.rootdict.dxf.handle, "assign to root dict"
def test_audit_fixes_invalid_text_style_handle(self, doc, new_style): new_style.dxf.text_style_handle = "ABBA" auditor = Auditor(doc) new_style.audit(auditor) assert len(auditor.fixes) == 1 text_style = doc.entitydb.get(new_style.dxf.text_style_handle) assert text_style.dxf.name == "Standard"
def test_audit_max_ratio(self, msp): ellipse = msp.add_ellipse((0, 0), (1, 0)) # can only happen for loaded DXF files ellipse.dxf.__dict__["ratio"] = 2.0 # hack auditor = Auditor(ellipse.doc) ellipse.audit(auditor) assert len(auditor.fixes) == 1 assert ellipse.dxf.ratio == 0.5 assert ellipse.dxf.major_axis.isclose((0.0, 2.0))
def test_audit_min_ratio(self, msp): ellipse = msp.add_ellipse((0, 0), (1, 0)) # can only happen for loaded DXF files ellipse.dxf.__dict__["ratio"] = 1e-9 # hack auditor = Auditor(ellipse.doc) ellipse.audit(auditor) assert len(auditor.fixes) == 1 assert ellipse.dxf.ratio == 1e-6 assert ellipse.dxf.major_axis.isclose((1.0, 0.0)), "should not changed"
def test_create_place_holder_for_invalid_default_vaue(self): doc = ezdxf.new() d = doc.objects.add_dictionary_with_default( owner=doc.rootdict.dxf.handle, default="0") auditor = Auditor(doc) d.audit(auditor) default = d.get("xxx") assert default.is_alive is True assert default.dxftype() == "ACDBPLACEHOLDER"
def set_document(self, document: Drawing, auditor: Auditor): error_count = len(auditor.errors) if error_count > 0: ret = qw.QMessageBox.question( self, 'Found DXF Errors', f'Found {error_count} errors in file "{document.filename}"\nLoad file anyway? ' ) if ret == qw.QMessageBox.No: auditor.print_error_report(auditor.errors) return self.doc = document self._render_context = RenderContext(document) self._reset_backend() # clear caches self._visible_layers = None self._current_layout = None self._populate_layouts() self._populate_layer_list() self.draw_layout('Model') self.setWindowTitle('CAD Viewer - ' + str(document.filename))
def test_fix_invalid_transparency(): doc = ezdxf.new() msp = doc.modelspace() line = msp.add_line((0, 0), (1, 0)) # transparency value requires 0x02000000 bit set line.dxf.unprotected_set("transparency", 0x10000000) auditor = Auditor(doc) line.audit(auditor) assert line.dxf.hasattr("transparency") is False assert len(auditor.fixes) == 1
def _load_and_audit_document(recover_tool) -> Tuple['Drawing', 'Auditor']: from ezdxf.document import Drawing doc = Drawing() doc._load_section_dict(recover_tool.section_dict) auditor = Auditor(doc) for code, msg in recover_tool.errors: auditor.add_error(code, msg) for code, msg in recover_tool.fixes: auditor.fixed_error(code, msg) auditor.run() return doc, auditor
def test_audit_ok(): doc = ezdxf.new() auditor = Auditor(doc) rootdict = doc.rootdict assert 'TEST_VAR_1' not in rootdict new_var = rootdict.add_dict_var('TEST_VAR_1', 'Hallo') assert new_var.dxftype() == 'DICTIONARYVAR' rootdict.audit(auditor) assert len(auditor) == 0
def test_audit_invalid_pointer(): doc = ezdxf.new() auditor = Auditor(doc) d = doc.rootdict.add_new_dict("TEST_AUDIT_2") entry = d.add_dict_var("TEST_VAR_2", "Hallo") entry.dxf.handle = "ABBA" d.audit(auditor) assert len(auditor.fixes) == 1 e = auditor.fixes[0] assert e.code == AuditError.INVALID_DICTIONARY_ENTRY
def test_audit_ok(): doc = ezdxf.new() auditor = Auditor(doc) rootdict = doc.rootdict assert "TEST_VAR_1" not in rootdict new_var = rootdict.add_dict_var("TEST_VAR_1", "Hallo") assert new_var.dxftype() == "DICTIONARYVAR" rootdict.audit(auditor) assert len(auditor) == 0
def test_block_cycle_detector_setup(): doc = ezdxf.new() a = doc.blocks.new('a') b = doc.blocks.new('b') c = doc.blocks.new('c') a.add_blockref('b', (0, 0)) a.add_blockref('c', (0, 0)) b.add_blockref('c', (0, 0)) c.add_blockref('a', (0, 0)) # cycle detector = BlockCycleDetector(doc) assert detector.has_cycle('a') is True assert detector.has_cycle('b') is True assert detector.has_cycle('c') is True auditor = Auditor(doc) auditor.check_block_reference_cycles() assert len(auditor.errors) == 3 # one entry for each involved block: 'a', 'b', 'c' assert auditor.errors[0].code == AuditError.INVALID_BLOCK_REFERENCE_CYCLE assert auditor.errors[1].code == AuditError.INVALID_BLOCK_REFERENCE_CYCLE assert auditor.errors[2].code == AuditError.INVALID_BLOCK_REFERENCE_CYCLE
def audit(self, auditor: Auditor) -> None: """Audit and repair OBJECTS section. .. important:: Do not delete entities while auditing process, because this would alter the entity database while iterating, instead use:: auditor.trash(entity) to delete invalid entities after auditing automatically. """ assert self.doc is auditor.doc, "Auditor for different DXF document." for entity in self._entity_space: if not is_dxf_object(entity): auditor.fixed_error( code=AuditError.REMOVED_INVALID_DXF_OBJECT, message=f"Removed invalid DXF entity {str(entity)} " f"from OBJECTS section.", ) auditor.trash(entity)
def test_duplicate_handle_loading_error(doc): msp = doc.modelspace() p1 = msp.add_point((0, 0)) p2 = msp.add_point((0, 0)) count = len(msp) # Simulate loading error with same handle for two entities! p2.dxf.handle = p1.dxf.handle auditor = Auditor(doc) block_record = doc.block_records.get("*Model_Space") block_record.audit(auditor) assert len(auditor.fixes) == 1 assert len(msp) == count-1 assert p2.is_alive, "entity should not be destroyed"
def test_audit_filters_invalid_entities(doc): group = doc.groups.new("test6") msp = doc.modelspace() block = doc.blocks.new("Block6") point1 = block.add_point((0, 0)) # invalid BLOCK entity point2 = msp.add_point((0, 0)) # valid model space entity ... point2.destroy() # ... but destroyed layer = doc.layers.get("0") # invalid table entry group.extend([point1, point2, layer]) auditor = Auditor(doc) group.audit(auditor) assert len(group) == 0
def test_audit_fix_invalid_pointer(): doc = ezdxf.new() auditor = Auditor(doc) d = doc.rootdict.add_new_dict("TEST_AUDIT_3") entry = d.add_dict_var("TEST_VAR_3", "Hallo") entry.dxf.handle = "ABBA" d.audit(auditor) assert len(auditor) == 0, "should return count of unfixed errors" assert len(auditor.errors) == 0 assert len(auditor.fixes) == 1 fix = auditor.fixes[0] assert fix.code == AuditError.INVALID_DICTIONARY_ENTRY # test if invalid entry was removed assert len(d) == 0 assert "TEST_VAR_3" not in d
def test_auditor_fixes_invalid_crease_count(msp): mesh = msp.add_mesh() with mesh.edit_data() as mesh_data: mesh_data.add_face([(0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0)]) mesh_data.add_edge_crease(v1=0, v2=1, crease=1.0) auditor = Auditor(msp.doc) mesh.audit(auditor) assert len(auditor.fixes) == 0 assert len(auditor.errors) == 0 auditor.reset() mesh.creases = [1, 1] # too much crease values for only one edge mesh.audit(auditor) assert len(auditor.fixes) == 1 assert list(mesh.creases) == [1.0] auditor.reset() mesh.creases = [] # too few crease values for only one edge mesh.audit(auditor) assert len(auditor.fixes) == 1 assert list(mesh.creases) == [0.0]
def auditor(dxf): return Auditor(dxf)
def auditor(doc): return Auditor(doc)
def audit(self, auditor: Auditor): """ Validity check. """ super().audit(auditor) auditor.check_text_style(self)
# Copyright (c) 2011-2020, Manfred Moitzi # License: MIT License import pytest from ezdxf.entitydb import EntityDB from ezdxf.entities.dxfentity import DXFEntity from ezdxf.audit import Auditor ENTITY = DXFEntity.new(handle='FFFF') auditor = Auditor(None) @pytest.fixture def db(): db = EntityDB() db['FEFE'] = ENTITY return db def test_get_value(db): assert ENTITY is db['FEFE'] def test_set_value(db): new_entity = DXFEntity.new(handle='FEFE') db['FEFE'] = new_entity assert new_entity is db['FEFE'] def test_del_value(db): del db['FEFE'] with pytest.raises(KeyError):
def auditor(self, doc): return Auditor(doc)