_text_helpers.py 2.5 KB

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