|
42 | 42 | from matplotlib.path import Path
|
43 | 43 | from matplotlib.dates import UTC
|
44 | 44 | from matplotlib import _path
|
45 |
| -from matplotlib import _ttconv |
46 | 45 | from . import _backend_pdf_ps
|
47 | 46 |
|
48 | 47 | _log = logging.getLogger(__name__)
|
@@ -556,6 +555,50 @@ def _flush(self):
|
556 | 555 | self.compressobj = None
|
557 | 556 |
|
558 | 557 |
|
| 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. |
| 576 | + quads, = np.nonzero(c == 3) |
| 577 | + quads_on = quads[1::2] |
| 578 | + quads_mid_on = np.array( |
| 579 | + sorted({*quads_on} & {*quads - 1} & {*quads + 1}), int) |
| 580 | + implicit = quads_mid_on[ |
| 581 | + (v[quads_mid_on] # As above, use astype(int), not // division |
| 582 | + == ((v[quads_mid_on - 1] + v[quads_mid_on + 1]) / 2).astype(int)) |
| 583 | + .all(axis=1)] |
| 584 | + if (font.postscript_name, glyph_id) in [ |
| 585 | + ("DejaVuSerif-Italic", 77), # j |
| 586 | + ("DejaVuSerif-Italic", 135), # \AA |
| 587 | + ]: |
| 588 | + v[:, 0] -= 1 # Hard-coded backcompat (FreeType shifts glyph by 1). |
| 589 | + v = (v * conv + .5).astype(int) # As above re: truncation vs rounding. |
| 590 | + v[implicit] = (( # Fix implicit points; again, truncate. |
| 591 | + (v[implicit - 1] + v[implicit + 1]) / 2).astype(int)) |
| 592 | + procs[font.get_glyph_name(glyph_id)] = ( |
| 593 | + " ".join(map(str, d1)).encode("ascii") + b" d1\n" |
| 594 | + + _path.convert_to_string( |
| 595 | + Path(v, c), None, None, False, None, -1, |
| 596 | + # no code for quad Beziers triggers auto-conversion to cubics. |
| 597 | + [b"m", b"l", b"", b"c", b"h"], True) |
| 598 | + + b"f") |
| 599 | + return procs |
| 600 | + |
| 601 | + |
559 | 602 | class PdfFile:
|
560 | 603 | """PDF file object."""
|
561 | 604 |
|
@@ -1089,15 +1132,8 @@ def get_char_width(charcode):
|
1089 | 1132 | differencesArray.append(Name(name))
|
1090 | 1133 | last_c = c
|
1091 | 1134 |
|
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 |
| 1135 | + # Make the charprocs array. |
| 1136 | + rawcharprocs = _get_pdf_charprocs(filename, glyph_ids) |
1101 | 1137 | charprocs = {}
|
1102 | 1138 | for charname in sorted(rawcharprocs):
|
1103 | 1139 | stream = rawcharprocs[charname]
|
|
0 commit comments