Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd81d35b

Browse files
committed
Stop using ttconv for pdf.
Quite a bit of work goes into reproducing quirks of the old ttconv codein order not to update baseline images, but this can easily be droppedonce the new baseline images system goes in.figure_align_label.pdf gets modified because of tiny differences inoutline extraction (due to the ttconv emulation); there's already thesvg test that tests the fixed-dpi case so I feel OK deleting it.This also fixes ttc subsetting for pdf output (cf. changes intest_font_manager.py).
1 parent4469385 commitd81d35b

File tree

5 files changed

+79
-38
lines changed

5 files changed

+79
-38
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
frommatplotlib.pathimportPath
4343
frommatplotlib.datesimportUTC
4444
frommatplotlibimport_path
45-
frommatplotlibimport_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+
forglyph_idinglyph_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+
returnprocs
602+
603+
559604
classPdfFile:
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-
exceptRuntimeError:
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
forcharnameinsorted(rawcharprocs):
11031141
stream=rawcharprocs[charname]
Binary file not shown.

‎lib/matplotlib/tests/test_figure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
importpytest
1919

2020

21-
@image_comparison(['figure_align_labels'],
21+
@image_comparison(['figure_align_labels'],extensions=['png','svg'],
2222
tol=0ifplatform.machine()=='x86_64'else0.01)
2323
deftest_align_labels():
2424
fig=plt.figure(tight_layout=True)

‎lib/matplotlib/tests/test_font_manager.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,7 @@ def test_find_ttc():
122122
ax.text(.5,.5,"\N{KANGXI RADICAL DRAGON}",fontproperties=fp)
123123
fig.savefig(BytesIO(),format="raw")
124124
fig.savefig(BytesIO(),format="svg")
125-
withpytest.raises(RuntimeError):
126-
fig.savefig(BytesIO(),format="pdf")
125+
fig.savefig(BytesIO(),format="pdf")
127126
withpytest.raises(RuntimeError):
128127
fig.savefig(BytesIO(),format="ps")
129128

‎src/_path.h

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,35 +1089,39 @@ void quad2cubic(double x0, double y0,
10891089
void__add_number(double val,char format_code,int precision,
10901090
std::string& buffer)
10911091
{
1092-
char *str =PyOS_double_to_string(val, format_code, precision,0,NULL);
1093-
1094-
// Delete trailing zeros and decimal point
1095-
char *q = str;
1096-
for (; *q !=0; ++q) {
1092+
if (precision == -1) {
1093+
// Special-case for compat with old ttconv code, which *truncated*
1094+
// values with a cast to int instead of rounding them as printf
1095+
// would do. The only point where non-integer values arise is from
1096+
// quad2cubic conversion (as we already perform a first truncation
1097+
// on Python's side), which can introduce additional floating point
1098+
// error (by adding 2/3 delta-x and then 1/3 delta-x), so compensate by
1099+
// first rounding to the closest 1/3 and then truncating.
1100+
char str[255];
1101+
PyOS_snprintf(str,255,"%d", (int)(round(val *3)) /3);
1102+
buffer += str;
1103+
}else {
1104+
char *str =PyOS_double_to_string(val, format_code, precision,0,NULL);
1105+
// Delete trailing zeros and decimal point
1106+
char *q = str;
10971107
// Find the end of the string
1098-
}
1099-
1100-
--q;
1101-
for (; q >= str && *q =='0'; --q) {
1102-
// Rewind through all the zeros
1103-
}
1104-
1105-
// If the end is a decimal point, delete that too
1106-
if (q >= str && *q =='.') {
1108+
while (*q) { ++q; }
11071109
--q;
1108-
}
1109-
1110-
// Truncate the string
1111-
++q;
1112-
*q =0;
1113-
1114-
try {
1115-
buffer += str;
1116-
}catch (std::bad_alloc& e) {
1110+
// Rewind through all the zeros
1111+
while (q >= str && *q =='0') { --q; }
1112+
// If the end is a decimal point, delete that too
1113+
if (q >= str && *q =='.') { --q; }
1114+
// Truncate the string
1115+
++q;
1116+
*q =0;
1117+
try {
1118+
buffer += str;
1119+
}catch (std::bad_alloc& e) {
1120+
PyMem_Free(str);
1121+
throw e;
1122+
}
11171123
PyMem_Free(str);
1118-
throw e;
11191124
}
1120-
PyMem_Free(str);
11211125
}
11221126

11231127

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp