| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- """
- Common functionality between the PDF and PS backends.
- """
- from io import BytesIO
- import functools
- import logging
- from fontTools import subset
- 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)
- def get_glyphs_subset(fontfile, characters):
- """
- Subset a TTF font
- Reads the named fontfile and restricts the font to the characters.
- Parameters
- ----------
- fontfile : str
- Path to the font file
- characters : str
- Continuous set of characters to include in subset
- Returns
- -------
- fontTools.ttLib.ttFont.TTFont
- An open font object representing the subset, which needs to
- be closed by the caller.
- """
- options = subset.Options(glyph_names=True, recommended_glyphs=True)
- # Prevent subsetting extra tables.
- options.drop_tables += [
- 'FFTM', # FontForge Timestamp.
- 'PfEd', # FontForge personal table.
- 'BDF', # X11 BDF header.
- 'meta', # Metadata stores design/supported languages (meaningless for subsets).
- 'MERG', # Merge Table.
- 'TSIV', # Microsoft Visual TrueType extension.
- 'Zapf', # Information about the individual glyphs in the font.
- 'bdat', # The bitmap data table.
- 'bloc', # The bitmap location table.
- 'cidg', # CID to Glyph ID table (Apple Advanced Typography).
- 'fdsc', # The font descriptors table.
- 'feat', # Feature name table (Apple Advanced Typography).
- 'fmtx', # The Font Metrics Table.
- 'fond', # Data-fork font information (Apple Advanced Typography).
- 'just', # The justification table (Apple Advanced Typography).
- 'kerx', # An extended kerning table (Apple Advanced Typography).
- 'ltag', # Language Tag.
- 'morx', # Extended Glyph Metamorphosis Table.
- 'trak', # Tracking table.
- 'xref', # The cross-reference table (some Apple font tooling information).
- ]
- # if fontfile is a ttc, specify font number
- if fontfile.endswith(".ttc"):
- options.font_number = 0
- font = subset.load_font(fontfile, options)
- subsetter = subset.Subsetter(options=options)
- subsetter.populate(text=characters)
- subsetter.subset(font)
- return font
- def font_as_file(font):
- """
- Convert a TTFont object into a file-like object.
- Parameters
- ----------
- font : fontTools.ttLib.ttFont.TTFont
- A font object
- Returns
- -------
- BytesIO
- A file object with the font saved into it
- """
- fh = BytesIO()
- font.save(fh, reorderTables=False)
- return 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 = {}
- def track(self, font, s):
- """Record that string *s* is being typeset using font *font*."""
- char_to_font = font._get_fontmap(s)
- for _c, _f in char_to_font.items():
- self.used.setdefault(_f.fname, set()).add(ord(_c))
- def track_glyph(self, font, glyph):
- """Record that codepoint *glyph* is being typeset using font *font*."""
- self.used.setdefault(font.fname, set()).add(glyph)
- 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":
- return super().get_text_width_height_descent(s, prop, ismath)
- elif ismath:
- parse = self._text2path.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.LoadFlags.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):
- fnames = font_manager.fontManager._find_fonts_by_props(prop)
- try:
- font = font_manager.get_font(fnames)
- font.clear()
- font.set_size(prop.get_size_in_points(), 72)
- return font
- except RuntimeError:
- logging.getLogger(__name__).warning(
- "The PostScript/PDF backend does not currently "
- "support the selected font (%s).", fnames)
- raise
|