1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
"""
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
|