8000 Factor out common code between pdf and ps backends. · matplotlib/matplotlib@7031b42 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7031b42

Browse files
committed
Factor out common code between pdf and ps backends.
(The AFM cache only needs to cache on filename, not on font properties, because the fontprop->filename conversion is already cached by font_manager. get_canvas_width_height can be merged because FigureCanvasPdf always makes sure to call newPage(width, height) and create a new RendererPdf with the same width and height, and it's likely other things break if the PdfFile and the Renderer's size don't match anyways.
1 parent 0c35833 commit 7031b42

File tree

5 files changed

+138
-182
lines changed

5 files changed

+138
-182
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
API deprecations
2+
````````````````
3+
4+
The following API elements are deprecated:
5+
6+
- ``backend_pdf.RendererPdf.afm_font_cache``,
7+
- ``backend_ps.RendererPS.afmfontd``,
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""
2+
Common functionality between the PDF and PS backends.
3+
"""
4+
5+
import functools
6+
7+
import matplotlib as mpl
8+
from .. import font_manager, ft2font
9+
from ..afm import AFM
10+
from ..backend_bases import RendererBase
11+
12+
13+
@functools.lru_cache(50)
14+
def _cached_get_afm_from_fname(fname):
15+
with open(fname, "rb") as fh:
16+
return AFM(fh)
17+
18+
19+
class RendererPDFPSBase(RendererBase):
20+
# The following attributes must be defined by the subclasses:
21+
# - _afm_font_dir
22+
# - _use_afm_rc_name
23+
24+
def flipy(self):
25+
# docstring inherited
26+
return False # y increases from bottom to top.
27+
28+
def option_scale_image(self):
29+
# docstring inherited
30+
return True # PDF and PS support arbitrary image scaling.
31+
32+
def option_image_nocomposite(self):
33+
# docstring inherited
34+
# Decide whether to composite image based on rcParam value.
35+
return not mpl.rcParams["image.composite_image"]
36+
37+
def get_canvas_width_height(self):
38+
# docstring inherited
39+
return self.width * 72.0, self.height * 72.0
40+
41+
def get_text_width_height_descent(self, s, prop, ismath):
42+
# docstring inherited
43+
if mpl.rcParams["text.usetex"]:
44+
texmanager = self.get_texmanager()
45+
fontsize = prop.get_size_in_points()
46+
w, h, d = texmanager.get_text_width_height_descent(
47+
s, fontsize, renderer=self)
48+
return w, h, d
49+
elif ismath:
50+
parse = self.mathtext_parser.parse(s, 72, prop)
51+
return parse.width, parse.height, parse.depth
52+
elif mpl.rcParams[self._use_afm_rc_name]:
53+
font = self._get_font_afm(prop)
54+
l, b, w, h, d = font.get_str_bbox_and_descent(s)
55+
scale = prop.get_size_in_points() / 1000
56+
w *= scale
57+
h *= scale
58+
d *= scale
59+
return w, h, d
60+
else:
61+
font = self._get_font_ttf(prop)
62+
font.set_text(s, 0.0, flags=ft2font.LOAD_NO_HINTING)
63+
w, h = font.get_width_height()
64+
d = font.get_descent()
65+
scale = 1 / 64
66+
w *= scale
67+
h *= scale
68+
d *= scale
69+
return w, h, d
70+
71+
def _get_font_afm(self, prop):
72+
fname = (
73+
font_manager.findfont(
74+
prop, fontext="afm", directory=self._afm_font_dir)
75+
or font_manager.findfont(
76+
"Helvetica", fontext="afm", directory=self._afm_font_dir))
77+
return _cached_get_afm_from_fname(fname)
78+
79+
def _get_font_ttf(self, prop):
80+
fname = font_manager.findfont(prop)
81+
font = font_manager.get_font(fname)
82+
font.clear()
83+
font.set_size(prop.get_size_in_points(), 72)
84+
return font

lib/matplotlib/backends/backend_pdf.py

Lines changed: 12 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import logging
1212
import math
1313
import os
14+
import pathlib
1415
import re
1516
import struct
1617
import time
@@ -26,7 +27,6 @@
2627
_Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase,
2728
RendererBase)
2829
from matplotlib.backends.backend_mixed import MixedModeRenderer
29-
from matplotlib.cbook import get_realpath_and_stat, maxdict
3030
from matplotlib.figure import Figure
3131
from matplotlib.font_manager import findfont, is_opentype_cff_font, get_font
3232
from matplotlib.afm import AFM
@@ -41,6 +41,7 @@
4141
from matplotlib import _path
4242
from matplotlib import _png
4343
from matplotlib import ttconv
44+
from . import _backend_pdf_ps
4445

4546
_log = logging.getLogger(__name__)
4647

@@ -706,7 +707,7 @@ def writeFonts(self):
706707
else:
707708
# a normal TrueType font
708709
_log.debug('Writing TrueType font.')
709-
realpath, stat_key = get_realpath_and_stat(filename)
710+
realpath, stat_key = cbook.get_realpath_and_stat(filename)
710711
chars = self.used_characters.get(stat_key)
711712
if chars is not None and len(chars[1]):
712713
fonts[Fx] = self.embedTTF(realpath, chars[1])
@@ -1579,8 +1580,14 @@ def writeTrailer(self):
15791580
self.write(b"\nstartxref\n%d\n%%%%EOF\n" % self.startxref)
15801581

15811582

1582-
class RendererPdf(RendererBase):
1583-
afm_font_cache = maxdict(50)
1583+
class RendererPdf(_backend_pdf_ps.RendererPDFPSBase):
1584+
@property
1585+
@cbook.deprecated("3.1")
1586+
def afm_font_cache(self, _cache=cbook.maxdict(50)):
1587+
return _cache
1588+
1589+
_afm_font_dir = pathlib.Path(rcParams["datapath"], "fonts", "pdfcorefonts")
1590+
_use_afm_rc_name = "pdf.use14corefonts"
15841591

15851592
def __init__(self, file, image_dpi, height, width):
15861593
RendererBase.__init__(self)
@@ -1628,7 +1635,7 @@ def track_characters(self, font, s):
16281635
fname = font
16291636
else:
16301637
fname = font.fname
1631-
realpath, stat_key = get_realpath_and_stat(fname)
1638+
realpath, stat_key = cbook.get_realpath_and_stat(fname)
16321639
used_characters = self.file.used_characters.setdefault(
16331640
stat_key, (realpath, set()))
16341641
used_characters[1].update(map(ord, s))
@@ -1642,14 +1649,6 @@ def merge_used_characters(self, other):
16421649
def get_image_magnification(self):
16431650
return self.image_dpi/72.0
16441651

1645-
def option_scale_image(self):
1646-
# docstring inherited
1647-
return True
1648-
1649-
def option_image_nocomposite(self):
1650-
# docstring inherited
1651-
return not rcParams['image.composite_image']
1652-
16531652
def draw_image(self, gc, x, y, im, transform=None):
16541653
# docstring inherited
16551654

@@ -2125,71 +2124,6 @@ def draw_text_woven(chunk F438 s):
21252124
else:
21262125
return draw_text_woven(chunks)
21272126

2128-
def get_text_width_height_descent(self, s, prop, ismath):
2129-
# docstring inherited
2130-
2131-
if rcParams['text.usetex']:
2132-
texmanager = self.get_texmanager()
2133-
fontsize = prop.get_size_in_points()
2134-
w, h, d = texmanager.get_text_width_height_descent(s, fontsize,
2135-
renderer=self)
2136-
return w, h, d
2137-
2138-
if ismath:
2139-
w, h, d, glyphs, rects, used_characters = \
2140-
self.mathtext_parser.parse(s, 72, prop)
2141-
2142-
elif rcParams['pdf.use14corefonts']:
2143-
font = self._get_font_afm(prop)
2144-
l, b, w, h, d = font.get_str_bbox_and_descent(s)
2145-
scale = prop.get_size_in_points()
2146-
w *= scale / 1000
2147-
h *= scale / 1000
2148-
d *= scale / 1000
2149-
else:
2150-
font = self._get_font_ttf(prop)
2151-
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
2152-
w, h = font.get_width_height()
2153-
scale = (1.0 / 64.0)
2154-
w *= scale
2155-
h *= scale
2156-
d = font.get_descent()
2157-
d *= scale
2158-
return w, h, d
2159-
2160-
def _get_font_afm(self, prop):
2161-
key = hash(prop)
2162-
font = self.afm_font_cache.get(key)
2163-
if font is None:
2164-
filename = findfont(
2165-
prop, fontext='afm', directory=self.file._core14fontdir)
2166-
if filename is None:
2167-
filename = findfont(
2168-
"Helvetica", fontext='afm',
2169-
directory=self.file._core14fontdir)
2170-
font = self.afm_font_cache.get(filename)
2171-
if font is None:
2172-
with open(filename, 'rb') as fh:
2173-
font = AFM(fh)
2174-
self.afm_font_cache[filename] = font
2175-
self.afm_font_cache[key] = font
2176-
return font
2177-
2178-
def _get_font_ttf(self, prop):
2179-
filename = findfont(prop)
2180-
font = get_font(filename)
2181-
font.clear()
2182-
font.set_size(prop.get_size_in_points(), 72)
2183-
return font
2184-
2185-
def flipy(self):
2186-
# docstring inherited
2187-
return False
2188-
2189-
def get_canvas_width_height(self):
2190-
# docstring inherited
2191-
return self.file.width * 72.0, self.file.height * 72.0
2192-
21932127
def new_gc(self):
21942128
# docstring inhe 10000 rited
21952129
return GraphicsContextPdf(self.file)

lib/matplotlib/backends/backend_ps.py

Lines changed: 13 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,22 @@
1717

1818
import numpy as np
1919

20-
from matplotlib import cbook, __version__, rcParams, checkdep_ghostscript
21-
from matplotlib.afm import AFM
20+
from matplotlib import (
21+
cbook, _path, __version__, rcParams, checkdep_ghostscript)
2222
from matplotlib.backend_bases import (
2323
_Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase,
2424
RendererBase)
2525
from matplotlib.cbook import (get_realpath_and_stat, is_writable_file_like,
26-
maxdict, file_requires_unicode)
27-
from matplotlib.font_manager import findfont, is_opentype_cff_font, get_font
26+
file_requires_unicode)
27+
from matplotlib.font_manager import is_opentype_cff_font, get_font
2828
from matplotlib.ft2font import KERNING_DEFAULT, LOAD_NO_HINTING
2929
from matplotlib.ttconv import convert_ttf_to_ps
3030
from matplotlib.mathtext import MathTextParser
3131
from matplotlib._mathtext_data import uni2type1
3232
from matplotlib.path import Path
33-
from matplotlib import _path
3433
from matplotlib.transforms import Affine2D
3534
from matplotlib.backends.backend_mixed import MixedModeRenderer
35+
from . import _backend_pdf_ps
3636

3737
_log = logging.getLogger(__name__)
3838

@@ -180,13 +180,19 @@ def _move_path_to_path_or_stream(src, dst):
180180
shutil.move(src, dst, copy_function=shutil.copyfile)
181181

182182

183-
class RendererPS(RendererBase):
183+
class RendererPS(_backend_pdf_ps.RendererPDFPSBase):
184184
"""
185185
The renderer handles all the drawing primitives using a graphics
186186
context instance that controls the colors/styles.
187187
"""
188188

189-
afmfontd = maxdict(50)
189+
@property
190+
@cbook.deprecated("3.1")
191+
def afmfontd(self, _cache=cbook.maxdict(50)):
192+
return _cache
193+
194+
_afm_font_dir = pathlib.Path(rcParams["datapath"], "fonts", "afm")
195+
_use_afm_rc_name = "ps.useafm"
190196

191197
def __init__(self, width, height, pswriter, imagedpi=72):
192198
# Although postscript itself is dpi independent, we need to imform the
@@ -217,9 +223,6 @@ def __init__(self, width, height, pswriter, imagedpi=72):
217223
self.used_characters = {}
218224
self.mathtext_parser = MathTextParser("PS")
219225

220-
self._afm_font_dir = os.path.join(
221-
rcParams['datapath'], 'fonts', 'afm')
222-
223226
def track_characters(self, font, s):
224227
"""Keeps track of which characters are required from each font."""
225228
realpath, stat_key = get_realpath_and_stat(font.fname)
@@ -323,75 +326,6 @@ def create_hatch(self, hatch):
323326
self._hatches[hatch] = name
324327
return name
325328

326-
def get_canvas_width_height(self):
327-
# docstring inherited
328-
return self.width * 72.0, self.height * 72.0
329-
330-
def get_text_width_height_descent(self, s, prop, ismath):
331-
# docstring inherited
332-
333-
if rcParams['text.usetex']:
334-
texmanager = self.get_texmanager()
335-
fontsize = prop.get_size_in_points()
336-
w, h, d = texmanager.get_text_width_height_descent(s, fontsize,
337-
renderer=self)
338-
return w, h, d
339-
340-
if ismath:
341-
width, height, descent, pswriter, used_characters = \
342-
self.mathtext_parser.parse(s, 72, prop)
343-
return width, height, descent
344-
345-
if rcParams['ps.useafm']:
346-
if ismath:
347-
s = s[1:-1]
348-
font = self._get_font_afm(prop)
349-
l, b, w, h, d = font.get_str_bbox_and_descent(s)
350-
351-
fontsize = prop.get_size_in_points()
352-
scale = 0.001 * fontsize
353-
w *= scale
354-
h *= scale
355-
d *= scale
356-
return w, h, d
357-
358-
font = self._get_font_ttf(prop)
359-
font.set_text(s, 0.0, flags=LOAD_NO_HINTING)
360-
w, h = font.get_width_height()
361-
w /= 64 # convert from subpixels
362-
h /= 64
363-
d = font.get_descent()
364-
d /= 64
365-
return w, h, d
366-
367-
def flipy(self):
368-
# docstring inherited
369-
return False
370-
371-
def _get_font_afm(self, prop):
372-
key = hash(prop)
373-
font = self.afmfontd.get(key)
374-
if font is None:
375-
fname = findfont(prop, fontext='afm', directory=self._afm_font_dir)
376-
if fname is None:
377-
fname = findfont(
378-
"Helvetica", fontext='afm', directory=self._afm_font_dir)
379-
font = self.afmfontd.get(fname)
380-
if font is None:
381-
with open(fname, 'rb') as fh:
382-
font = AFM(fh)
383-
self.afmfontd[fname] = font
384-
self.afmfontd[key] = font
385-
return font
386- 741A
387-
def _get_font_ttf(self, prop):
388-
fname = findfont(prop)
389-
font = get_font(fname)
390-
font.clear()
391-
size = prop.get_size_in_points()
392-
font.set_size(size, 72.0)
393-
return font
394-
395329
def get_image_magnification(self):
396330
"""
397331
Get the factor by which to magnify images passed to draw_image.
@@ -400,14 +334,6 @@ def get_image_magnification(self):
400334
"""
401335
return self.image_magnification
402336

403-
def option_scale_image(self):
404-
# docstring inherited
405-
return True
406-
407-
def option_image_nocomposite(self):
408-
# docstring inherited
409-
return not rcParams['image.composite_image']
410-
411337
def draw_image(self, gc, x, y, im, transform=None):
412338
# docstring inherited
413339

0 commit comments

Comments
 (0)
0