8000 Merge pull request #18181 from anntzer/unttpdf · matplotlib/matplotlib@c9bf76c · GitHub
[go: up one dir, main page]

Skip to content

Commit c9bf76c

Browse files
authored
Merge pull request #18181 from anntzer/unttpdf
Replace ttconv by plain python for pdf subsetting
2 parents 0375315 + 19359c1 commit c9bf76c

20 files changed

+3233
-3100
lines changed

lib/matplotlib/backends/backend_pdf.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
from matplotlib.path import Path
4343
from matplotlib.dates import UTC
4444
from matplotlib import _path
45-
from matplotlib import _ttconv
4645
from . import _backend_pdf_ps
4746

4847
_log = logging.getLogger(__name__)
@@ -556,6 +555,52 @@ def _flush(self):
556555
self.compressobj = None
557556

558557

558+
def _get_pdf_charprocs(font_path, glyph_ids):
559+
font = get_font(font_path, hinting_factor=1)
560+
conv = 1000 / font.units_per_EM # Conversion to PS units (1/1000's).
561+
procs = {}
562+
for glyph_id in glyph_ids:
563+
g = font.load_glyph(glyph_id, LOAD_NO_SCALE)
564+
# NOTE: We should be using round(), but instead use
565+
# "(x+.5).astype(int)" to keep backcompat with the old ttconv code
566+
# (this is different for negative x's).
567+
d1 = (np.array([g.horiAdvance, 0, *g.bbox]) * conv + .5).astype(int)
568+
v, c = font.get_path()
569+
v = (v * 64).astype(int) # Back to TrueType's internal units (1/64's).
570+
# Backcompat with old ttconv code: control points between two quads are
571+
# omitted if they are exactly at the midpoint between the control of
572+
# the quad before and the quad after, but ttconv used to interpolate
573+
# *after* conversion to PS units, causing floating point errors. Here
574+
# we reproduce ttconv's logic, detecting these "implicit" points and
575+
# re-interpolating them. Note that occasionally (e.g. with DejaVu Sans
576+
# glyph "0") a point detected as "implicit" is actually explicit, and
577+
# will thus be shifted by 1.
578+
quads, = np.nonzero(c == 3)
579+
quads_on = quads[1::2]
580+
quads_mid_on = np.array(
581+
sorted({*quads_on} & {*(quads - 1)} & {*(quads + 1)}), int)
582+
implicit = quads_mid_on[
583+
(v[quads_mid_on] # As above, use astype(int), not // division
584+
== ((v[quads_mid_on - 1] + v[quads_mid_on + 1]) / 2).astype(int))
585+
.all(axis=1)]
586+
if (font.postscript_name, glyph_id) in [
587+
("DejaVuSerif-Italic", 77), # j
588+
("DejaVuSerif-Italic", 135), # \AA
589+
]:
590+
v[:, 0] -= 1 # Hard-coded backcompat (FreeType shifts glyph by 1).
591+
v = (v * conv + .5).astype(int) # As above re: truncation vs rounding.
592+
v[implicit] = (( # Fix implicit points; again, truncate.
593+
(v[implicit - 1] + v[implicit + 1]) / 2).astype(int))
594+
procs[font.get_glyph_name(glyph_id)] = (
595+
" ".join(map(str, d1)).encode("ascii") + b" d1\n"
596+
+ _path.convert_to_string(
597+
Path(v, c), None, None, False, None, -1,
598+
# no code for quad Beziers triggers auto-conversion to cubics.
599+
[b"m", b"l", b"", b"c", b"h"], True)
600+
+ b"f")
601+
return procs
602+
603+
559604
class PdfFile:
560605
"""PDF file object."""
561606

@@ -1089,15 +1134,8 @@ def get_char_width(charcode):
10891134
differencesArray.append(Name(name))
10901135
last_c = c
10911136

1092-
# Make the charprocs array (using ttconv to generate the
1093-
# actual outlines)
1094-
try:
1095-
rawcharprocs = _ttconv.get_pdf_charprocs(
1096-
os.fsencode(filename), glyph_ids)
1097-
except RuntimeError:
1098-
_log.warning("The PDF backend does not currently support the "
1099-
"selected font.")
1100-
raise
1137+
# Make the charprocs array.
1138+
rawcharprocs = _get_pdf_charprocs(filename, glyph_ids)
11011139
charprocs = {}
11021140
for charname in sorted(rawcharprocs):
11031141
stream = rawcharprocs[charname]
Binary file not shown.

lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_69.svg

Lines changed: 129 additions & 114 deletions
Loading

0 commit comments

Comments
 (0)
0