# Purpose: tables section # Created: 12.03.2011 # Copyright (c) 2011-2018, Manfred Moitzi # License: MIT License from typing import TYPE_CHECKING, Iterable, Sequence, Iterator from ezdxf.lldxf.tags import DXFTag from ezdxf.lldxf.extendedtags import ExtendedTags from ezdxf.lldxf.const import DXFAttributeError, DXFStructureError from .table import Table, ViewportTable, StyleTable if TYPE_CHECKING: from ezdxf.eztypes import Drawing, TagWriter, Tags class TablesSection: name = 'TABLES' def __init__(self, entities: Iterable[Sequence[DXFTag]], drawing: 'Drawing'): self._drawing = drawing self._tables = {} if entities is None: section_head = [DXFTag(0, 'SECTION'), DXFTag(2, 'TABLES')] entities = [section_head] self._setup_tables(iter(entities)) def __iter__(self) -> Iterable[Table]: return iter(self._tables.values()) @staticmethod def key(name: str) -> str: return name.upper() def _setup_tables(self, entities: Iterator[Sequence[DXFTag]]) -> None: 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 _new_table(self, name: str, table_entities: Iterable['Tags']) -> None: table_class = TABLESMAP[name] new_table = table_class(table_entities, self._drawing) self._tables[self.key(new_table.name)] = new_table def _setup_table(self, name): """ Setup new empty table. Args: name: real table name like 'VPORT' for viewports """ name = self.key(name) if self._drawing is not None: dxfversion = self._drawing.dxfversion handle = self._drawing.entitydb.get_unique_handle() else: # test environment without Drawing() object dxfversion = 'AC1009' # DXF R12 handle = '0' if dxfversion <= 'AC1009': table_entities = [[ DXFTag(0, 'TABLE'), DXFTag(2, name), DXFTag(70, 0) ]] else: table_entities = [[ DXFTag(0, 'TABLE'), DXFTag(2, name), DXFTag(5, handle), DXFTag(330, '0'), DXFTag(100, 'AcDbSymbolTable'), DXFTag(70, 0) ]] self._new_table(name, table_entities) def _create_missing_tables(self) -> None: if 'LAYERS' not in self: self._setup_table('LAYER') if 'LINETYPES' not in self: self._setup_table('LTYPE') if 'STYLES' not in self: self._setup_table('STYLE') if 'DIMSTYLES' not in self: self._setup_table('DIMSTYLE') if 'VIEWPORTS' not in self: self._setup_table('VPORT') if 'APPIDS' not in self: self._setup_table('APPID') if 'UCS' not in self: self._setup_table('UCS') def __contains__(self, item: str) -> bool: return self.key(item) in self._tables def __getattr__(self, key: str) -> Table: key = self.key(key) try: return self._tables[key] except KeyError: # internal exception raise DXFAttributeError(key) def __getitem__(self, key: str) -> Table: return self._tables[self.key(key)] def __delitem__(self, key: str) -> None: del self._tables[self.key(key)] def write(self, tagwriter: 'TagWriter') -> None: tagwriter.write_str(' 0\nSECTION\n 2\nTABLES\n') for table_name in TABLE_ORDER: table = self._tables.get(table_name) if table is not None: table.write(tagwriter) tagwriter.write_tag2(0, 'ENDSEC') TABLESMAP = { 'LAYER': Table, 'LTYPE': Table, 'STYLE': StyleTable, 'DIMSTYLE': Table, 'VPORT': ViewportTable, 'VIEW': Table, 'UCS': Table, 'APPID': Table, 'BLOCK_RECORD': Table, } # The order of the tables may change, but the LTYPE table always precedes the LAYER table. TABLE_ORDER = ('VIEWPORTS', 'LINETYPES', 'LAYERS', 'STYLES', 'VIEWS', 'UCS', 'APPIDS', 'DIMSTYLES', 'BLOCK_RECORDS')