# Created: 07.03.2016 # Copyright (c) 2016-2018, Manfred Moitzi # License: MIT License from typing import Iterable, Tuple, Sequence, List, TYPE_CHECKING, 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.math import Vector from .graphics import none_subclass, entity_subclass, ModernGraphicEntity if TYPE_CHECKING: from ezdxf.eztypes import Vertex _IMAGE_CLS = """0 CLASS 1 IMAGE 2 AcDbRasterImage 3 ISM 90 2175 91 0 280 0 281 1 """ _IMAGE_TPL = """0 IMAGE 5 0 330 0 100 AcDbEntity 8 0 100 AcDbRasterImage 90 0 10 0.0 20 0.0 30 0.0 11 0.0 21 0.0 31 0.0 12 0.0 22 0.0 32 0.0 13 640 23 320 340 0 70 3 280 0 281 50 282 50 283 0 360 0 71 1 91 2 """ image_subclass = DefSubclass('AcDbRasterImage', { 'insert': DXFAttr(10, xtype=XType.point3d), 'u_pixel': DXFAttr(11, xtype=XType.point3d), # U-vector of a single pixel (points along the visual bottom of the image, starting at the insertion point) 'v_pixel': DXFAttr(12, xtype=XType.point3d), # V2-vector of a single pixel (points along the visual left side of the image, starting at the insertion point) 'image_size': DXFAttr(13, xtype=XType.point2d), # Image size in pixels 'image_def': DXFAttr(340), # Hard reference to image def object 'flags': DXFAttr(70, default=3), # Image display properties: # 1 = Show image # 2 = Show image when not aligned with screen # 4 = Use clipping boundary # 8 = Transparency is on 'clipping': DXFAttr(280, default=0), # Clipping state: 0 = Off; 1 = On 'brightness': DXFAttr(281, default=50), # Brightness value (0-100; default = 50) 'contrast': DXFAttr(282, default=50), # Contrast value (0-100; default = 50) 'fade': DXFAttr(283, default=0), # Fade value (0-100; default = 0) 'image_def_reactor': DXFAttr(360), # Hard reference to image def reactor object, not required by AutoCAD 'clipping_boundary_type': DXFAttr(71, default=1), # Clipping boundary type. 1 = Rectangular; 2 = Polygonal 'count_boundary_points': DXFAttr(91), # Number of clip boundary vertices that follow 'clip_mode': DXFAttr(290, dxfversion='AC1024'), # 0 = outside, 1 = inside mode # boundary path coordinates are pixel coordinates NOT drawing units }) class Image(ModernGraphicEntity): __slots__ = () TEMPLATE = ExtendedTags.from_text(_IMAGE_TPL) CLASS = ExtendedTags.from_text(_IMAGE_CLS) DXFATTRIBS = DXFAttributes(none_subclass, entity_subclass, image_subclass) # flags for IMAGE SHOW_IMAGE = 1 SHOW_IMAGE_WHEN_NOT_ALIGNED = 2 USE_CLIPPING_BOUNDARY = 4 USE_TRANSPARENCY = 8 def post_new_hook(self) -> None: self.reset_boundary_path() def set_boundary_path(self, vertices: Iterable[Tuple[float, float]]) -> None: vertices = list(vertices) if len(vertices) > 2 and vertices[-1] != vertices[0]: vertices.append(vertices[0]) # close path, else AutoCAD crashes self._set_path_tags(vertices) self.set_flag_state(self.USE_CLIPPING_BOUNDARY, state=True) self.dxf.clipping = 1 self.dxf.clipping_boundary_type = 1 if len(vertices) < 3 else 2 def _set_path_tags(self, vertices: Sequence[Tuple[float, float]]): boundary = [DXFVertex(14, value) for value in vertices] subclasstags = Tags(tag for tag in self.tags.subclasses[2] if tag.code != 14) subclasstags.extend(boundary) self.tags.subclasses[2] = subclasstags self.dxf.count_boundary_points = len(vertices) def reset_boundary_path(self) -> None: lower_left_corner = (-.5, -.5) upper_right_corner = Vector(self.dxf.image_size) + lower_left_corner self._set_path_tags([lower_left_corner, upper_right_corner[:2]]) self.set_flag_state(Image.USE_CLIPPING_BOUNDARY, state=False) self.dxf.clipping = 0 self.dxf.clipping_boundary_type = 1 def get_boundary_path(self) -> List['Vertex']: image_subclass = self.tags.subclasses[2] return [tag.value for tag in image_subclass if tag.code == 14] def get_image_def(self) -> 'ImageDef': return cast('ImageDef', self.dxffactory.wrap_handle(self.dxf.image_def)) def destroy(self) -> None: super(Image, self).destroy() # remove rectors image_def = self.get_image_def() reactor_handle = self.get_dxf_attrib('image_def_reactor', None) if reactor_handle is None: return image_def.remove_reactor_handle(reactor_handle) if self.drawing is not None: reactor = self.dxffactory.wrap_handle(reactor_handle) self.drawing.objects.delete_entity(reactor) _IMAGE_DEF_CLS = """0 CLASS 1 IMAGEDEF 2 AcDbRasterImageDef 3 ISM 90 0 91 0 280 0 281 0 """ _IMAGE_DEF_TPL = """0 IMAGEDEF 5 0 330 0 102 {ACAD_REACTORS 102 } 100 AcDbRasterImageDef 90 0 1 path/filename.jpg 10 640 20 480 11 0.01 21 0.01 280 1 281 0 """ image_def_subclass = DefSubclass('AcDbRasterImageDef', { 'class_version': DXFAttr(90), # class version 'filename': DXFAttr(1), # File name of image 'image_size': DXFAttr(10, xtype=XType.point2d), # image size in pixels 'pixel_size': DXFAttr(11, xtype=XType.point2d), # Default size of one pixel in AutoCAD units 'loaded': DXFAttr(280, default=1), 'resolution_units': DXFAttr(281, default=0), # Resolution units. 0 = No units; 2 = Centimeters; 5 = Inch }) # IMAGEDEF - requires entry in objects table ACAD_IMAGE_DICT, ACAD_IMAGE_DICT exists not by default class ImageDef(DXFEntity): TEMPLATE = ExtendedTags.from_text(_IMAGE_DEF_TPL) CLASS = ExtendedTags.from_text(_IMAGE_DEF_CLS) DXFATTRIBS = DXFAttributes(none_subclass, image_def_subclass) _IMAGE_DEF_REACTOR_CLS = """0 CLASS 1 IMAGEDEF_REACTOR 2 AcDbRasterImageDefReactor 3 ISM 90 1 91 0 280 0 281 0 """ _IMAGE_DEF_REACTOR_TPL = """0 IMAGEDEF_REACTOR 5 0 330 0 100 AcDbRasterImageDefReactor 90 2 330 0 """ # IMAGEDEF_REACTOR is not required by AutoCAD # owner -> IMAGE class ImageDefReactor(DXFEntity): TEMPLATE = ExtendedTags.from_text(_IMAGE_DEF_REACTOR_TPL) CLASS = ExtendedTags.from_text(_IMAGE_DEF_REACTOR_CLS) DXFATTRIBS = DXFAttributes(none_subclass, DefSubclass('AcDbRasterImageDef', { 'image': DXFAttr(330), # handle to image })) _RASTER_VARIABLES_CLS = """0 CLASS 1 RASTERVARIABLES 2 AcDbRasterVariables 3 ISM 90 0 91 0 280 0 281 0 """ _RASTER_VARIABLES_TPL = """0 RASTERVARIABLES 5 0 102 {ACAD_REACTORS 330 0 102 } 330 0 100 AcDbRasterVariables 90 0 70 0 71 1 72 3 """ class RasterVariables(DXFEntity): TEMPLATE = ExtendedTags.from_text(_RASTER_VARIABLES_TPL) CLASS = ExtendedTags.from_text(_RASTER_VARIABLES_CLS) DXFATTRIBS = DXFAttributes( none_subclass, DefSubclass('AcDbRasterVariables', { 'version': DXFAttr(90, default=0), 'frame': DXFAttr(70, default=0), # 0 = no frame; 1= show frame 'quality': DXFAttr(71, default=1), # 0=draft; 1=high 'units': DXFAttr(72, default=3), # 0 = None; 1 = mm; 2 = cm 3 = m; 4 = km; 5 = in 6 = ft; 7 = yd; 8 = mi }), )