underlay.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. # Created: 03.04.2016
  2. # Copyright (C) 2016-2018, Manfred Moitzi
  3. # License: MIT License
  4. from typing import TYPE_CHECKING, Tuple, Union, Iterable, List, cast
  5. from ezdxf.dxfentity import DXFEntity
  6. from ezdxf.lldxf.attributes import DXFAttr, DXFAttributes, DefSubclass, XType
  7. from ezdxf.lldxf.types import DXFVertex
  8. from ezdxf.lldxf.tags import Tags
  9. from ezdxf.lldxf.extendedtags import ExtendedTags
  10. from ezdxf.lldxf import const
  11. from .graphics import none_subclass, entity_subclass, ModernGraphicEntity
  12. if TYPE_CHECKING:
  13. from ezdxf.eztypes import Vertex
  14. _PDFUNDERLAY_CLS = """0
  15. CLASS
  16. 1
  17. PDFUNDERLAY
  18. 2
  19. AcDbPdfReference
  20. 3
  21. ObjectDBX Classes
  22. 90
  23. 4095
  24. 91
  25. 0
  26. 280
  27. 0
  28. 281
  29. 1
  30. """
  31. _PDFUNDERLAY_TPL = """0
  32. PDFUNDERLAY
  33. 5
  34. 0
  35. 330
  36. 0
  37. 100
  38. AcDbEntity
  39. 8
  40. 0
  41. 100
  42. AcDbUnderlayReference
  43. 340
  44. 0
  45. 10
  46. 0.0
  47. 20
  48. 0.0
  49. 30
  50. 0.0
  51. 41
  52. 1.0
  53. 42
  54. 1.0
  55. 43
  56. 1.0
  57. 50
  58. 0.0
  59. 280
  60. 2
  61. 281
  62. 100
  63. 282
  64. 0
  65. """
  66. underlay_subclass = DefSubclass('AcDbUnderlayReference', {
  67. 'underlay_def': DXFAttr(340), # Hard reference to underlay definition object
  68. 'insert': DXFAttr(10, xtype=XType.point3d),
  69. 'scale_x': DXFAttr(41, default=1.), # scale x factor
  70. 'scale_y': DXFAttr(42, default=1.), # scale y factor
  71. 'scale_z': DXFAttr(43, default=1.), # scale z factor
  72. 'rotation': DXFAttr(50, default=0.), # rotation angle in degrees?
  73. 'extrusion': DXFAttr(210, xtype=XType.point3d),
  74. 'flags': DXFAttr(280, default=0), # Underlay display properties:
  75. # 1 = Clipping is on
  76. # 2 = Underlay is on
  77. # 4 = Monochrome
  78. # 8 = Adjust for background
  79. 'contrast': DXFAttr(281, default=100), # Contrast value (20-100; default = 100)
  80. 'fade': DXFAttr(282, default=0), # Fade value (0-80; default = 0)
  81. })
  82. class PdfUnderlay(ModernGraphicEntity):
  83. __slots__ = ()
  84. TEMPLATE = ExtendedTags.from_text(_PDFUNDERLAY_TPL)
  85. CLASS = ExtendedTags.from_text(_PDFUNDERLAY_CLS)
  86. DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, underlay_subclass)
  87. @property
  88. def clipping(self) -> bool:
  89. return bool(self.dxf.flags & const.UNDERLAY_CLIPPING)
  90. @clipping.setter
  91. def clipping(self, state: bool) -> None:
  92. self.set_flags(const.UNDERLAY_CLIPPING, state)
  93. @property
  94. def on(self) -> bool:
  95. return bool(self.dxf.flags & const.UNDERLAY_ON)
  96. @on.setter
  97. def on(self, state: bool) -> None:
  98. self.set_flags(const.UNDERLAY_ON, state)
  99. @property
  100. def monochrome(self) -> bool:
  101. return bool(self.dxf.flags & const.UNDERLAY_MONOCHROME)
  102. @monochrome.setter
  103. def monochrome(self, state: bool) -> None:
  104. self.set_flags(const.UNDERLAY_MONOCHROME, state)
  105. @property
  106. def adjust_for_background(self) -> bool:
  107. return bool(self.dxf.flags & const.UNDERLAY_ADJUST_FOR_BG)
  108. @adjust_for_background.setter
  109. def adjust_for_background(self, state: bool):
  110. self.set_flags(const.UNDERLAY_ADJUST_FOR_BG, state)
  111. @property
  112. def scale(self) -> Tuple[float, float, float]:
  113. return self.dxf.scale_x, self.dxf.scale_y, self.dxf.scale_z
  114. @scale.setter
  115. def scale(self, scale: Union[float, Tuple]):
  116. if type(scale) in (float, int):
  117. x, y, z = scale, scale, scale
  118. else:
  119. x, y, z = scale
  120. self.dxf.scale_x = x
  121. self.dxf.scale_y = y
  122. self.dxf.scale_z = z
  123. def set_flags(self, flag: int, state: bool = True) -> None:
  124. if state:
  125. self.dxf.flags = self.dxf.flags | flag
  126. else:
  127. self.dxf.flags = self.dxf.flags & ~flag
  128. def set_boundary_path(self, vertices: Iterable['Vertex']) -> None: # path coordinates as drawing coordinates but unscaled
  129. vertices = list(vertices)
  130. self._set_path_tags(vertices)
  131. self.clipping = bool(len(vertices))
  132. def _set_path_tags(self, vertices: Iterable['Vertex']):
  133. boundary = [DXFVertex(11, value) for value in vertices]
  134. subclasstags = Tags(tag for tag in self.tags.subclasses[2] if tag.code != 11) # filter out existing path tags
  135. subclasstags.extend(boundary)
  136. self.tags.subclasses[2] = subclasstags
  137. def reset_boundary_path(self) -> None:
  138. self._set_path_tags([])
  139. self.clipping = False
  140. def get_boundary_path(self) -> List['Vertex']:
  141. underlay_subclass = self.tags.subclasses[2]
  142. return [tag.value for tag in underlay_subclass if tag.code == 11] # fetch path tags
  143. def get_underlay_def(self) -> 'UnderlayDef':
  144. return cast('UnderlayDef', self.dxffactory.wrap_handle(self.dxf.underlay_def))
  145. def destroy(self) -> None:
  146. super(PdfUnderlay, self).destroy()
  147. underlay_def = self.get_underlay_def()
  148. underlay_def.remove_reactor_handle(self.dxf.handle)
  149. _DWFUNDERLAY_CLS = """0
  150. CLASS
  151. 1
  152. DWFUNDERLAY
  153. 2
  154. AcDbDwfReference
  155. 3
  156. ObjectDBX Classes
  157. 90
  158. 1153
  159. 91
  160. 0
  161. 280
  162. 0
  163. 281
  164. 1
  165. """
  166. class DwfUnderlay(PdfUnderlay):
  167. __slots__ = ()
  168. TEMPLATE = ExtendedTags.from_text(_PDFUNDERLAY_TPL.replace('PDF', 'DWF'))
  169. CLASS = ExtendedTags.from_text(_DWFUNDERLAY_CLS)
  170. _DGNUNDERLAY_CLS = """0
  171. CLASS
  172. 1
  173. DGNUNDERLAY
  174. 2
  175. AcDbDgnReference
  176. 3
  177. ObjectDBX Classes
  178. 90
  179. 1153
  180. 91
  181. 0
  182. 280
  183. 0
  184. 281
  185. 1
  186. """
  187. class DgnUnderlay(PdfUnderlay):
  188. __slots__ = ()
  189. TEMPLATE = ExtendedTags.from_text(_PDFUNDERLAY_TPL.replace('PDF', 'DGN'))
  190. CLASS = ExtendedTags.from_text(_DGNUNDERLAY_CLS)
  191. _PDF_DEF_CLS = """0
  192. CLASS
  193. 1
  194. PDFDEFINITION
  195. 2
  196. AcDbPdfDefinition
  197. 3
  198. ObjectDBX Classes
  199. 90
  200. 1153
  201. 91
  202. 0
  203. 280
  204. 0
  205. 281
  206. 0
  207. """
  208. # Using reactors in PdfDefinition for well defined UNDERLAYS
  209. _PDF_DEF_TPL = """0
  210. PDFDEFINITION
  211. 5
  212. 0
  213. 102
  214. {ACAD_REACTORS
  215. 102
  216. }
  217. 330
  218. 0
  219. 100
  220. AcDbUnderlayDefinition
  221. 1
  222. noname.pdf
  223. 2
  224. 1
  225. """
  226. underlay_def_subclass = DefSubclass('AcDbUnderlayDefinition', {
  227. 'filename': DXFAttr(1), # File name of underlay
  228. 'name': DXFAttr(2), # underlay name - pdf=page number to display; dgn=default; dwf=????
  229. })
  230. # (PDF|DWF|DGN)DEFINITION - requires entry in objects table ACAD_(PDF|DWF|DGN)DEFINITIONS,
  231. # ACAD_(PDF|DWF|DGN)DEFINITIONS do not exist by default
  232. class PdfDefinition(DXFEntity):
  233. __slots__ = ()
  234. TEMPLATE = ExtendedTags.from_text(_PDF_DEF_TPL)
  235. CLASS = ExtendedTags.from_text(_PDF_DEF_CLS)
  236. DXFATTRIBS = DXFAttributes(none_subclass, underlay_def_subclass)
  237. @property
  238. def entity_name(self):
  239. return self.dxftype()[:3] + "UNDERLAY"
  240. def post_new_hook(self):
  241. self.set_reactors([self.dxf.owner])
  242. _DWF_DEF_CLS = """0
  243. CLASS
  244. 1
  245. DWFDEFINITION
  246. 2
  247. AcDbDwfDefinition
  248. 3
  249. ObjectDBX Classes
  250. 90
  251. 1153
  252. 91
  253. 0
  254. 280
  255. 0
  256. 281
  257. 0
  258. """
  259. class DwfDefinition(PdfDefinition):
  260. __slots__ = ()
  261. TEMPLATE = ExtendedTags.from_text(_PDF_DEF_TPL.replace('PDF', 'DWF'))
  262. CLASS = ExtendedTags.from_text(_DWF_DEF_CLS)
  263. _DGN_DEF_CLS = """0
  264. CLASS
  265. 1
  266. DGNDEFINITION
  267. 2
  268. AcDbDgnDefinition
  269. 3
  270. ObjectDBX Classes
  271. 90
  272. 1153
  273. 91
  274. 0
  275. 280
  276. 0
  277. 281
  278. 0
  279. """
  280. class DgnDefinition(PdfDefinition):
  281. __slots__ = ()
  282. TEMPLATE = ExtendedTags.from_text(_PDF_DEF_TPL.replace('PDF', 'DGN'))
  283. CLASS = ExtendedTags.from_text(_DGN_DEF_CLS)
  284. UnderlayDef = Union[PdfDefinition, DgnDefinition, DwfDefinition]
  285. Underlay = Union[PdfUnderlay, DgnUnderlay, DwfUnderlay]