def make_hatches_from_str(s: str, font: fonts.FontFace, size: float = 1.0, align: str = 'LEFT', length: float = 0, segments: int = 4, dxfattribs: Dict = None, m: Matrix44 = None) -> List[Hatch]: """ Convert a single line string `s` into a list of virtual :class:`~ezdxf.entities.Hatch` entities. The text `size` is the height of the uppercase letter "X" (cap height). The paths are aligned about the insertion point at (0, 0). The HATCH entities are aligned to this insertion point. BASELINE means the bottom of the letter "X". Args: s: text to convert font: font face definition size: text size (cap height) in drawing units align: alignment as string, default is "LEFT" length: target length for the "ALIGNED" and "FIT" alignments segments: minimal segment count per Bézier curve dxfattribs: additional DXF attributes m: transformation :class:`~ezdxf.math.Matrix44` """ font_properties, font_measurements = _get_font_data(font) # scale cap_height for 1 drawing unit! scaled_size = size / font_measurements.cap_height scaled_fm = font_measurements.scale_from_baseline(scaled_size) paths = _str_to_paths(s, font_properties, scaled_size) # HATCH is an OCS entity, transforming just the polyline paths # is not correct! The Hatch has to be created in the xy-plane! hatches = [] dxfattribs = dxfattribs or dict() dxfattribs.setdefault('solid_fill', 1) dxfattribs.setdefault('pattern_name', 'SOLID') dxfattribs.setdefault('color', 7) for contour, holes in group_contour_and_holes(paths): hatch = Hatch.new(dxfattribs=dxfattribs) # Vec2 removes the z-axis, which would be interpreted as bulge value! hatch.paths.add_polyline_path( Vec2.generate(contour.flattening(1, segments=segments)), flags=1) for hole in holes: hatch.paths.add_polyline_path( Vec2.generate(hole.flattening(1, segments=segments)), flags=0) hatches.append(hatch) halign, valign = const.TEXT_ALIGN_FLAGS[align.upper()] bbox = path.bbox(paths, precise=False) matrix = get_alignment_transformation(scaled_fm, bbox, halign, valign) if m is not None: matrix *= m # Transform HATCH entities as a unit: return [hatch.transform(matrix) for hatch in hatches]
def make_paths_from_str(s: str, font: fonts.FontFace, size: float = 1.0, align: str = 'LEFT', length: float = 0, m: Matrix44 = None) -> List[Path]: """ Convert a single line string `s` into a list of :class:`~ezdxf.render.path.Path` objects. All paths are returned in a single list. The text `size` is the height of the uppercase letter "X" (cap height). The paths are aligned about the insertion point at (0, 0). BASELINE means the bottom of the letter "X". Args: s: text to convert font: font face definition size: text size (cap height) in drawing units align: alignment as string, default is "LEFT" length: target length for the "ALIGNED" and "FIT" alignments m: transformation :class:`~ezdxf.math.Matrix44` """ font_properties, font_measurements = _get_font_data(font) scaled_size = size / font_measurements.cap_height scaled_fm = font_measurements.scale_from_baseline(scaled_size) paths = _str_to_paths(s, font_properties, scaled_size) bbox = path.bbox(paths, precise=False) halign, valign = const.TEXT_ALIGN_FLAGS[align.upper()] matrix = get_alignment_transformation(scaled_fm, bbox, halign, valign) stretch_x = 1.0 stretch_y = 1.0 if align == 'ALIGNED': stretch_x = length / bbox.size.x stretch_y = stretch_x elif align == 'FIT': stretch_x = length / bbox.size.x if stretch_x != 1.0: matrix *= Matrix44.scale(stretch_x, stretch_y, 1.0) if m is not None: matrix *= m return list(path.transform_paths(paths, matrix))
def test_non_uniform_stretch_paths(self, spath): result = fit_paths_into_box([spath], (8, 7, 6), uniform=False) box = bbox(result) assert box.size == (8, 7, 6)
def test_project_into_xz(self, spath): result = fit_paths_into_box([spath], (6, 0, 6)) box = bbox(result) assert box.size == (2, 0, 6), "y-axis should be ignored"
def test_project_into_xy(self, spath): result = fit_paths_into_box([spath], (6, 6, 0)) box = bbox(result) # Note: z-axis is also ignored by extent detection: # scaling factor = 3x assert box.size == (3, 6, 0), "z-axis should be ignored"
def test_uniform_shrink_paths(self, spath): result = fit_paths_into_box([spath], (1.5, 1.5, 1.5)) box = bbox(result) assert box.size == (0.5, 1, 1.5)
def test_uniform_stretch_paths_limited_by_x(self, spath): result = fit_paths_into_box([spath], (1.2, 6, 6)) box = bbox(result) # stretch factor: 1.2 assert box.size == (1.2, 2.4, 3.6)
def test_uniform_stretch_paths_limited_by_y(self, spath): result = fit_paths_into_box([spath], (6, 3, 6)) box = bbox(result) # stretch factor: 1.5 assert box.size == (1.5, 3, 4.5)
def test_project_into_xz(self, spath): result = path.fit_paths_into_box([spath], (6, 0, 6), uniform=False) box = path.bbox(result) assert box.size == (6, 0, 6), "y-axis should be ignored"
def test_precise_box(self, quadratic): result = bbox([quadratic], precise=True) assert result.extmax.y == pytest.approx(0.5) # parabola
def test_not_precise_box(self, quadratic): result = bbox([quadratic], precise=False) assert result.extmax.y == pytest.approx(1) # control point
def test_two_path(self): p1 = Path() p1.line_to((1, 2, 3)) p2 = Path() p2.line_to((-3, -2, -1)) assert bbox([p1, p2]).size == (4, 4, 4)
def test_one_path(self): p = Path() p.line_to((1, 2, 3)) assert bbox([p]).size == (1, 2, 3)
def test_empty_paths(self): result = bbox([]) assert result.has_data is False
def test_non_uniform_shrink_paths(self, spath): result = fit_paths_into_box([spath], (1.5, 1.5, 1.5), uniform=False) box = bbox(result) assert box.size == (1.5, 1.5, 1.5)
def test_uniform_stretch_paths_limited_by_z(self, spath): result = fit_paths_into_box([spath], (6, 6, 6)) box = bbox(result) assert box.size == (2, 4, 6)
def test_project_into_yz(self, spath): result = fit_paths_into_box([spath], (0, 6, 6), uniform=False) box = bbox(result) assert box.size == (0, 6, 6), "x-axis should be ignored"
def test_project_into_yz(self, spath): result = path.fit_paths_into_box([spath], (0, 6, 6)) box = path.bbox(result) assert box.size == (0, 4, 6), "x-axis should be ignored"