# Created: 03.04.2016 # Copyright (C) 2016-2018, Manfred Moitzi # License: MIT License from typing import TYPE_CHECKING, Tuple, Union, Iterable, List, cast from ezdxf.dxfentity import DXFEntity from ezdxf.lldxf.attributes import DXFAttr, DXFAttributes, DefSubclass, XType from ezdxf.lldxf.types import DXFVertex from ezdxf.lldxf.tags import Tags from ezdxf.lldxf.extendedtags import ExtendedTags from ezdxf.lldxf import const from .graphics import none_subclass, entity_subclass, ModernGraphicEntity if TYPE_CHECKING: from ezdxf.eztypes import Vertex _PDFUNDERLAY_CLS = """0 CLASS 1 PDFUNDERLAY 2 AcDbPdfReference 3 ObjectDBX Classes 90 4095 91 0 280 0 281 1 """ _PDFUNDERLAY_TPL = """0 PDFUNDERLAY 5 0 330 0 100 AcDbEntity 8 0 100 AcDbUnderlayReference 340 0 10 0.0 20 0.0 30 0.0 41 1.0 42 1.0 43 1.0 50 0.0 280 2 281 100 282 0 """ underlay_subclass = DefSubclass('AcDbUnderlayReference', { 'underlay_def': DXFAttr(340), # Hard reference to underlay definition object 'insert': DXFAttr(10, xtype=XType.point3d), 'scale_x': DXFAttr(41, default=1.), # scale x factor 'scale_y': DXFAttr(42, default=1.), # scale y factor 'scale_z': DXFAttr(43, default=1.), # scale z factor 'rotation': DXFAttr(50, default=0.), # rotation angle in degrees? 'extrusion': DXFAttr(210, xtype=XType.point3d), 'flags': DXFAttr(280, default=0), # Underlay display properties: # 1 = Clipping is on # 2 = Underlay is on # 4 = Monochrome # 8 = Adjust for background 'contrast': DXFAttr(281, default=100), # Contrast value (20-100; default = 100) 'fade': DXFAttr(282, default=0), # Fade value (0-80; default = 0) }) class PdfUnderlay(ModernGraphicEntity): __slots__ = () TEMPLATE = ExtendedTags.from_text(_PDFUNDERLAY_TPL) CLASS = ExtendedTags.from_text(_PDFUNDERLAY_CLS) DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, underlay_subclass) @property def clipping(self) -> bool: return bool(self.dxf.flags & const.UNDERLAY_CLIPPING) @clipping.setter def clipping(self, state: bool) -> None: self.set_flags(const.UNDERLAY_CLIPPING, state) @property def on(self) -> bool: return bool(self.dxf.flags & const.UNDERLAY_ON) @on.setter def on(self, state: bool) -> None: self.set_flags(const.UNDERLAY_ON, state) @property def monochrome(self) -> bool: return bool(self.dxf.flags & const.UNDERLAY_MONOCHROME) @monochrome.setter def monochrome(self, state: bool) -> None: self.set_flags(const.UNDERLAY_MONOCHROME, state) @property def adjust_for_background(self) -> bool: return bool(self.dxf.flags & const.UNDERLAY_ADJUST_FOR_BG) @adjust_for_background.setter def adjust_for_background(self, state: bool): self.set_flags(const.UNDERLAY_ADJUST_FOR_BG, state) @property def scale(self) -> Tuple[float, float, float]: return self.dxf.scale_x, self.dxf.scale_y, self.dxf.scale_z @scale.setter def scale(self, scale: Union[float, Tuple]): if type(scale) in (float, int): x, y, z = scale, scale, scale else: x, y, z = scale self.dxf.scale_x = x self.dxf.scale_y = y self.dxf.scale_z = z def set_flags(self, flag: int, state: bool = True) -> None: if state: self.dxf.flags = self.dxf.flags | flag else: self.dxf.flags = self.dxf.flags & ~flag def set_boundary_path(self, vertices: Iterable['Vertex']) -> None: # path coordinates as drawing coordinates but unscaled vertices = list(vertices) self._set_path_tags(vertices) self.clipping = bool(len(vertices)) def _set_path_tags(self, vertices: Iterable['Vertex']): boundary = [DXFVertex(11, value) for value in vertices] subclasstags = Tags(tag for tag in self.tags.subclasses[2] if tag.code != 11) # filter out existing path tags subclasstags.extend(boundary) self.tags.subclasses[2] = subclasstags def reset_boundary_path(self) -> None: self._set_path_tags([]) self.clipping = False def get_boundary_path(self) -> List['Vertex']: underlay_subclass = self.tags.subclasses[2] return [tag.value for tag in underlay_subclass if tag.code == 11] # fetch path tags def get_underlay_def(self) -> 'UnderlayDef': return cast('UnderlayDef', self.dxffactory.wrap_handle(self.dxf.underlay_def)) def destroy(self) -> None: super(PdfUnderlay, self).destroy() underlay_def = self.get_underlay_def() underlay_def.remove_reactor_handle(self.dxf.handle) _DWFUNDERLAY_CLS = """0 CLASS 1 DWFUNDERLAY 2 AcDbDwfReference 3 ObjectDBX Classes 90 1153 91 0 280 0 281 1 """ class DwfUnderlay(PdfUnderlay): __slots__ = () TEMPLATE = ExtendedTags.from_text(_PDFUNDERLAY_TPL.replace('PDF', 'DWF')) CLASS = ExtendedTags.from_text(_DWFUNDERLAY_CLS) _DGNUNDERLAY_CLS = """0 CLASS 1 DGNUNDERLAY 2 AcDbDgnReference 3 ObjectDBX Classes 90 1153 91 0 280 0 281 1 """ class DgnUnderlay(PdfUnderlay): __slots__ = () TEMPLATE = ExtendedTags.from_text(_PDFUNDERLAY_TPL.replace('PDF', 'DGN')) CLASS = ExtendedTags.from_text(_DGNUNDERLAY_CLS) _PDF_DEF_CLS = """0 CLASS 1 PDFDEFINITION 2 AcDbPdfDefinition 3 ObjectDBX Classes 90 1153 91 0 280 0 281 0 """ # Using reactors in PdfDefinition for well defined UNDERLAYS _PDF_DEF_TPL = """0 PDFDEFINITION 5 0 102 {ACAD_REACTORS 102 } 330 0 100 AcDbUnderlayDefinition 1 noname.pdf 2 1 """ underlay_def_subclass = DefSubclass('AcDbUnderlayDefinition', { 'filename': DXFAttr(1), # File name of underlay 'name': DXFAttr(2), # underlay name - pdf=page number to display; dgn=default; dwf=???? }) # (PDF|DWF|DGN)DEFINITION - requires entry in objects table ACAD_(PDF|DWF|DGN)DEFINITIONS, # ACAD_(PDF|DWF|DGN)DEFINITIONS do not exist by default class PdfDefinition(DXFEntity): __slots__ = () TEMPLATE = ExtendedTags.from_text(_PDF_DEF_TPL) CLASS = ExtendedTags.from_text(_PDF_DEF_CLS) DXFATTRIBS = DXFAttributes(none_subclass, underlay_def_subclass) @property def entity_name(self): return self.dxftype()[:3] + "UNDERLAY" def post_new_hook(self): self.set_reactors([self.dxf.owner]) _DWF_DEF_CLS = """0 CLASS 1 DWFDEFINITION 2 AcDbDwfDefinition 3 ObjectDBX Classes 90 1153 91 0 280 0 281 0 """ class DwfDefinition(PdfDefinition): __slots__ = () TEMPLATE = ExtendedTags.from_text(_PDF_DEF_TPL.replace('PDF', 'DWF')) CLASS = ExtendedTags.from_text(_DWF_DEF_CLS) _DGN_DEF_CLS = """0 CLASS 1 DGNDEFINITION 2 AcDbDgnDefinition 3 ObjectDBX Classes 90 1153 91 0 280 0 281 0 """ class DgnDefinition(PdfDefinition): __slots__ = () TEMPLATE = ExtendedTags.from_text(_PDF_DEF_TPL.replace('PDF', 'DGN')) CLASS = ExtendedTags.from_text(_DGN_DEF_CLS) UnderlayDef = Union[PdfDefinition, DgnDefinition, DwfDefinition] Underlay = Union[PdfUnderlay, DgnUnderlay, DwfUnderlay]