# Purpose: entity section # Created: 13.03.2011 # Copyright (c) 2011-2018, Manfred Moitzi # License: MIT License from typing import TYPE_CHECKING, Iterable, Tuple, cast import logging from ezdxf.modern.dxfdict import DXFDictionary from ezdxf.lldxf.const import DXFStructureError, DXFValueError, RASTER_UNITS, DXFKeyError from ezdxf.modern.dxfgroups import GroupManager from ezdxf.modern.material import MaterialManager from ezdxf.modern.mleader import MLeaderStyleManager from ezdxf.modern.mline import MLineStyleManager from ezdxf.modern.tablestyle import TableStyleManager from ezdxf.entityspace import EntitySpace from .abstract import AbstractSection if TYPE_CHECKING: from ezdxf.eztypes import Drawing, DXFEntity, GeoData logger = logging.getLogger('ezdxf') class ObjectsSection(AbstractSection): name = 'OBJECTS' def __init__(self, entities, drawing: 'Drawing'): entity_space = EntitySpace(drawing.entitydb) super(ObjectsSection, self).__init__(entity_space, entities, drawing) def __iter__(self) -> Iterable['DXFEntity']: for handle in self._entity_space: yield self.dxffactory.wrap_handle(handle) @property def roothandle(self) -> str: return self._entity_space[0] @property def rootdict(self) -> DXFDictionary: if len(self): return self.dxffactory.wrap_entity(self.entitydb[self.roothandle]) else: return self.setup_rootdict() def setup_rootdict(self) -> DXFDictionary: """ Create a root dictionary. Has to be the first object in the objects section. """ 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 setup_objects_management_tables(self, rootdict: DXFDictionary) -> None: def setup_plot_style_name_table(): plot_style_name_dict = self.add_dictionary_with_default(owner=rootdict.dxf.handle) plot_style_name_dict_handle = plot_style_name_dict.dxf.handle placeholder = self.add_placeholder(owner=plot_style_name_dict_handle) placeholder_handle = placeholder.dxf.handle plot_style_name_dict.dxf.default = placeholder_handle plot_style_name_dict['Normal'] = placeholder_handle rootdict['ACAD_PLOTSTYLENAME'] = plot_style_name_dict_handle for name in _OBJECT_TABLE_NAMES: if name in rootdict: continue # just create not existing tables logger.info('creating {} dictionary'.format(name)) if name == "ACAD_PLOTSTYLENAME": setup_plot_style_name_table() else: rootdict.add_new_dict(name) def add_dxf_object_with_reactor(self, dxftype: str, dxfattribs: dict) -> 'DXFEntity': dxfobject = self.create_new_dxf_entity(dxftype, dxfattribs) dxfobject.set_reactors([dxfattribs['owner']]) return dxfobject def groups(self): return GroupManager(self.drawing) def materials(self): return MaterialManager(self.drawing) def mleader_styles(self): return MLeaderStyleManager(self.drawing) def mline_styles(self): return MLineStyleManager(self.drawing) def table_styles(self): return TableStyleManager(self.drawing) def add_dictionary(self, owner: str = '0') -> DXFDictionary: entity = self.create_new_dxf_entity('DICTIONARY', dxfattribs={'owner': owner}) return cast(DXFDictionary, entity) def add_dictionary_with_default(self, owner='0', default="0") -> 'DXFDictionary': entity = self.create_new_dxf_entity('ACDBDICTIONARYWDFLT', dxfattribs={ 'owner': owner, 'default': default, }) return cast(DXFDictionary, entity) def add_xrecord(self, owner: str = '0') -> 'DXFEntity': return self.create_new_dxf_entity('XRECORD', dxfattribs={'owner': owner}) def add_placeholder(self, owner: str = '0') -> 'DXFEntity': return self.create_new_dxf_entity('ACDBPLACEHOLDER', dxfattribs={'owner': owner}) def set_raster_variables(self, frame: int = 0, quality: int = 1, units: str = 'm') -> None: units = RASTER_UNITS.get(units, 0) try: raster_vars = self.rootdict.get_entity('ACAD_IMAGE_VARS') except DXFKeyError: raster_vars = self.add_dxf_object_with_reactor('RASTERVARIABLES', dxfattribs={ 'owner': self.rootdict.dxf.handle, 'frame': frame, 'quality': quality, 'units': units, }) self.rootdict['ACAD_IMAGE_VARS'] = raster_vars.dxf.handle else: raster_vars.dxf.frame = frame raster_vars.dxf.quality = quality raster_vars.dxf.units = units def set_wipeout_variables(self, frame: int = 0) -> None: try: wipeout_vars = self.rootdict.get_entity('ACAD_WIPEOUT_VARS') except DXFKeyError: wipeout_vars = self.add_dxf_object_with_reactor('WIPEOUTVARIABLES', dxfattribs={ 'owner': self.rootdict.dxf.handle, 'frame': int(frame), }) self.rootdict['ACAD_WIPEOUT_VARS'] = wipeout_vars.dxf.handle else: wipeout_vars.dxf.frame = int(frame) def add_image_def(self, filename: str, size_in_pixel: Tuple[int, int], name=None) -> 'DXFEntity': # removed auto-generated name # use absolute image paths for filename and AutoCAD loads images automatically if name is None: name = filename image_dict = self.rootdict.get_required_dict('ACAD_IMAGE_DICT') image_def = self.add_dxf_object_with_reactor('IMAGEDEF', dxfattribs={ 'owner': image_dict.dxf.handle, 'filename': filename, 'image_size': size_in_pixel, }) image_dict[name] = image_def.dxf.handle return image_def def add_image_def_reactor(self, image_handle: str) -> 'DXFEntity': return self.create_new_dxf_entity('IMAGEDEF_REACTOR', dxfattribs={ 'owner': image_handle, 'image': image_handle, }) def add_underlay_def(self, filename: str, format: str = 'pdf', name: str = None) -> 'DXFEntity': fmt = format.upper() if fmt in ('PDF', 'DWF', 'DGN'): underlay_dict_name = 'ACAD_{}DEFINITIONS'.format(fmt) underlay_def_entity = "{}DEFINITION".format(fmt) else: raise DXFValueError("Unsupported file format: '{}'".format(fmt)) if name is None: if fmt == 'PDF': name = '1' # Display first page by default elif fmt == 'DGN': name = 'default' else: name = 'Model' # Display model space for DWF ??? underlay_dict = self.rootdict.get_required_dict(underlay_dict_name) underlay_def = self.create_new_dxf_entity(underlay_def_entity, dxfattribs={ 'owner': underlay_dict.dxf.handle, 'filename': filename, 'name': name, }) # auto-generated underlay key key = self.dxffactory.next_underlay_key(lambda k: k not in underlay_dict) underlay_dict[key] = underlay_def.dxf.handle return underlay_def def add_geodata(self, owner: str = '0', dxfattribs: dict = None) -> 'GeoData': if dxfattribs is None: dxfattribs = {} dxfattribs['owner'] = owner return cast('GeoData', self.add_dxf_object_with_reactor('GEODATA', dxfattribs)) _OBJECT_TABLE_NAMES = [ "ACAD_COLOR", "ACAD_GROUP", "ACAD_LAYOUT", "ACAD_MATERIAL", "ACAD_MLEADERSTYLE", "ACAD_MLINESTYLE", "ACAD_PLOTSETTINGS", "ACAD_PLOTSTYLENAME", "ACAD_SCALELIST", "ACAD_TABLESTYLE", "ACAD_VISUALSTYLE", ]