""" Common functionality between the PDF and PS backends. """ import functools import matplotlib as mpl from .. import font_manager, ft2font from ..afm import AFM from ..backend_bases import RendererBase @functools.lru_cache(50) def _cached_get_afm_from_fname(fname): with open(fname, "rb") as fh: return AFM(fh) class CharacterTracker: """ Helper for font subsetting by the pdf and ps backends. Maintains a mapping of font paths to the set of character codepoints that are being used from that font. """ def __init__(self): self.used = {} @mpl.cbook.deprecated("3.3") @property def used_characters(self): d = {} for fname, chars in self.used.items(): realpath, stat_key = mpl.cbook.get_realpath_and_stat(fname) d[stat_key] = (realpath, chars) return d def track(self, font, s): """Record that string *s* is being typeset using font *font*.""" if isinstance(font, str): # Unused, can be removed after removal of track_characters. fname = font else: fname = font.fname self.used.setdefault(fname, set()).update(map(ord, s)) def merge(self, other): """Update self with a font path to character codepoints.""" for fname, charset in other.items(): self.used.setdefault(fname, set()).update(charset) class RendererPDFPSBase(RendererBase): # The following attributes must be defined by the subclasses: # - _afm_font_dir # - _use_afm_rc_name def __init__(self, width, height): super().__init__() self.width = width self.height = height def flipy(self): # docstring inherited return False # y increases from bottom to top. def option_scale_image(self): # docstring inherited return True # PDF and PS support arbitrary image scaling. def option_image_nocomposite(self): # docstring inherited # Decide whether to composite image based on rcParam value. return not mpl.rcParams["image.composite_image"] def get_canvas_width_height(self): # docstring inherited return self.width * 72.0, self.height * 72.0 def get_text_width_height_descent(self, s, prop, ismath): # docstring inherited if ismath == "TeX": texmanager = self.get_texmanager() fontsize = prop.get_size_in_points() w, h, d = texmanager.get_text_width_height_descent( s, fontsize, renderer=self) return w, h, d elif ismath: parse = self.mathtext_parser.parse(s, 72, prop) return parse.width, parse.height, parse.depth elif mpl.rcParams[self._use_afm_rc_name]: font = self._get_font_afm(prop) l, b, w, h, d = font.get_str_bbox_and_descent(s) scale = prop.get_size_in_points() / 1000 w *= scale h *= scale d *= scale return w, h, d else: font = self._get_font_ttf(prop) font.set_text(s, 0.0, flags=ft2font.LOAD_NO_HINTING) w, h = font.get_width_height() d = font.get_descent() scale = 1 / 64 w *= scale h *= scale d *= scale return w, h, d def _get_font_afm(self, prop): fname = font_manager.findfont( prop, fontext="afm", directory=self._afm_font_dir) return _cached_get_afm_from_fname(fname) def _get_font_ttf(self, prop): fname = font_manager.findfont(prop) font = font_manager.get_font(fname) font.clear() font.set_size(prop.get_size_in_points(), 72) return font