123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- # Purpose: The MText entity is a composite entity, consisting of basic TEXT entities.
- # Created: 09.03.2010, adapted 2018 for ezdxf
- # Copyright (c) 2010-2018, Manfred Moitzi
- # License: MIT License
- """
- MText -- MultiLine-Text-Entity, created by simple TEXT-Entities.
- MTEXT was introduced in R13, so this is a replacement with multiple simple
- TEXT entities. Supports valign (TOP, MIDDLE, BOTTOM), halign (LEFT, CENTER,
- RIGHT), rotation for an arbitrary (!) angle and mirror.
- """
- from typing import TYPE_CHECKING
- import math
- from .mixins import SubscriptAttributes
- import ezdxf
- from ezdxf.lldxf import const
- if TYPE_CHECKING:
- from ezdxf.eztypes import Vertex, GenericLayoutType
- class MText(SubscriptAttributes):
- """
- MultiLine-Text buildup with simple Text-Entities.
- Caution: align point is always the insert point, I don't need a second
- alignpoint because horizontal alignment FIT, ALIGN, BASELINE_MIDDLE is not
- supported.
- linespacing -- linespacing in percent of height, 1.5 = 150% = 1+1/2 lines
- supported align values:
- 'BOTTOM_LEFT', 'BOTTOM_CENTER', 'BOTTOM_RIGHT'
- 'MIDDLE_LEFT', 'MIDDLE_CENTER', 'MIDDLE_RIGHT'
- 'TOP_LEFT', 'TOP_CENTER', 'TOP_RIGHT'
- """
- MIRROR_X = const.MIRROR_X
- MIRROR_Y = const.MIRROR_Y
- TOP = const.TOP
- MIDDLE = const.MIDDLE
- BOTTOM = const.BOTTOM
- LEFT = const.LEFT
- CENTER = const.CENTER
- RIGHT = const.RIGHT
- VALID_ALIGN = frozenset([
- 'BOTTOM_LEFT',
- 'BOTTOM_CENTER',
- 'BOTTOM_RIGHT',
- 'MIDDLE_LEFT',
- 'MIDDLE_CENTER',
- 'MIDDLE_RIGHT',
- 'TOP_LEFT',
- 'TOP_CENTER',
- 'TOP_RIGHT',
- ])
- def __init__(self, text: str, insert: 'Vertex', linespacing: float = 1.5, **kwargs):
- self.textlines = text.split('\n')
- self.insert = insert
- self.linespacing = linespacing
- if 'align' in kwargs:
- self.align = kwargs.get('align', 'TOP_LEFT').upper()
- else: # support for compatibility: valign, halign
- halign = kwargs.get('halign', 0)
- valign = kwargs.get('valign', 3)
- self.align = const.TEXT_ALIGNMENT_BY_FLAGS.get((halign, valign), 'TOP_LEFT')
- if self.align not in MText.VALID_ALIGN:
- raise ezdxf.DXFValueError('Invalid align parameter: {}'.format(self.align))
- self.height = kwargs.get('height', 1.0)
- self.style = kwargs.get('style', 'STANDARD')
- self.oblique = kwargs.get('oblique', 0.0) # in degree
- self.rotation = kwargs.get('rotation', 0.0) # in degree
- self.xscale = kwargs.get('xscale', 1.0)
- self.mirror = kwargs.get('mirror', 0) # renamed to text_generation_flag in ezdxf
- self.layer = kwargs.get('layer', '0')
- self.color = kwargs.get('color', const.BYLAYER)
- @property
- def lineheight(self) -> float:
- """ Absolute linespacing in drawing units.
- """
- return self.height * self.linespacing
- def render(self, layout: 'GenericLayoutType') -> None:
- """ Create the DXF-TEXT entities.
- """
- textlines = self.textlines
- if len(textlines) > 1:
- if self.mirror & const.MIRROR_Y:
- textlines.reverse()
- for linenum, text in enumerate(textlines):
- alignpoint = self._get_align_point(linenum)
- layout.add_text(
- text,
- dxfattribs=self._dxfattribs(alignpoint),
- )
- elif len(textlines) == 1:
- layout.add_text(
- textlines[0],
- dxfattribs=self._dxfattribs(self.insert),
- )
- def _get_align_point(self, linenum: int) -> 'Vertex':
- """ Calculate the align point depending on the line number.
- """
- x = self.insert[0]
- y = self.insert[1]
- try:
- z = self.insert[2]
- except IndexError:
- z = 0.
- # rotation not respected
- if self.align.startswith('TOP'):
- y -= linenum * self.lineheight
- elif self.align.startswith('MIDDLE'):
- y0 = linenum * self.lineheight
- fullheight = (len(self.textlines) - 1) * self.lineheight
- y += (fullheight / 2) - y0
- else: # BOTTOM
- y += (len(self.textlines) - 1 - linenum) * self.lineheight
- return self._rotate((x, y, z)) # consider rotation
- def _rotate(self, alignpoint: 'Vertex') -> 'Vertex':
- """
- Rotate alignpoint around insert point about rotation degrees.
- """
- dx = alignpoint[0] - self.insert[0]
- dy = alignpoint[1] - self.insert[1]
- beta = math.radians(self.rotation)
- x = self.insert[0] + dx * math.cos(beta) - dy * math.sin(beta)
- y = self.insert[1] + dy * math.cos(beta) + dx * math.sin(beta)
- return round(x, 6), round(y, 6), alignpoint[2]
- def _dxfattribs(self, alignpoint: 'Vertex') -> dict:
- """
- Build keyword arguments for TEXT entity creation.
- """
- halign, valign = const.TEXT_ALIGN_FLAGS.get(self.align)
- return {
- 'insert': alignpoint,
- 'align_point': alignpoint,
- 'layer': self.layer,
- 'color': self.color,
- 'style': self.style,
- 'height': self.height,
- 'width': self.xscale,
- 'text_generation_flag': self.mirror,
- 'rotation': self.rotation,
- 'oblique': self.oblique,
- 'halign': halign,
- 'valign': valign,
- }
|