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

1""" 

2Common functionality between the PDF and PS backends. 

3""" 

4 

5from io import BytesIO 

6import functools 

7 

8from fontTools import subset 

9 

10import matplotlib as mpl 

11from .. import font_manager, ft2font 

12from .._afm import AFM 

13from ..backend_bases import RendererBase 

14 

15 

16@functools.lru_cache(50) 

17def _cached_get_afm_from_fname(fname): 

18 with open(fname, "rb") as fh: 

19 return AFM(fh) 

20 

21 

22def get_glyphs_subset(fontfile, characters): 

23 """ 

24 Subset a TTF font 

25 

26 Reads the named fontfile and restricts the font to the characters. 

27 Returns a serialization of the subset font as file-like object. 

28 

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 """ 

36 

37 options = subset.Options(glyph_names=True, recommended_glyphs=True) 

38 

39 # prevent subsetting FontForge Timestamp and other tables 

40 options.drop_tables += ['FFTM', 'PfEd', 'BDF'] 

41 

42 # if fontfile is a ttc, specify font number 

43 if fontfile.endswith(".ttc"): 

44 options.font_number = 0 

45 

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 

53 

54 

55class CharacterTracker: 

56 """ 

57 Helper for font subsetting by the pdf and ps backends. 

58 

59 Maintains a mapping of font paths to the set of character codepoints that 

60 are being used from that font. 

61 """ 

62 

63 def __init__(self): 

64 self.used = {} 

65 

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)) 

71 

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) 

75 

76 

77class RendererPDFPSBase(RendererBase): 

78 # The following attributes must be defined by the subclasses: 

79 # - _afm_font_dir 

80 # - _use_afm_rc_name 

81 

82 def __init__(self, width, height): 

83 super().__init__() 

84 self.width = width 

85 self.height = height 

86 

87 def flipy(self): 

88 # docstring inherited 

89 return False # y increases from bottom to top. 

90 

91 def option_scale_image(self): 

92 # docstring inherited 

93 return True # PDF and PS support arbitrary image scaling. 

94 

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"] 

99 

100 def get_canvas_width_height(self): 

101 # docstring inherited 

102 return self.width * 72.0, self.height * 72.0 

103 

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 

133 

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) 

138 

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