8000 Merge pull request #9867 from anntzer/pdfps-common · matplotlib/matplotlib@2b86111 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2b86111

Browse files
authored
Merge pull request #9867 from anntzer/pdfps-common
Factor out common code between pdf and ps backends.
2 parents c044780 + 7031b42 commit 2b86111

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(chunks):
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 inherited
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-
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