1# Purpose: acdsdata section manager 2# Created: 05.05.2014 3# Copyright (c) 2014-2019, Manfred Moitzi 4# License: MIT License 5""" 6ACDSDATA entities have NO handles, therefore they can not be stored in the drawing entity database. 7every routine written until now (2014-05-05), expects entities with valid handle 8 9section structure (work in progress): 100 <str> SECTION 112 <str> ACDSDATA 1270 <int> 2 # flag? 1371 <int> 6 # count of following ACDSSCHEMA entities ??? no, just another flag 14 150 <str> ACDSSCHEMA # dxftype: schema definition 1690 <int> 0 # schema number 0, 1, 2, 3 ... 171 <str> AcDb3DSolid_ASM_Data # schema name 18 192 <str> AcDbDs::ID # subsection name 20280 <int> 10 # subsection type 10 = ??? 2191 <int> 8 # data ??? 22 232 <str> ASM_Data # subsection name 24280 <int> 15 # subsection type 2591 <int> 0 # data ??? 26101 <str> ACDSRECORD # data 2795 <int> 0 2890 <int> 2 29... 30 310 <str> ACDSSCHEMA 3290 <int> 1 331 <str> AcDb_Thumbnail_Schema 34... 35 360 <str> ACDSSCHEMA 3790 <int> 2 381 <str> AcDbDs::TreatedAsObjectDataSchema 39... 40 410 <str> ACDSSCHEMA 4290 <int> 3 431 <str> AcDbDs::LegacySchema 442 <str> AcDbDs::Legacy 45280 <int> 1 4691 <int> 0 47 480 <str> ACDSSCHEMA 4990 <int> 4 501 <str> AcDbDs::IndexedPropertySchema 512 <str> AcDs:Indexable 52280 <int> 1 5391 <int> 0 54 550 <str> ACDSSCHEMA 5690 <int> 5 571 <str> AcDbDs::HandleAttributeSchema 582 <str> AcDbDs::HandleAttribute 59280 <int> 7 6091 <int> 1 61284 <int> 1 62 630 <str> ACDSRECORD # dxftype: data record 6490 <int> 0 # ??? flag 652 <str> AcDbDs::ID # subsection name 66280 <int> 10 # subsection type 10 = handle to owner entity, 3DSOLID/REGION 67320 <str> 339 # handle 682 <str> ASM_Data # subsection name 69280 <int> 15 # subsection type 15 = binary data 7094 <int> 1088 # size of data 71310 <binary encoded data> # data 72310 <binary encoded data> # data 73... 74 750 <str> ENDSEC 76""" 77from typing import TYPE_CHECKING, Iterator, Iterable, List, Any 78from itertools import islice 79 80from ezdxf.lldxf.tags import group_tags, Tags 81from ezdxf.lldxf.const import DXFKeyError, DXFStructureError 82 83if TYPE_CHECKING: # import forward declarations 84 from ezdxf.eztypes import TagWriter, Drawing 85 86 87class AcDsDataSection: 88 name = 'ACDSDATA' 89 90 def __init__(self, doc: 'Drawing', entities: Iterable[Tags] = None): 91 self.doc = doc 92 self.entities = [] # type: List[AcDsData] 93 self.section_info = [] # type: Tags 94 if entities is not None: 95 self.load_tags(iter(entities)) 96 97 @property 98 def is_valid(self): 99 return len(self.section_info) 100 101 def load_tags(self, entities: Iterator[Tags]) -> None: 102 section_head = next(entities) 103 if section_head[0] != (0, 'SECTION') or section_head[1] != (2, 'ACDSDATA'): 104 raise DXFStructureError("Critical structure error in ACDSDATA section.") 105 106 self.section_info = section_head 107 for entity in entities: 108 self.append(AcDsData(entity)) # tags have no subclasses 109 110 def append(self, entity: 'AcDsData') -> None: 111 cls = ACDSDATA_TYPES.get(entity.dxftype(), AcDsData) 112 entity = cls(entity.tags) 113 self.entities.append(entity) 114 115 def export_dxf(self, tagwriter: 'TagWriter') -> None: 116 if not self.is_valid: 117 return 118 tagwriter.write_tags(self.section_info) 119 for entity in self.entities: 120 entity.export_dxf(tagwriter) 121 tagwriter.write_tag2(0, 'ENDSEC') 122 123 @property 124 def acdsrecords(self) -> Iterable['AcDsRecord']: 125 return (entity for entity in self.entities if entity.dxftype() == 'ACDSRECORD') 126 127 def get_acis_data(self, handle: str) -> List[str]: 128 for record in self.acdsrecords: 129 try: 130 section = record.get_section('AcDbDs::ID') 131 except DXFKeyError: # not present 132 continue 133 asm_handle = section.get_first_value(320, None) 134 if asm_handle == handle: 135 try: 136 asm_data = record.get_section('ASM_Data') 137 except DXFKeyError: # no data stored 138 break 139 return [tag.value for tag in asm_data if tag.code == 310] 140 return [] 141 142 143class AcDsData: 144 def __init__(self, tags: Tags): 145 self.tags = tags 146 147 def export_dxf(self, tagwriter: 'TagWriter'): 148 tagwriter.write_tags(self.tags) 149 150 def dxftype(self) -> str: 151 return self.tags[0].value 152 153 154class Section(Tags): 155 @property 156 def name(self) -> str: 157 return self[0].value 158 159 @property 160 def type(self) -> str: 161 return self[1].value 162 163 @property 164 def data(self) -> Tags: 165 return self[2:] 166 167 168class AcDsRecord: 169 def __init__(self, tags: Tags): 170 self._dxftype = tags[0] 171 self.flags = tags[1] 172 self.sections = [Section(group) for group in group_tags(islice(tags, 2, None), splitcode=2)] 173 174 def dxftype(self) -> str: 175 return 'ACDSRECORD' 176 177 def has_section(self, name: str) -> bool: 178 return self.get_section(name, default=None) is not None 179 180 def get_section(self, name: str, default: Any = DXFKeyError) -> Section: 181 for section in self.sections: 182 if section.name == name: 183 return section 184 if default is DXFKeyError: 185 raise DXFKeyError(name) 186 else: 187 return default 188 189 def __len__(self): 190 return len(self.sections) 191 192 def __getitem__(self, item) -> Section: 193 return self.sections[item] 194 195 def _write_header(self, tagwriter: 'TagWriter') -> None: 196 tagwriter.write_tags(Tags([self._dxftype, self.flags])) 197 198 def export_dxf(self, tagwriter: 'TagWriter') -> None: 199 self._write_header(tagwriter) 200 for section in self.sections: 201 tagwriter.write_tags(section) 202 203 204ACDSDATA_TYPES = { 205 'ACDSRECORD': AcDsRecord, 206} 207