1"""
2Low-level text helper utilities.
3"""
4
5from __future__ import annotations
6
7import dataclasses
8
9from . import _api
10from .ft2font import KERNING_DEFAULT, LOAD_NO_HINTING, FT2Font
11
12
13@dataclasses.dataclass(frozen=True)
14class LayoutItem:
15 ft_object: FT2Font
16 char: str
17 glyph_idx: int
18 x: float
19 prev_kern: float
20
21
22def warn_on_missing_glyph(codepoint, fontnames):
23 _api.warn_external(
24 f"Glyph {codepoint} "
25 f"({chr(codepoint).encode('ascii', 'namereplace').decode('ascii')}) "
26 f"missing from font(s) {fontnames}.")
27
28 block = ("Hebrew" if 0x0590 <= codepoint <= 0x05ff else
29 "Arabic" if 0x0600 <= codepoint <= 0x06ff else
30 "Devanagari" if 0x0900 <= codepoint <= 0x097f else
31 "Bengali" if 0x0980 <= codepoint <= 0x09ff else
32 "Gurmukhi" if 0x0a00 <= codepoint <= 0x0a7f else
33 "Gujarati" if 0x0a80 <= codepoint <= 0x0aff else
34 "Oriya" if 0x0b00 <= codepoint <= 0x0b7f else
35 "Tamil" if 0x0b80 <= codepoint <= 0x0bff else
36 "Telugu" if 0x0c00 <= codepoint <= 0x0c7f else
37 "Kannada" if 0x0c80 <= codepoint <= 0x0cff else
38 "Malayalam" if 0x0d00 <= codepoint <= 0x0d7f else
39 "Sinhala" if 0x0d80 <= codepoint <= 0x0dff else
40 None)
41 if block:
42 _api.warn_external(
43 f"Matplotlib currently does not support {block} natively.")
44
45
46def layout(string, font, *, kern_mode=KERNING_DEFAULT):
47 """
48 Render *string* with *font*.
49
50 For each character in *string*, yield a LayoutItem instance. When such an instance
51 is yielded, the font's glyph is set to the corresponding character.
52
53 Parameters
54 ----------
55 string : str
56 The string to be rendered.
57 font : FT2Font
58 The font.
59 kern_mode : int
60 A FreeType kerning mode.
61
62 Yields
63 ------
64 LayoutItem
65 """
66 x = 0
67 prev_glyph_idx = None
68 char_to_font = font._get_fontmap(string)
69 base_font = font
70 for char in string:
71 # This has done the fallback logic
72 font = char_to_font.get(char, base_font)
73 glyph_idx = font.get_char_index(ord(char))
74 kern = (
75 base_font.get_kerning(prev_glyph_idx, glyph_idx, kern_mode) / 64
76 if prev_glyph_idx is not None else 0.
77 )
78 x += kern
79 glyph = font.load_glyph(glyph_idx, flags=LOAD_NO_HINTING)
80 yield LayoutItem(font, char, glyph_idx, x, kern)
81 x += glyph.linearHoriAdvance / 65536
82 prev_glyph_idx = glyph_idx