Coverage for /usr/lib/python3/dist-packages/matplotlib/backends/_backend_pdf_ps.py: 66%
80 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1"""
2Common functionality between the PDF and PS backends.
3"""
5from io import BytesIO
6import functools
8from fontTools import subset
10import matplotlib as mpl
11from .. import font_manager, ft2font
12from .._afm import AFM
13from ..backend_bases import RendererBase
16@functools.lru_cache(50)
17def _cached_get_afm_from_fname(fname):
18 with open(fname, "rb") as fh:
19 return AFM(fh)
22def get_glyphs_subset(fontfile, characters):
23 """
24 Subset a TTF font
26 Reads the named fontfile and restricts the font to the characters.
27 Returns a serialization of the subset font as file-like object.
29 Parameters
30 ----------
31 symbol : str
32 Path to the font file
33 characters : str
34 Continuous set of characters to include in subset
35 """
37 options = subset.Options(glyph_names=True, recommended_glyphs=True)
39 # prevent subsetting FontForge Timestamp and other tables
40 options.drop_tables += ['FFTM', 'PfEd', 'BDF']
42 # if fontfile is a ttc, specify font number
43 if fontfile.endswith(".ttc"):
44 options.font_number = 0
46 with subset.load_font(fontfile, options) as font:
47 subsetter = subset.Subsetter(options=options)
48 subsetter.populate(text=characters)
49 subsetter.subset(font)
50 fh = BytesIO()
51 font.save(fh, reorderTables=False)
52 return fh
55class CharacterTracker:
56 """
57 Helper for font subsetting by the pdf and ps backends.
59 Maintains a mapping of font paths to the set of character codepoints that
60 are being used from that font.
61 """
63 def __init__(self):
64 self.used = {}
66 def track(self, font, s):
67 """Record that string *s* is being typeset using font *font*."""
68 char_to_font = font._get_fontmap(s)
69 for _c, _f in char_to_font.items():
70 self.used.setdefault(_f.fname, set()).add(ord(_c))
72 def track_glyph(self, font, glyph):
73 """Record that codepoint *glyph* is being typeset using font *font*."""
74 self.used.setdefault(font.fname, set()).add(glyph)
77class RendererPDFPSBase(RendererBase):
78 # The following attributes must be defined by the subclasses:
79 # - _afm_font_dir
80 # - _use_afm_rc_name
82 def __init__(self, width, height):
83 super().__init__()
84 self.width = width
85 self.height = height
87 def flipy(self):
88 # docstring inherited
89 return False # y increases from bottom to top.
91 def option_scale_image(self):
92 # docstring inherited
93 return True # PDF and PS support arbitrary image scaling.
95 def option_image_nocomposite(self):
96 # docstring inherited
97 # Decide whether to composite image based on rcParam value.
98 return not mpl.rcParams["image.composite_image"]
100 def get_canvas_width_height(self):
101 # docstring inherited
102 return self.width * 72.0, self.height * 72.0
104 def get_text_width_height_descent(self, s, prop, ismath):
105 # docstring inherited
106 if ismath == "TeX":
107 texmanager = self.get_texmanager()
108 fontsize = prop.get_size_in_points()
109 w, h, d = texmanager.get_text_width_height_descent(
110 s, fontsize, renderer=self)
111 return w, h, d
112 elif ismath:
113 parse = self._text2path.mathtext_parser.parse(s, 72, prop)
114 return parse.width, parse.height, parse.depth
115 elif mpl.rcParams[self._use_afm_rc_name]:
116 font = self._get_font_afm(prop)
117 l, b, w, h, d = font.get_str_bbox_and_descent(s)
118 scale = prop.get_size_in_points() / 1000
119 w *= scale
120 h *= scale
121 d *= scale
122 return w, h, d
123 else:
124 font = self._get_font_ttf(prop)
125 font.set_text(s, 0.0, flags=ft2font.LOAD_NO_HINTING)
126 w, h = font.get_width_height()
127 d = font.get_descent()
128 scale = 1 / 64
129 w *= scale
130 h *= scale
131 d *= scale
132 return w, h, d
134 def _get_font_afm(self, prop):
135 fname = font_manager.findfont(
136 prop, fontext="afm", directory=self._afm_font_dir)
137 return _cached_get_afm_from_fname(fname)
139 def _get_font_ttf(self, prop):
140 fnames = font_manager.fontManager._find_fonts_by_props(prop)
141 font = font_manager.get_font(fnames)
142 font.clear()
143 font.set_size(prop.get_size_in_points(), 72)
144 return font