Skip to content

Commit a1df646

Browse files
committed
Add an rcParam to restore old kerning behaviour.
This is enabled for the classic test style, so that the 100 or so old images do not need to be regenerated.
1 parent 7f57740 commit a1df646

17 files changed

+75
-5
lines changed

lib/matplotlib/font_manager.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,8 @@ def is_opentype_cff_font(filename):
13271327
def get_font(filename, hinting_factor=None):
13281328
if hinting_factor is None:
13291329
hinting_factor = rcParams['text.hinting_factor']
1330-
return _get_font(os.fspath(filename), hinting_factor)
1330+
return _get_font(os.fspath(filename), hinting_factor,
1331+
_kerning_factor=rcParams['text.kerning_factor'])
13311332

13321333

13331334
def _rebuild():
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# This patch should go on top of the "classic" style and exists solely to avoid
22
# changing baseline images.
33

4+
text.kerning_factor : 6
5+
46
ytick.alignment: center_baseline

lib/matplotlib/rcsetup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,7 @@ def _validate_linestyle(ls):
10911091
'text.latex.preview': [False, validate_bool],
10921092
'text.hinting': ['auto', validate_hinting],
10931093
'text.hinting_factor': [8, validate_int],
1094+
'text.kerning_factor': [0, validate_int],
10941095
'text.antialiased': [True, validate_bool],
10951096

10961097
'mathtext.cal': ['cursive', validate_font_properties],

lib/matplotlib/tests/test_artist.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,9 @@ def test_remove():
201201

202202
@image_comparison(["default_edges.png"], remove_text=True, style='default')
203203
def test_default_edges():
204+
# Remove this line when this test image is regenerated.
205+
plt.rcParams['text.kerning_factor'] = 6
206+
204207
fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2)
205208

206209
ax1.plot(np.arange(10), np.arange(10), 'x',

lib/matplotlib/tests/test_axes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ def test_get_labels():
4848

4949
@image_comparison(['acorr.png'], style='mpl20')
5050
def test_acorr():
51+
# Remove this line when this test image is regenerated.
52+
plt.rcParams['text.kerning_factor'] = 6
53+
5154
np.random.seed(19680801)
5255
n = 512
5356
x = np.random.normal(0, 1, n).cumsum()
@@ -5730,6 +5733,9 @@ def test_axisbelow():
57305733

57315734
@image_comparison(['titletwiny.png'], style='mpl20')
57325735
def test_titletwiny():
5736+
# Remove this line when this test image is regenerated.
5737+
plt.rcParams['text.kerning_factor'] = 6
5738+
57335739
# Test that title is put above xlabel if xlabel at top
57345740
fig, ax = plt.subplots()
57355741
fig.subplots_adjust(top=0.8)

lib/matplotlib/tests/test_image.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
@image_comparison(['image_interps'], style='mpl20')
2828
def test_image_interps():
2929
'make the basic nearest, bilinear and bicubic interps'
30+
# Remove this line when this test image is regenerated.
31+
plt.rcParams['text.kerning_factor'] = 6
32+
3033
X = np.arange(100)
3134
X = X.reshape(5, 20)
3235

lib/matplotlib/tests/test_legend.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ def test_legend_expand():
188188

189189
@image_comparison(['hatching'], remove_text=True, style='default')
190190
def test_hatching():
191+
# Remove this line when this test image is regenerated.
192+
plt.rcParams['text.kerning_factor'] = 6
193+
191194
fig, ax = plt.subplots()
192195

193196
# Patches

lib/matplotlib/tests/test_text.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ def test_multiline():
130130

131131
@image_comparison(['multiline2'], style='mpl20')
132132
def test_multiline2():
133+
# Remove this line when this test image is regenerated.
134+
plt.rcParams['text.kerning_factor'] = 6
135+
133136
fig, ax = plt.subplots()
134137

135138
ax.set_xlim([0, 1.4])
@@ -580,6 +583,9 @@ def test_annotation_update():
580583

581584
@image_comparison(['large_subscript_title.png'], style='mpl20')
582585
def test_large_subscript_title():
586+
# Remove this line when this test image is regenerated.
587+
plt.rcParams['text.kerning_factor'] = 6
588+
583589
fig, axs = plt.subplots(1, 2, figsize=(9, 2.5), constrained_layout=True)
584590
ax = axs[0]
585591
ax.set_title(r'$\sum_{i} x_i$')

lib/matplotlib/tests/test_widgets.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ def test_CheckButtons():
264264

265265
@image_comparison(['check_radio_buttons.png'], style='mpl20', remove_text=True)
266266
def test_check_radio_buttons_image():
267+
# Remove this line when this test image is regenerated.
268+
plt.rcParams['text.kerning_factor'] = 6
269+
267270
get_ax()
268271
plt.subplots_adjust(left=0.3)
269272
rax1 = plt.axes([0.05, 0.7, 0.15, 0.15])

lib/mpl_toolkits/tests/test_axisartist_axis_artist.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ def test_ticks():
2626

2727
@image_comparison(['axis_artist_labelbase.png'], style='default')
2828
def test_labelbase():
29+
# Remove this line when this test image is regenerated.
30+
plt.rcParams['text.kerning_factor'] = 6
31+
2932
fig, ax = plt.subplots()
3033

3134
ax.plot([0.5], [0.5], "o")
@@ -40,6 +43,9 @@ def test_labelbase():
4043

4144
@image_comparison(['axis_artist_ticklabels.png'], style='default')
4245
def test_ticklabels():
46+
# Remove this line when this test image is regenerated.
47+
plt.rcParams['text.kerning_factor'] = 6
48+
4349
fig, ax = plt.subplots()
4450

4551
ax.xaxis.set_visible(False)
@@ -72,6 +78,9 @@ def test_ticklabels():
7278

7379
@image_comparison(['axis_artist.png'], style='default')
7480
def test_axis_artist():
81+
# Remove this line when this test image is regenerated.
82+
plt.rcParams['text.kerning_factor'] = 6
83+
7584
fig, ax = plt.subplots()
7685

7786
ax.xaxis.set_visible(False)

lib/mpl_toolkits/tests/test_axisartist_axislines.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
@image_comparison(['SubplotZero.png'], style='default')
1313
def test_SubplotZero():
14+
# Remove this line when this test image is regenerated.
15+
plt.rcParams['text.kerning_factor'] = 6
16+
1417
fig = plt.figure()
1518

1619
ax = SubplotZero(fig, 1, 1, 1)
@@ -29,6 +32,9 @@ def test_SubplotZero():
2932

3033
@image_comparison(['Subplot.png'], style='default')
3134
def test_Subplot():
35+
# Remove this line when this test image is regenerated.
36+
plt.rcParams['text.kerning_factor'] = 6
37+
3238
fig = plt.figure()
3339

3440
ax = Subplot(fig, 1, 1, 1)

lib/mpl_toolkits/tests/test_axisartist_floating_axes.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ def test_curvelinear3():
7474

7575
@image_comparison(['curvelinear4.png'], style='default', tol=0.015)
7676
def test_curvelinear4():
77+
# Remove this line when this test image is regenerated.
78+
plt.rcParams['text.kerning_factor'] = 6
79+
7780
fig = plt.figure(figsize=(5, 5))
7881

7982
tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) +

lib/mpl_toolkits/tests/test_axisartist_grid_helper_curvelinear.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ def inverted(self):
8585
@image_comparison(['polar_box.png'], style='default',
8686
tol={'aarch64': 0.04}.get(platform.machine(), 0.03))
8787
def test_polar_box():
88+
# Remove this line when this test image is regenerated.
89+
plt.rcParams['text.kerning_factor'] = 6
90+
8891
fig = plt.figure(figsize=(5, 5))
8992

9093
# PolarAxes.PolarTransform takes radian. However, we want our coordinate
@@ -145,6 +148,9 @@ def test_polar_box():
145148

146149
@image_comparison(['axis_direction.png'], style='default', tol=0.03)
147150
def test_axis_direction():
151+
# Remove this line when this test image is regenerated.
152+
plt.rcParams['text.kerning_factor'] = 6
153+
148154
fig = plt.figure(figsize=(5, 5))
149155

150156
# PolarAxes.PolarTransform takes radian. However, we want our coordinate

matplotlibrc.template

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,10 @@
316316
#text.hinting_factor : 8 ## Specifies the amount of softness for hinting in the
317317
## horizontal direction. A value of 1 will hint to full
318318
## pixels. A value of 2 will hint to half pixels etc.
319+
#text.kerning_factor : 0 ## Specifies the scaling factor for kerning values. This
320+
## is provided solely to allow old test images to remain
321+
## unchanged. Set to 6 to obtain previous behavior. Values
322+
## other than 0 or 6 have no defined meaning.
319323
#text.antialiased : True ## If True (default), the text will be antialiased.
320324
## This only affects the Agg backend.
321325

src/ft2font.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,9 @@ FT2Font::FT2Font(FT_Open_Args &open_args, long hinting_factor_) : image(), face(
525525
throw_ft_error("Can not load face", error);
526526
}
527527

528+
// set default kerning factor to 0, i.e., no kerning manipulation
529+
kerning_factor = 0;
530+
528531
// set a default fontsize 12 pt at 72dpi
529532
hinting_factor = hinting_factor_;
530533

@@ -602,12 +605,17 @@ int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode)
602605
FT_Vector delta;
603606

604607
if (!FT_Get_Kerning(face, left, right, mode, &delta)) {
605-
return (int)(delta.x) / hinting_factor;
608+
return (int)(delta.x) / (hinting_factor << kerning_factor);
606609
} else {
607610
return 0;
608611
}
609612
}
610613

614+
void FT2Font::set_kerning_factor(int factor)
615+
{
616+
kerning_factor = factor;
617+
}
618+
611619
void FT2Font::set_text(
612620
size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector<double> &xys)
613621
{
@@ -640,7 +648,7 @@ void FT2Font::set_text(
640648
if (use_kerning && previous && glyph_index) {
641649
FT_Vector delta;
642650
FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
643-
pen.x += delta.x / hinting_factor;
651+
pen.x += delta.x / (hinting_factor << kerning_factor);
644652
}
645653
if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) {
646654
throw_ft_error("Could not load glyph", error);

src/ft2font.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class FT2Font
7474
void set_text(
7575
size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector<double> &xys);
7676
int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode);
77+
void set_kerning_factor(int factor);
7778
void load_char(long charcode, FT_Int32 flags);
7879
void load_glyph(FT_UInt glyph_index, FT_Int32 flags);
7980
void get_width_height(long *width, long *height);
@@ -122,6 +123,7 @@ class FT2Font
122123
FT_BBox bbox;
123124
FT_Pos advance;
124125
long hinting_factor;
126+
int kerning_factor;
125127

126128
// prevent copying
127129
FT2Font(const FT2Font &);

src/ft2font_wrapper.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,10 +553,12 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds)
553553
PyObject *fname;
554554
FT_Open_Args open_args;
555555
long hinting_factor = 8;
556-
const char *names[] = { "filename", "hinting_factor", NULL };
556+
int kerning_factor = 0;
557+
const char *names[] = { "filename", "hinting_factor", "_kerning_factor", NULL };
557558

558559
if (!PyArg_ParseTupleAndKeywords(
559-
args, kwds, "O|l:FT2Font", (char **)names, &fname, &hinting_factor)) {
560+
args, kwds, "O|l$i:FT2Font", (char **)names, &fname,
561+
&hinting_factor, &kerning_factor)) {
560562
return -1;
561563
}
562564

@@ -567,6 +569,8 @@ static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds)
567569
CALL_CPP_FULL(
568570
"FT2Font", (self->x = new FT2Font(open_args, hinting_factor)), PyFT2Font_fail(self), -1);
569571

572+
CALL_CPP("FT2Font->set_kerning_factor", (self->x->set_kerning_factor(kerning_factor)));
573+
570574
Py_INCREF(fname);
571575
self->fname = fname;
572576

0 commit comments

Comments
 (0)