From 8caff885c195fd65588c5dae6c4feaf5e57c44f7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 3 May 2025 03:04:33 -0400 Subject: [PATCH 01/20] Remove ttconv backwards-compatibility code --- lib/matplotlib/backends/backend_pdf.py | 38 +++++---------------- src/_path.h | 47 ++++++++++---------------- 2 files changed, 25 insertions(+), 60 deletions(-) diff --git a/lib/matplotlib/backends/backend_pdf.py b/lib/matplotlib/backends/backend_pdf.py index 8db640d888b1..53bf10c90b48 100644 --- a/lib/matplotlib/backends/backend_pdf.py +++ b/lib/matplotlib/backends/backend_pdf.py @@ -617,40 +617,18 @@ def _get_pdf_charprocs(font_path, glyph_ids): procs = {} for glyph_id in glyph_ids: g = font.load_glyph(glyph_id, LoadFlags.NO_SCALE) - # NOTE: We should be using round(), but instead use - # "(x+.5).astype(int)" to keep backcompat with the old ttconv code - # (this is different for negative x's). - d1 = (np.array([g.horiAdvance, 0, *g.bbox]) * conv + .5).astype(int) + d1 = [ + round(g.horiAdvance * conv), 0, + # Round bbox corners *outwards*, so that they indeed bound the glyph. + math.floor(g.bbox[0] * conv), math.floor(g.bbox[1] * conv), + math.ceil(g.bbox[2] * conv), math.ceil(g.bbox[3] * conv), + ] v, c = font.get_path() - v = (v * 64).astype(int) # Back to TrueType's internal units (1/64's). - # Backcompat with old ttconv code: control points between two quads are - # omitted if they are exactly at the midpoint between the control of - # the quad before and the quad after, but ttconv used to interpolate - # *after* conversion to PS units, causing floating point errors. Here - # we reproduce ttconv's logic, detecting these "implicit" points and - # re-interpolating them. Note that occasionally (e.g. with DejaVu Sans - # glyph "0") a point detected as "implicit" is actually explicit, and - # will thus be shifted by 1. - quads, = np.nonzero(c == 3) - quads_on = quads[1::2] - quads_mid_on = np.array( - sorted({*quads_on} & {*(quads - 1)} & {*(quads + 1)}), int) - implicit = quads_mid_on[ - (v[quads_mid_on] # As above, use astype(int), not // division - == ((v[quads_mid_on - 1] + v[quads_mid_on + 1]) / 2).astype(int)) - .all(axis=1)] - if (font.postscript_name, glyph_id) in [ - ("DejaVuSerif-Italic", 77), # j - ("DejaVuSerif-Italic", 135), # \AA - ]: - v[:, 0] -= 1 # Hard-coded backcompat (FreeType shifts glyph by 1). - v = (v * conv + .5).astype(int) # As above re: truncation vs rounding. - v[implicit] = (( # Fix implicit points; again, truncate. - (v[implicit - 1] + v[implicit + 1]) / 2).astype(int)) + v = (v * 64 * conv).round() # Back to TrueType's internal units (1/64's). procs[font.get_glyph_name(glyph_id)] = ( " ".join(map(str, d1)).encode("ascii") + b" d1\n" + _path.convert_to_string( - Path(v, c), None, None, False, None, -1, + Path(v, c), None, None, False, None, 0, # no code for quad Beziers triggers auto-conversion to cubics. [b"m", b"l", b"", b"c", b"h"], True) + b"f") diff --git a/src/_path.h b/src/_path.h index c03703776760..1b54426c7e81 100644 --- a/src/_path.h +++ b/src/_path.h @@ -1066,38 +1066,25 @@ void quad2cubic(double x0, double y0, void __add_number(double val, char format_code, int precision, std::string& buffer) { - if (precision == -1) { - // Special-case for compat with old ttconv code, which *truncated* - // values with a cast to int instead of rounding them as printf - // would do. The only point where non-integer values arise is from - // quad2cubic conversion (as we already perform a first truncation - // on Python's side), which can introduce additional floating point - // error (by adding 2/3 delta-x and then 1/3 delta-x), so compensate by - // first rounding to the closest 1/3 and then truncating. - char str[255]; - PyOS_snprintf(str, 255, "%d", (int)(round(val * 3)) / 3); - buffer += str; - } else { - char *str = PyOS_double_to_string( - val, format_code, precision, Py_DTSF_ADD_DOT_0, nullptr); - // Delete trailing zeros and decimal point - char *c = str + strlen(str) - 1; // Start at last character. - // Rewind through all the zeros and, if present, the trailing decimal - // point. Py_DTSF_ADD_DOT_0 ensures we won't go past the start of str. - while (*c == '0') { - --c; - } - if (*c == '.') { - --c; - } - try { - buffer.append(str, c + 1); - } catch (std::bad_alloc& e) { - PyMem_Free(str); - throw e; - } + char *str = PyOS_double_to_string( + val, format_code, precision, Py_DTSF_ADD_DOT_0, nullptr); + // Delete trailing zeros and decimal point + char *c = str + strlen(str) - 1; // Start at last character. + // Rewind through all the zeros and, if present, the trailing decimal + // point. Py_DTSF_ADD_DOT_0 ensures we won't go past the start of str. + while (*c == '0') { + --c; + } + if (*c == '.') { + --c; + } + try { + buffer.append(str, c + 1); + } catch (std::bad_alloc& e) { PyMem_Free(str); + throw e; } + PyMem_Free(str); } From c44db77b9fb1318934767cfa01397ba6b81e30f7 Mon Sep 17 00:00:00 2001 From: Wiliam Date: Tue, 26 Nov 2024 19:24:26 +0100 Subject: [PATCH 02/20] Fix center of rotation with rotation_mode='anchor' --- lib/matplotlib/backends/backend_agg.py | 18 ++++++++++++++---- .../test_text/rotation_anchor.png | Bin 0 -> 15484 bytes lib/matplotlib/tests/test_text.py | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_text/rotation_anchor.png diff --git a/lib/matplotlib/backends/backend_agg.py b/lib/matplotlib/backends/backend_agg.py index b435ae565ce4..f25b89e2b053 100644 --- a/lib/matplotlib/backends/backend_agg.py +++ b/lib/matplotlib/backends/backend_agg.py @@ -197,10 +197,20 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): xo, yo = font.get_bitmap_offset() xo /= 64.0 yo /= 64.0 - xd = d * sin(radians(angle)) - yd = d * cos(radians(angle)) - x = round(x + xo + xd) - y = round(y + yo + yd) + + rad = radians(angle) + xd = d * sin(rad) + yd = d * cos(rad) + # Rotating the offset vector ensures text rotates around the anchor point. + # Without this, rotated text offsets incorrectly, causing a horizontal shift. + # Applying the 2D rotation matrix. + rotated_xo = xo * cos(rad) - yo * sin(rad) + rotated_yo = xo * sin(rad) + yo * cos(rad) + # Subtract rotated_yo to account for the inverted y-axis in computer graphics, + # compared to the mathematical convention. + x = round(x + rotated_xo + xd) + y = round(y - rotated_yo + yd) + self._renderer.draw_text_image(font, x, y + 1, angle, gc) def get_text_width_height_descent(self, s, prop, ismath): diff --git a/lib/matplotlib/tests/baseline_images/test_text/rotation_anchor.png b/lib/matplotlib/tests/baseline_images/test_text/rotation_anchor.png new file mode 100644 index 0000000000000000000000000000000000000000..3dad1f9a19f754558bcaae58122536d4544bd710 GIT binary patch literal 15484 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

e-(#m58!y>EXEBxmOCkFyGfXFjdMYq~`*< z6WjNf@2x3a@_dzxJM|ldFxN`-l9a5g*FRV2tC>occ?1-)J1k=bCX*Nx>iNx|t@h3E zMW#dpd8n0*tlw0nn9;ut4mTu$fyfHz&=fnKJa~nay?DO0X%tV`42tke49HXI|INX{ znodkW@SCZbnSkYt7)JhDIr)z&XGS2D(Nlm(JlRy{-L--6Pj z1|{`}cok227M%XmqTGaVbsY~}u9aQY`u?8ot>+@5&H1k~OPUt8EP?t=jF=THtRK<6 z>^LJC{J*C@PEP6Hdi|q@w$53!p+o_BG*xE+I!gpe`XG?5(dyW=JqaBYBt_QS;R!<> z?{hw=b5m^8=KEDXG4(0gsr9@K))9%Wa3d+yU+YP98% z2Ral7yEdXgbM0N~;^3ahh12xSl+e)C20CmAZJidu02hwf!X78mM-%xJ4oCg8C_O2B zO946W_0g}XK5T0*-Esfox2d72S>SfG+#X|UdiG|#fWbKCT0RU5Yj>mGBiGqb0Cj(l z+IlOPh>#^nIf{{OptSTXrD2_qJLBSV{?DI?;Tqv%0ngmhW%JJ3^9vb~%xhfjD5Q5g z4$cf$erCj5Fogg&j$VD1cNcIlxmSaY0p5)Q&0MG`Am8ddP~I#!-%Jm|gBA7S71>~8 z-!$^t1~#;C9f%^nZ>`pyogZ;w?&Jb@O7!&=!!7G`+kE zH0|lJYfVtNxOY5;S3ua_Xa~Gv6-B5arr@+{am35 zms~L*fEEY)r)#Z= z>}S=TjfQg)&WUY$j*OWF3d{eYp+s`Ud8HPwR&%M{DW9cbImr5H^X-OJSc`Snmvp_^ z+HiF?ZWrIZlZ;$f{a!(j`lKI8$#k+Fznj5FPms)%*rD!4^_QD%-8nUbDTkLfZtqe& z+%8Tx$19}I7lBzg+k_xXNJzkGpt;zcIB9N8y0W9)V|?s)urTU8ryS`i)+J4eZh70- z1fCL2=x&Q-v^Ra4i(idOFzfa^KV0DIZhluSLio6wra)zfbx4W?s~4qX)#KP?MH@ATUqM;WYv54?EJDPNz`W8 zBvD3C#p$5<_3HlNV42g};k%k6Hmwv*S)Wxie&D^1Go|$YVt)%$jI6ia)pimmyBl-u zle@kZ(e{#(1HYb&Bpo`u!P~yEjURXG2O}bw4W-70z`Ww&2>5OY9()I~2{>yeFq;Vq zqC&GM45g@}m5JJ!`FgDWgf7{<_u-9utlBZ=@Pl0!RL8|vP?-eK@0{#zH CCYEdf literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index 407d7a96be4d..906484c8a526 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -301,6 +301,22 @@ def test_alignment(): ax.set_yticks([]) +@image_comparison(baseline_images=['rotation_anchor.png'], style='mpl20', + remove_text=True) +def test_rotation_mode_anchor(): + fig, ax = plt.subplots() + + ax.plot([0, 1], lw=0) + ax.axvline(.5, linewidth=.5, color='.5') + ax.axhline(.5, linewidth=.5, color='.5') + + N = 4 + for r in range(N): + ax.text(.5, .5, 'pP', color=f'C{r}', size=100, + rotation=r/N*360, rotation_mode='anchor', + verticalalignment='center_baseline') + + @image_comparison(['axes_titles.png']) def test_axes_titles(): # Related to issue #3327 From 41cc933f33a87fe7f2b69fe0ffe9a82e60c1389b Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 12 Jun 2025 18:42:01 -0400 Subject: [PATCH 03/20] Remove fallback code for glyph indices Glyph indices are specific to each font. It does not make sense to fall back based on glyph index to another font. This could only really be populated by calling `FT2Font.set_text`, but even that was fragile. If a fallback font was used for a character with the same glyph index as a previous character in the main font, then these lookups could be overwritten to the fallback instead of the main font, with a completely different character! Fortunately, nothing actually uses or requires a fallback through glyph indices. --- src/ft2font.cpp | 65 +++++------------------------------------ src/ft2font.h | 8 ++--- src/ft2font_wrapper.cpp | 12 +++----- 3 files changed, 14 insertions(+), 71 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index bdfa2873ca80..9726e96233ab 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -253,7 +253,6 @@ void FT2Font::clear() } glyphs.clear(); - glyph_to_font.clear(); char_to_font.clear(); for (auto & fallback : fallbacks) { @@ -287,35 +286,13 @@ void FT2Font::select_charmap(unsigned long i) FT_CHECK(FT_Select_Charmap, face, (FT_Encoding)i); } -int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode, - bool fallback = false) -{ - if (fallback && glyph_to_font.find(left) != glyph_to_font.end() && - glyph_to_font.find(right) != glyph_to_font.end()) { - FT2Font *left_ft_object = glyph_to_font[left]; - FT2Font *right_ft_object = glyph_to_font[right]; - if (left_ft_object != right_ft_object) { - // we do not know how to do kerning between different fonts - return 0; - } - // if left_ft_object is the same as right_ft_object, - // do the exact same thing which set_text does. - return right_ft_object->get_kerning(left, right, mode, false); - } - else - { - FT_Vector delta; - return get_kerning(left, right, mode, delta); - } -} - -int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode, - FT_Vector &delta) +int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode) { if (!FT_HAS_KERNING(face)) { return 0; } + FT_Vector delta; if (!FT_Get_Kerning(face, left, right, mode, &delta)) { return (int)(delta.x) / (hinting_factor << kerning_factor); } else { @@ -364,7 +341,7 @@ void FT2Font::set_text( std::set glyph_seen_fonts; FT2Font *ft_object_with_glyph = this; bool was_found = load_char_with_fallback(ft_object_with_glyph, glyph_index, glyphs, - char_to_font, glyph_to_font, codepoint, flags, + char_to_font, codepoint, flags, charcode_error, glyph_error, glyph_seen_fonts, false); if (!was_found) { ft_glyph_warn((FT_ULong)codepoint, glyph_seen_fonts); @@ -372,8 +349,7 @@ void FT2Font::set_text( // come back to top-most font ft_object_with_glyph = this; char_to_font[codepoint] = ft_object_with_glyph; - glyph_to_font[glyph_index] = ft_object_with_glyph; - ft_object_with_glyph->load_glyph(glyph_index, flags, ft_object_with_glyph, false); + ft_object_with_glyph->load_glyph(glyph_index, flags); } else if (ft_object_with_glyph->warn_if_used) { ft_glyph_warn((FT_ULong)codepoint, glyph_seen_fonts); } @@ -383,8 +359,7 @@ void FT2Font::set_text( ft_object_with_glyph->has_kerning() && // if the font knows how to kern previous && glyph_index // and we really have 2 glyphs ) { - FT_Vector delta; - pen.x += ft_object_with_glyph->get_kerning(previous, glyph_index, FT_KERNING_DEFAULT, delta); + pen.x += ft_object_with_glyph->get_kerning(previous, glyph_index, FT_KERNING_DEFAULT); } // extract glyph image and store it in our table @@ -434,7 +409,7 @@ void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool FT_Error charcode_error, glyph_error; FT2Font *ft_object_with_glyph = this; bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, - glyphs, char_to_font, glyph_to_font, + glyphs, char_to_font, charcode, flags, charcode_error, glyph_error, glyph_seen_fonts, true); if (!was_found) { @@ -493,7 +468,6 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, FT_UInt &final_glyph_index, std::vector &parent_glyphs, std::unordered_map &parent_char_to_font, - std::unordered_map &parent_glyph_to_font, long charcode, FT_Int32 flags, FT_Error &charcode_error, @@ -523,7 +497,6 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, // need to store this for anytime a character is loaded from a parent // FT2Font object or to generate a mapping of individual characters to fonts ft_object_with_glyph = this; - parent_glyph_to_font[final_glyph_index] = this; parent_char_to_font[charcode] = this; parent_glyphs.push_back(thisGlyph); return true; @@ -532,7 +505,7 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, for (auto & fallback : fallbacks) { bool was_found = fallback->load_char_with_fallback( ft_object_with_glyph, final_glyph_index, parent_glyphs, - parent_char_to_font, parent_glyph_to_font, charcode, flags, + parent_char_to_font, charcode, flags, charcode_error, glyph_error, glyph_seen_fonts, override); if (was_found) { return true; @@ -542,21 +515,6 @@ bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, } } -void FT2Font::load_glyph(FT_UInt glyph_index, - FT_Int32 flags, - FT2Font *&ft_object, - bool fallback = false) -{ - // cache is only for parent FT2Font - if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { - ft_object = glyph_to_font[glyph_index]; - } else { - ft_object = this; - } - - ft_object->load_glyph(glyph_index, flags); -} - void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) { FT_CHECK(FT_Load_Glyph, face, glyph_index, flags); @@ -644,15 +602,8 @@ void FT2Font::draw_glyph_to_bitmap( draw_bitmap(im, &bitmap->bitmap, x + bitmap->left, y); } -void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer, - bool fallback = false) +void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer) { - if (fallback && glyph_to_font.find(glyph_number) != glyph_to_font.end()) { - // cache is only for parent FT2Font - FT2Font *ft_object = glyph_to_font[glyph_number]; - ft_object->get_glyph_name(glyph_number, buffer, false); - return; - } if (!FT_HAS_GLYPH_NAMES(face)) { /* Note that this generated name must match the name that is generated by ttconv in ttfont_CharStrings_getname. */ diff --git a/src/ft2font.h b/src/ft2font.h index 8db0239ed4fd..262ff395ac5d 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -108,22 +108,19 @@ class FT2Font void select_charmap(unsigned long i); void set_text(std::u32string_view codepoints, double angle, FT_Int32 flags, std::vector &xys); - int get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode, bool fallback); - int get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode, FT_Vector &delta); + int get_kerning(FT_UInt left, FT_UInt right, FT_Kerning_Mode mode); void set_kerning_factor(int factor); void load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback); bool load_char_with_fallback(FT2Font *&ft_object_with_glyph, FT_UInt &final_glyph_index, std::vector &parent_glyphs, std::unordered_map &parent_char_to_font, - std::unordered_map &parent_glyph_to_font, long charcode, FT_Int32 flags, FT_Error &charcode_error, FT_Error &glyph_error, std::set &glyph_seen_fonts, bool override); - void load_glyph(FT_UInt glyph_index, FT_Int32 flags, FT2Font *&ft_object, bool fallback); void load_glyph(FT_UInt glyph_index, FT_Int32 flags); void get_width_height(long *width, long *height); void get_bitmap_offset(long *x, long *y); @@ -132,7 +129,7 @@ class FT2Font void draw_glyph_to_bitmap( py::array_t im, int x, int y, size_t glyphInd, bool antialiased); - void get_glyph_name(unsigned int glyph_number, std::string &buffer, bool fallback); + void get_glyph_name(unsigned int glyph_number, std::string &buffer); long get_name_index(char *name); FT_UInt get_char_index(FT_ULong charcode, bool fallback); void get_path(std::vector &vertices, std::vector &codes); @@ -176,7 +173,6 @@ class FT2Font FT_Vector pen; /* untransformed origin */ std::vector glyphs; std::vector fallbacks; - std::unordered_map glyph_to_font; std::unordered_map char_to_font; FT_BBox bbox; FT_Pos advance; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index ca2db6aa0e5b..cb816efff9a9 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -618,7 +618,6 @@ static int PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right, std::variant mode_or_int) { - bool fallback = true; FT_Kerning_Mode mode; if (auto value = std::get_if(&mode_or_int)) { @@ -636,7 +635,7 @@ PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right, throw py::type_error("mode must be Kerning or int"); } - return self->x->get_kerning(left, right, mode, fallback); + return self->x->get_kerning(left, right, mode); } const char *PyFT2Font_get_fontmap__doc__ = R"""( @@ -834,8 +833,6 @@ static PyGlyph * PyFT2Font_load_glyph(PyFT2Font *self, FT_UInt glyph_index, std::variant flags_or_int = LoadFlags::FORCE_AUTOHINT) { - bool fallback = true; - FT2Font *ft_object = nullptr; LoadFlags flags; if (auto value = std::get_if(&flags_or_int)) { @@ -853,9 +850,9 @@ PyFT2Font_load_glyph(PyFT2Font *self, FT_UInt glyph_index, throw py::type_error("flags must be LoadFlags or int"); } - self->x->load_glyph(glyph_index, static_cast(flags), ft_object, fallback); + self->x->load_glyph(glyph_index, static_cast(flags)); - return PyGlyph_from_FT2Font(ft_object); + return PyGlyph_from_FT2Font(self->x); } const char *PyFT2Font_get_width_height__doc__ = R"""( @@ -1022,10 +1019,9 @@ static py::str PyFT2Font_get_glyph_name(PyFT2Font *self, unsigned int glyph_number) { std::string buffer; - bool fallback = true; buffer.resize(128); - self->x->get_glyph_name(glyph_number, buffer, fallback); + self->x->get_glyph_name(glyph_number, buffer); return buffer; } From 389373eca101a613ffb7f88271d5eb9c10712005 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Mon, 30 Jun 2025 17:44:09 -0400 Subject: [PATCH 04/20] ci: Preload existing test images from text-overhaul-figures branch This allows checking that there are no _new_ failures, without committing the new figures to the repo until the branch is complete. --- .appveyor.yml | 20 ++++++++++++++++++++ .github/workflows/tests.yml | 19 +++++++++++++++++++ azure-pipelines.yml | 19 +++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index c3fcb0ea9591..3e3a3b884d18 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -60,6 +60,26 @@ install: - micromamba env create -f environment.yml python=%PYTHON_VERSION% pywin32 - micromamba activate mpl-dev +before_test: + - git config --global user.name 'Matplotlib' + - git config --global user.email 'nobody@matplotlib.org' + - git fetch https://github.com/QuLogic/matplotlib.git text-overhaul-figures:text-overhaul-figures + - git merge --no-commit text-overhaul-figures || true + # If there are any conflicts in baseline images, then pick "ours", + # which should be the updated images in the PR. + - ps: | + $conflicts = git diff --name-only --diff-filter=U ` + lib/matplotlib/tests/baseline_images ` + lib/mpl_toolkits/*/tests/baseline_images + if ($conflicts) { + git checkout --ours -- $conflicts + git add -- $conflicts + } + git status + # If committing fails, there were conflicts other than the baseline images, + # which should not be allowed to happen, and should fail the build. + - git commit -m "Preload test images from branch text-overhaul-figures" + test_script: # Now build the thing.. - set LINK=/LIBPATH:%cd%\lib diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 85ace93445b6..53d47346c6eb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -95,6 +95,25 @@ jobs: fetch-depth: 0 persist-credentials: false + - name: Preload test images + run: | + git config --global user.name 'Matplotlib' + git config --global user.email 'nobody@matplotlib.org' + git fetch https://github.com/QuLogic/matplotlib.git text-overhaul-figures:text-overhaul-figures + git merge --no-commit text-overhaul-figures || true + # If there are any conflicts in baseline images, then pick "ours", + # which should be the updated images in the PR. + conflicts=$(git diff --name-only --diff-filter=U \ + lib/matplotlib/tests/baseline_images \ + lib/mpl_toolkits/*/tests/baseline_images) + if [ -n "${conflicts}" ]; then + git checkout --ours -- "${conflicts}" + git add -- "${conflicts}" + fi + # If committing fails, there were conflicts other than the baseline images, + # which should not be allowed to happen, and should fail the build. + git commit -m 'Preload test images from branch text-overhaul-figures' + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d68a9d36f0d3..a5a0e965e97b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -68,6 +68,25 @@ stages: architecture: 'x64' displayName: 'Use Python $(python.version)' + - bash: | + git config --global user.name 'Matplotlib' + git config --global user.email 'nobody@matplotlib.org' + git fetch https://github.com/QuLogic/matplotlib.git text-overhaul-figures:text-overhaul-figures + git merge --no-commit text-overhaul-figures || true + # If there are any conflicts in baseline images, then pick "ours", + # which should be the updated images in the PR. + conflicts=$(git diff --name-only --diff-filter=U \ + lib/matplotlib/tests/baseline_images \ + lib/mpl_toolkits/*/tests/baseline_images) + if [ -n "${conflicts}" ]; then + git checkout --ours -- "${conflicts}" + git add -- "${conflicts}" + fi + # If committing fails, there were conflicts other than the baseline images, + # which should not be allowed to happen, and should fail the build. + git commit -m 'Preload test images from branch text-overhaul-figures' + displayName: Preload test images + - bash: | choco install ninja displayName: 'Install dependencies' From a01860688d2d1a01c1f808983c15a170aa90f099 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 3 Jun 2025 00:32:10 -0400 Subject: [PATCH 05/20] Add typing to AFM parser Also, check some expected conditions at parse time instead of somewhere during use of the data. --- lib/matplotlib/_afm.py | 231 ++++++++++++++------------ lib/matplotlib/backends/backend_ps.py | 2 +- lib/matplotlib/tests/test_afm.py | 49 +++--- 3 files changed, 154 insertions(+), 128 deletions(-) diff --git a/lib/matplotlib/_afm.py b/lib/matplotlib/_afm.py index 9094206c2d7c..352d3c42247e 100644 --- a/lib/matplotlib/_afm.py +++ b/lib/matplotlib/_afm.py @@ -27,9 +27,10 @@ being used. """ -from collections import namedtuple +import inspect import logging import re +from typing import BinaryIO, NamedTuple, TypedDict from ._mathtext_data import uni2type1 @@ -37,7 +38,7 @@ _log = logging.getLogger(__name__) -def _to_int(x): +def _to_int(x: bytes | str) -> int: # Some AFM files have floats where we are expecting ints -- there is # probably a better way to handle this (support floats, round rather than # truncate). But I don't know what the best approach is now and this @@ -46,7 +47,7 @@ def _to_int(x): return int(float(x)) -def _to_float(x): +def _to_float(x: bytes | str) -> float: # Some AFM files use "," instead of "." as decimal separator -- this # shouldn't be ambiguous (unless someone is wicked enough to use "," as # thousands separator...). @@ -57,27 +58,56 @@ def _to_float(x): return float(x.replace(',', '.')) -def _to_str(x): +def _to_str(x: bytes) -> str: return x.decode('utf8') -def _to_list_of_ints(s): +def _to_list_of_ints(s: bytes) -> list[int]: s = s.replace(b',', b' ') return [_to_int(val) for val in s.split()] -def _to_list_of_floats(s): +def _to_list_of_floats(s: bytes | str) -> list[float]: return [_to_float(val) for val in s.split()] -def _to_bool(s): +def _to_bool(s: bytes) -> bool: if s.lower().strip() in (b'false', b'0', b'no'): return False else: return True -def _parse_header(fh): +class FontMetricsHeader(TypedDict, total=False): + StartFontMetrics: float + FontName: str + FullName: str + FamilyName: str + Weight: str + ItalicAngle: float + IsFixedPitch: bool + FontBBox: list[int] + UnderlinePosition: float + UnderlineThickness: float + Version: str + # Some AFM files have non-ASCII characters (which are not allowed by the spec). + # Given that there is actually no public API to even access this field, just return + # it as straight bytes. + Notice: bytes + EncodingScheme: str + CapHeight: float # Is the second version a mistake, or + Capheight: float # do some AFM files contain 'Capheight'? -JKS + XHeight: float + Ascender: float + Descender: float + StdHW: float + StdVW: float + StartCharMetrics: int + CharacterSet: str + Characters: int + + +def _parse_header(fh: BinaryIO) -> FontMetricsHeader: """ Read the font metrics header (up to the char metrics). @@ -98,34 +128,15 @@ def _parse_header(fh): * '-168 -218 1000 898' -> [-168, -218, 1000, 898] """ header_converters = { - b'StartFontMetrics': _to_float, - b'FontName': _to_str, - b'FullName': _to_str, - b'FamilyName': _to_str, - b'Weight': _to_str, - b'ItalicAngle': _to_float, - b'IsFixedPitch': _to_bool, - b'FontBBox': _to_list_of_ints, - b'UnderlinePosition': _to_float, - b'UnderlineThickness': _to_float, - b'Version': _to_str, - # Some AFM files have non-ASCII characters (which are not allowed by - # the spec). Given that there is actually no public API to even access - # this field, just return it as straight bytes. - b'Notice': lambda x: x, - b'EncodingScheme': _to_str, - b'CapHeight': _to_float, # Is the second version a mistake, or - b'Capheight': _to_float, # do some AFM files contain 'Capheight'? -JKS - b'XHeight': _to_float, - b'Ascender': _to_float, - b'Descender': _to_float, - b'StdHW': _to_float, - b'StdVW': _to_float, - b'StartCharMetrics': _to_int, - b'CharacterSet': _to_str, - b'Characters': _to_int, + bool: _to_bool, + bytes: lambda x: x, + float: _to_float, + int: _to_int, + list[int]: _to_list_of_ints, + str: _to_str, } - d = {} + header_value_types = inspect.get_annotations(FontMetricsHeader) + d: FontMetricsHeader = {} first_line = True for line in fh: line = line.rstrip() @@ -147,14 +158,16 @@ def _parse_header(fh): else: val = b'' try: - converter = header_converters[key] - except KeyError: + key_str = _to_str(key) + value_type = header_value_types[key_str] + except (KeyError, UnicodeDecodeError): _log.error("Found an unknown keyword in AFM header (was %r)", key) continue try: - d[key] = converter(val) + converter = header_converters[value_type] + d[key_str] = converter(val) # type: ignore[literal-required] except ValueError: - _log.error('Value error parsing header in AFM: %s, %s', key, val) + _log.error('Value error parsing header in AFM: %r, %r', key, val) continue if key == b'StartCharMetrics': break @@ -163,8 +176,8 @@ def _parse_header(fh): return d -CharMetrics = namedtuple('CharMetrics', 'width, name, bbox') -CharMetrics.__doc__ = """ +class CharMetrics(NamedTuple): + """ Represents the character metrics of a single character. Notes @@ -172,13 +185,20 @@ def _parse_header(fh): The fields do currently only describe a subset of character metrics information defined in the AFM standard. """ + + width: float + name: str + bbox: tuple[int, int, int, int] + + CharMetrics.width.__doc__ = """The character width (WX).""" CharMetrics.name.__doc__ = """The character name (N).""" CharMetrics.bbox.__doc__ = """ The bbox of the character (B) as a tuple (*llx*, *lly*, *urx*, *ury*).""" -def _parse_char_metrics(fh): +def _parse_char_metrics(fh: BinaryIO) -> tuple[dict[int, CharMetrics], + dict[str, CharMetrics]]: """ Parse the given filehandle for character metrics information. @@ -198,12 +218,12 @@ def _parse_char_metrics(fh): """ required_keys = {'C', 'WX', 'N', 'B'} - ascii_d = {} - name_d = {} - for line in fh: + ascii_d: dict[int, CharMetrics] = {} + name_d: dict[str, CharMetrics] = {} + for bline in fh: # We are defensively letting values be utf8. The spec requires # ascii, but there are non-compliant fonts in circulation - line = _to_str(line.rstrip()) # Convert from byte-literal + line = _to_str(bline.rstrip()) if line.startswith('EndCharMetrics'): return ascii_d, name_d # Split the metric line into a dictionary, keyed by metric identifiers @@ -214,8 +234,9 @@ def _parse_char_metrics(fh): num = _to_int(vals['C']) wx = _to_float(vals['WX']) name = vals['N'] - bbox = _to_list_of_floats(vals['B']) - bbox = list(map(int, bbox)) + bbox = tuple(map(int, _to_list_of_floats(vals['B']))) + if len(bbox) != 4: + raise RuntimeError(f'Bad parse: bbox has {len(bbox)} elements, should be 4') metrics = CharMetrics(wx, name, bbox) # Workaround: If the character name is 'Euro', give it the # corresponding character code, according to WinAnsiEncoding (see PDF @@ -230,7 +251,7 @@ def _parse_char_metrics(fh): raise RuntimeError('Bad parse') -def _parse_kern_pairs(fh): +def _parse_kern_pairs(fh: BinaryIO) -> dict[tuple[str, str], float]: """ Return a kern pairs dictionary. @@ -242,12 +263,11 @@ def _parse_kern_pairs(fh): d['A', 'y'] = -50 """ - line = next(fh) if not line.startswith(b'StartKernPairs'): - raise RuntimeError('Bad start of kern pairs data: %s' % line) + raise RuntimeError(f'Bad start of kern pairs data: {line!r}') - d = {} + d: dict[tuple[str, str], float] = {} for line in fh: line = line.rstrip() if not line: @@ -257,21 +277,26 @@ def _parse_kern_pairs(fh): return d vals = line.split() if len(vals) != 4 or vals[0] != b'KPX': - raise RuntimeError('Bad kern pairs line: %s' % line) + raise RuntimeError(f'Bad kern pairs line: {line!r}') c1, c2, val = _to_str(vals[1]), _to_str(vals[2]), _to_float(vals[3]) d[(c1, c2)] = val raise RuntimeError('Bad kern pairs parse') -CompositePart = namedtuple('CompositePart', 'name, dx, dy') -CompositePart.__doc__ = """ - Represents the information on a composite element of a composite char.""" +class CompositePart(NamedTuple): + """Represents the information on a composite element of a composite char.""" + + name: bytes + dx: float + dy: float + + CompositePart.name.__doc__ = """Name of the part, e.g. 'acute'.""" CompositePart.dx.__doc__ = """x-displacement of the part from the origin.""" CompositePart.dy.__doc__ = """y-displacement of the part from the origin.""" -def _parse_composites(fh): +def _parse_composites(fh: BinaryIO) -> dict[bytes, list[CompositePart]]: """ Parse the given filehandle for composites information. @@ -292,11 +317,11 @@ def _parse_composites(fh): will be represented as:: - composites['Aacute'] = [CompositePart(name='A', dx=0, dy=0), - CompositePart(name='acute', dx=160, dy=170)] + composites[b'Aacute'] = [CompositePart(name=b'A', dx=0, dy=0), + CompositePart(name=b'acute', dx=160, dy=170)] """ - composites = {} + composites: dict[bytes, list[CompositePart]] = {} for line in fh: line = line.rstrip() if not line: @@ -306,6 +331,9 @@ def _parse_composites(fh): vals = line.split(b';') cc = vals[0].split() name, _num_parts = cc[1], _to_int(cc[2]) + if len(vals) != _num_parts + 2: # First element is 'CC', last is empty. + raise RuntimeError(f'Bad composites parse: expected {_num_parts} parts, ' + f'but got {len(vals) - 2}') pccParts = [] for s in vals[1:-1]: pcc = s.split() @@ -316,7 +344,8 @@ def _parse_composites(fh): raise RuntimeError('Bad composites parse') -def _parse_optional(fh): +def _parse_optional(fh: BinaryIO) -> tuple[dict[tuple[str, str], float], + dict[bytes, list[CompositePart]]]: """ Parse the optional fields for kern pair data and composites. @@ -329,44 +358,38 @@ def _parse_optional(fh): A dict containing composite information. May be empty. See `._parse_composites`. """ - optional = { - b'StartKernData': _parse_kern_pairs, - b'StartComposites': _parse_composites, - } - - d = {b'StartKernData': {}, - b'StartComposites': {}} + kern_data: dict[tuple[str, str], float] = {} + composites: dict[bytes, list[CompositePart]] = {} for line in fh: line = line.rstrip() if not line: continue - key = line.split()[0] - - if key in optional: - d[key] = optional[key](fh) + match line.split()[0]: + case b'StartKernData': + kern_data = _parse_kern_pairs(fh) + case b'StartComposites': + composites = _parse_composites(fh) - return d[b'StartKernData'], d[b'StartComposites'] + return kern_data, composites class AFM: - def __init__(self, fh): + def __init__(self, fh: BinaryIO): """Parse the AFM file in file object *fh*.""" self._header = _parse_header(fh) self._metrics, self._metrics_by_name = _parse_char_metrics(fh) self._kern, self._composite = _parse_optional(fh) - def get_str_bbox_and_descent(self, s): + def get_str_bbox_and_descent(self, s: str) -> tuple[int, int, float, int, int]: """Return the string bounding box and the maximal descent.""" if not len(s): return 0, 0, 0, 0, 0 - total_width = 0 - namelast = None - miny = 1e9 + total_width = 0.0 + namelast = '' + miny = 1_000_000_000 maxy = 0 left = 0 - if not isinstance(s, str): - s = _to_str(s) for c in s: if c == '\n': continue @@ -386,11 +409,11 @@ def get_str_bbox_and_descent(self, s): return left, miny, total_width, maxy - miny, -miny - def get_glyph_name(self, glyph_ind): # For consistency with FT2Font. + def get_glyph_name(self, glyph_ind: int) -> str: # For consistency with FT2Font. """Get the name of the glyph, i.e., ord(';') is 'semicolon'.""" return self._metrics[glyph_ind].name - def get_char_index(self, c): # For consistency with FT2Font. + def get_char_index(self, c: int) -> int: # For consistency with FT2Font. """ Return the glyph index corresponding to a character code point. @@ -398,38 +421,38 @@ def get_char_index(self, c): # For consistency with FT2Font. """ return c - def get_width_char(self, c): + def get_width_char(self, c: int) -> float: """Get the width of the character code from the character metric WX field.""" return self._metrics[c].width - def get_width_from_char_name(self, name): + def get_width_from_char_name(self, name: str) -> float: """Get the width of the character from a type1 character name.""" return self._metrics_by_name[name].width - def get_kern_dist_from_name(self, name1, name2): + def get_kern_dist_from_name(self, name1: str, name2: str) -> float: """ Return the kerning pair distance (possibly 0) for chars *name1* and *name2*. """ return self._kern.get((name1, name2), 0) - def get_fontname(self): + def get_fontname(self) -> str: """Return the font name, e.g., 'Times-Roman'.""" - return self._header[b'FontName'] + return self._header['FontName'] @property - def postscript_name(self): # For consistency with FT2Font. + def postscript_name(self) -> str: # For consistency with FT2Font. return self.get_fontname() - def get_fullname(self): + def get_fullname(self) -> str: """Return the font full name, e.g., 'Times-Roman'.""" - name = self._header.get(b'FullName') + name = self._header.get('FullName') if name is None: # use FontName as a substitute - name = self._header[b'FontName'] + name = self._header['FontName'] return name - def get_familyname(self): + def get_familyname(self) -> str: """Return the font family name, e.g., 'Times'.""" - name = self._header.get(b'FamilyName') + name = self._header.get('FamilyName') if name is not None: return name @@ -440,26 +463,26 @@ def get_familyname(self): return re.sub(extras, '', name) @property - def family_name(self): # For consistency with FT2Font. + def family_name(self) -> str: # For consistency with FT2Font. """The font family name, e.g., 'Times'.""" return self.get_familyname() - def get_weight(self): + def get_weight(self) -> str: """Return the font weight, e.g., 'Bold' or 'Roman'.""" - return self._header[b'Weight'] + return self._header['Weight'] - def get_angle(self): + def get_angle(self) -> float: """Return the fontangle as float.""" - return self._header[b'ItalicAngle'] + return self._header['ItalicAngle'] - def get_capheight(self): + def get_capheight(self) -> float: """Return the cap height as float.""" - return self._header[b'CapHeight'] + return self._header['CapHeight'] - def get_xheight(self): + def get_xheight(self) -> float: """Return the xheight as float.""" - return self._header[b'XHeight'] + return self._header['XHeight'] - def get_underline_thickness(self): + def get_underline_thickness(self) -> float: """Return the underline thickness as float.""" - return self._header[b'UnderlineThickness'] + return self._header['UnderlineThickness'] diff --git a/lib/matplotlib/backends/backend_ps.py b/lib/matplotlib/backends/backend_ps.py index ea5868387918..368564a1518d 100644 --- a/lib/matplotlib/backends/backend_ps.py +++ b/lib/matplotlib/backends/backend_ps.py @@ -779,7 +779,7 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): .decode("ascii")) scale = 0.001 * prop.get_size_in_points() thisx = 0 - last_name = None # kerns returns 0 for None. + last_name = '' # kerns returns 0 for ''. for c in s: name = uni2type1.get(ord(c), f"uni{ord(c):04X}") try: diff --git a/lib/matplotlib/tests/test_afm.py b/lib/matplotlib/tests/test_afm.py index 80cf8ac60feb..bc1d587baf6b 100644 --- a/lib/matplotlib/tests/test_afm.py +++ b/lib/matplotlib/tests/test_afm.py @@ -47,20 +47,20 @@ def test_parse_header(): fh = BytesIO(AFM_TEST_DATA) header = _afm._parse_header(fh) assert header == { - b'StartFontMetrics': 2.0, - b'FontName': 'MyFont-Bold', - b'EncodingScheme': 'FontSpecific', - b'FullName': 'My Font Bold', - b'FamilyName': 'Test Fonts', - b'Weight': 'Bold', - b'ItalicAngle': 0.0, - b'IsFixedPitch': False, - b'UnderlinePosition': -100, - b'UnderlineThickness': 56.789, - b'Version': '001.000', - b'Notice': b'Copyright \xa9 2017 No one.', - b'FontBBox': [0, -321, 1234, 369], - b'StartCharMetrics': 3, + 'StartFontMetrics': 2.0, + 'FontName': 'MyFont-Bold', + 'EncodingScheme': 'FontSpecific', + 'FullName': 'My Font Bold', + 'FamilyName': 'Test Fonts', + 'Weight': 'Bold', + 'ItalicAngle': 0.0, + 'IsFixedPitch': False, + 'UnderlinePosition': -100, + 'UnderlineThickness': 56.789, + 'Version': '001.000', + 'Notice': b'Copyright \xa9 2017 No one.', + 'FontBBox': [0, -321, 1234, 369], + 'StartCharMetrics': 3, } @@ -69,20 +69,23 @@ def test_parse_char_metrics(): _afm._parse_header(fh) # position metrics = _afm._parse_char_metrics(fh) assert metrics == ( - {0: (250.0, 'space', [0, 0, 0, 0]), - 42: (1141.0, 'foo', [40, 60, 800, 360]), - 99: (583.0, 'bar', [40, -10, 543, 210]), - }, - {'space': (250.0, 'space', [0, 0, 0, 0]), - 'foo': (1141.0, 'foo', [40, 60, 800, 360]), - 'bar': (583.0, 'bar', [40, -10, 543, 210]), - }) + { + 0: _afm.CharMetrics(250.0, 'space', (0, 0, 0, 0)), + 42: _afm.CharMetrics(1141.0, 'foo', (40, 60, 800, 360)), + 99: _afm.CharMetrics(583.0, 'bar', (40, -10, 543, 210)), + }, + { + 'space': _afm.CharMetrics(250.0, 'space', (0, 0, 0, 0)), + 'foo': _afm.CharMetrics(1141.0, 'foo', (40, 60, 800, 360)), + 'bar': _afm.CharMetrics(583.0, 'bar', (40, -10, 543, 210)), + } + ) def test_get_familyname_guessed(): fh = BytesIO(AFM_TEST_DATA) font = _afm.AFM(fh) - del font._header[b'FamilyName'] # remove FamilyName, so we have to guess + del font._header['FamilyName'] # remove FamilyName, so we have to guess assert font.get_familyname() == 'My Font' From aff20cf00c41e61ee7b72fdd803a8b389530e076 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 8 Jul 2025 03:02:36 -0400 Subject: [PATCH 06/20] ci: Fix image preload with multiple conflicts --- .github/workflows/tests.yml | 4 ++-- azure-pipelines.yml | 4 ++-- lib/matplotlib/mlab.py | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 53d47346c6eb..7c27ec84f86a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -107,8 +107,8 @@ jobs: lib/matplotlib/tests/baseline_images \ lib/mpl_toolkits/*/tests/baseline_images) if [ -n "${conflicts}" ]; then - git checkout --ours -- "${conflicts}" - git add -- "${conflicts}" + git checkout --ours -- ${conflicts} + git add -- ${conflicts} fi # If committing fails, there were conflicts other than the baseline images, # which should not be allowed to happen, and should fail the build. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a5a0e965e97b..eef71162f9cb 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -79,8 +79,8 @@ stages: lib/matplotlib/tests/baseline_images \ lib/mpl_toolkits/*/tests/baseline_images) if [ -n "${conflicts}" ]; then - git checkout --ours -- "${conflicts}" - git add -- "${conflicts}" + git checkout --ours -- ${conflicts} + git add -- ${conflicts} fi # If committing fails, there were conflicts other than the baseline images, # which should not be allowed to happen, and should fail the build. diff --git a/lib/matplotlib/mlab.py b/lib/matplotlib/mlab.py index b4b4c3f96828..de890935c23b 100644 --- a/lib/matplotlib/mlab.py +++ b/lib/matplotlib/mlab.py @@ -219,9 +219,6 @@ def _stride_windows(x, n, noverlap=0): raise ValueError(f'n ({n}) and noverlap ({noverlap}) must be positive integers ' f'with n < noverlap and n <= x.size ({x.size})') - if n == 1 and noverlap == 0: - return x[np.newaxis] - step = n - noverlap shape = (n, (x.shape[-1]-noverlap)//step) strides = (x.strides[0], step*x.strides[0]) From 7b4d725306b2d5907a023c14a30474a04280f804 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 28 Mar 2025 00:09:04 -0400 Subject: [PATCH 07/20] Remove kerning_factor from tests --- .../mpl-data/stylelib/_classic_test_patch.mplstyle | 2 -- lib/matplotlib/tests/test_artist.py | 3 --- lib/matplotlib/tests/test_legend.py | 3 --- lib/matplotlib/tests/test_text.py | 5 ----- lib/mpl_toolkits/axisartist/tests/test_axis_artist.py | 9 --------- lib/mpl_toolkits/axisartist/tests/test_axislines.py | 6 ------ lib/mpl_toolkits/axisartist/tests/test_floating_axes.py | 3 --- .../axisartist/tests/test_grid_helper_curvelinear.py | 5 +---- 8 files changed, 1 insertion(+), 35 deletions(-) diff --git a/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle b/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle index abd972925871..478ff5e415f9 100644 --- a/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle @@ -1,8 +1,6 @@ # This patch should go on top of the "classic" style and exists solely to avoid # changing baseline images. -text.kerning_factor : 6 - ytick.alignment: center_baseline hatch.color: edge diff --git a/lib/matplotlib/tests/test_artist.py b/lib/matplotlib/tests/test_artist.py index 1367701ffe3e..d891609d4eb3 100644 --- a/lib/matplotlib/tests/test_artist.py +++ b/lib/matplotlib/tests/test_artist.py @@ -217,9 +217,6 @@ def test_remove(): @image_comparison(["default_edges.png"], remove_text=True, style='default') def test_default_edges(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, [[ax1, ax2], [ax3, ax4]] = plt.subplots(2, 2) ax1.plot(np.arange(10), np.arange(10), 'x', diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 9b100037cc41..7ef31bf64a53 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -258,9 +258,6 @@ def test_legend_expand(): @image_comparison(['hatching'], remove_text=True, style='default') def test_hatching(): # Remove legend texts when this image is regenerated. - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() # Patches diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index 7e1a50df8a2f..911de1d4a59a 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -139,9 +139,6 @@ def test_multiline(): @image_comparison(['multiline2'], style='mpl20') def test_multiline2(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() ax.set_xlim(0, 1.4) @@ -694,8 +691,6 @@ def test_annotation_units(fig_test, fig_ref): @image_comparison(['large_subscript_title.png'], style='mpl20') def test_large_subscript_title(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 plt.rcParams['axes.titley'] = None fig, axs = plt.subplots(1, 2, figsize=(9, 2.5), constrained_layout=True) diff --git a/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py b/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py index d44a61b6dd4a..7caf4fc21683 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py +++ b/lib/mpl_toolkits/axisartist/tests/test_axis_artist.py @@ -26,9 +26,6 @@ def test_ticks(): @image_comparison(['axis_artist_labelbase.png'], style='default') def test_labelbase(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() ax.plot([0.5], [0.5], "o") @@ -43,9 +40,6 @@ def test_labelbase(): @image_comparison(['axis_artist_ticklabels.png'], style='default') def test_ticklabels(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() ax.xaxis.set_visible(False) @@ -78,9 +72,6 @@ def test_ticklabels(): @image_comparison(['axis_artist.png'], style='default') def test_axis_artist(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() ax.xaxis.set_visible(False) diff --git a/lib/mpl_toolkits/axisartist/tests/test_axislines.py b/lib/mpl_toolkits/axisartist/tests/test_axislines.py index a1485d4f436b..c371d6453932 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_axislines.py +++ b/lib/mpl_toolkits/axisartist/tests/test_axislines.py @@ -9,9 +9,6 @@ @image_comparison(['SubplotZero.png'], style='default') def test_SubplotZero(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure() ax = SubplotZero(fig, 1, 1, 1) @@ -30,9 +27,6 @@ def test_SubplotZero(): @image_comparison(['Subplot.png'], style='default') def test_Subplot(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure() ax = Subplot(fig, 1, 1, 1) diff --git a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py index feb667af013e..3dd4309d199e 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py +++ b/lib/mpl_toolkits/axisartist/tests/test_floating_axes.py @@ -69,9 +69,6 @@ def test_curvelinear3(): # remove when image is regenerated. @image_comparison(['curvelinear4.png'], style='default', tol=0.9) def test_curvelinear4(): - # Remove this line when this test image is regenerated. - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure(figsize=(5, 5)) tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) + diff --git a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py index 7d6554782fe6..f58a42471680 100644 --- a/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py +++ b/lib/mpl_toolkits/axisartist/tests/test_grid_helper_curvelinear.py @@ -135,11 +135,8 @@ def test_polar_box(): ax1.grid(True) -# Remove tol & kerning_factor when this test image is regenerated. -@image_comparison(['axis_direction.png'], style='default', tol=0.13) +@image_comparison(['axis_direction.png'], style='default', tol=0.04) def test_axis_direction(): - plt.rcParams['text.kerning_factor'] = 6 - fig = plt.figure(figsize=(5, 5)) # PolarAxes.PolarTransform takes radian. However, we want our coordinate From 8255ae206b273524657a4c81c8c06162a31a27e0 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 9 Apr 2025 05:03:23 -0400 Subject: [PATCH 08/20] Set text hinting to defaults Namely, `text.hinting` is now `default` instead of `force_autohint` (or `none` for classic tests) and `text.hinting_factor` is now 1, not 8. --- lib/matplotlib/mpl-data/matplotlibrc | 4 ++-- .../mpl-data/stylelib/_classic_test_patch.mplstyle | 3 +++ lib/matplotlib/testing/__init__.py | 11 +++++++++-- lib/matplotlib/tests/test_axes.py | 6 +++--- lib/matplotlib/tests/test_figure.py | 2 +- lib/matplotlib/tests/test_legend.py | 8 ++++---- lib/matplotlib/tests/test_polar.py | 2 +- lib/matplotlib/tests/test_text.py | 6 +++--- 8 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index ec649560ba3b..e1f66cce0c36 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -301,9 +301,9 @@ ## ("native" is a synonym.) ## - force_autohint: Use FreeType's auto-hinter. ("auto" is a synonym.) ## - no_hinting: Disable hinting. ("none" is a synonym.) -#text.hinting: force_autohint +#text.hinting: default -#text.hinting_factor: 8 # Specifies the amount of softness for hinting in the +#text.hinting_factor: 1 # Specifies the amount of softness for hinting in the # horizontal direction. A value of 1 will hint to full # pixels. A value of 2 will hint to half pixels etc. #text.kerning_factor: 0 # Specifies the scaling factor for kerning values. This diff --git a/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle b/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle index 478ff5e415f9..3dc92f832b20 100644 --- a/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle +++ b/lib/matplotlib/mpl-data/stylelib/_classic_test_patch.mplstyle @@ -4,3 +4,6 @@ ytick.alignment: center_baseline hatch.color: edge + +text.hinting: default +text.hinting_factor: 1 diff --git a/lib/matplotlib/testing/__init__.py b/lib/matplotlib/testing/__init__.py index d6affb1b039f..453cc631c0ea 100644 --- a/lib/matplotlib/testing/__init__.py +++ b/lib/matplotlib/testing/__init__.py @@ -19,8 +19,15 @@ def set_font_settings_for_testing(): mpl.rcParams['font.family'] = 'DejaVu Sans' - mpl.rcParams['text.hinting'] = 'none' - mpl.rcParams['text.hinting_factor'] = 8 + # We've changed the default for ourselves here, but for backwards-compatibility, use + # the old setting if not called in our own tests (which would set + # `_called_from_pytest` from our `conftest.py`). + if getattr(mpl, '_called_from_pytest', False): + mpl.rcParams['text.hinting'] = 'default' + mpl.rcParams['text.hinting_factor'] = 1 + else: + mpl.rcParams['text.hinting'] = 'none' + mpl.rcParams['text.hinting_factor'] = 8 def set_reproducibility_for_testing(): diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index c96173e340f7..60d507e2b999 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -8272,8 +8272,8 @@ def test_normal_axes(): # test the axis bboxes target = [ - [123.375, 75.88888888888886, 983.25, 33.0], - [85.51388888888889, 99.99999999999997, 53.375, 993.0] + [124.0, 76.89, 982.0, 32.0], + [86.89, 100.5, 52.0, 992.0], ] for nn, b in enumerate(bbaxis): targetbb = mtransforms.Bbox.from_bounds(*target[nn]) @@ -8293,7 +8293,7 @@ def test_normal_axes(): targetbb = mtransforms.Bbox.from_bounds(*target) assert_array_almost_equal(bbax.bounds, targetbb.bounds, decimal=2) - target = [85.5138, 75.88888, 1021.11, 1017.11] + target = [86.89, 76.89, 1019.11, 1015.61] targetbb = mtransforms.Bbox.from_bounds(*target) assert_array_almost_equal(bbtb.bounds, targetbb.bounds, decimal=2) diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index c5890a2963b3..cad77e2d00d7 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -814,7 +814,7 @@ def test_tightbbox(): ax.set_xlim(0, 1) t = ax.text(1., 0.5, 'This dangles over end') renderer = fig.canvas.get_renderer() - x1Nom0 = 9.035 # inches + x1Nom0 = 8.9375 # inches assert abs(t.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2 assert abs(ax.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2 assert abs(fig.get_tightbbox(renderer).x1 - x1Nom0) < 0.05 diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index 7ef31bf64a53..9c43217cabb5 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -481,10 +481,10 @@ def test_figure_legend_outside(): todos += ['left ' + pos for pos in ['lower', 'center', 'upper']] todos += ['right ' + pos for pos in ['lower', 'center', 'upper']] - upperext = [20.347556, 27.722556, 790.583, 545.499] - lowerext = [20.347556, 71.056556, 790.583, 588.833] - leftext = [151.681556, 27.722556, 790.583, 588.833] - rightext = [20.347556, 27.722556, 659.249, 588.833] + upperext = [20.722556, 26.722556, 790.333, 545.999] + lowerext = [20.722556, 70.056556, 790.333, 589.333] + leftext = [152.056556, 26.722556, 790.333, 589.333] + rightext = [20.722556, 26.722556, 658.999, 589.333] axbb = [upperext, upperext, upperext, lowerext, lowerext, lowerext, leftext, leftext, leftext, diff --git a/lib/matplotlib/tests/test_polar.py b/lib/matplotlib/tests/test_polar.py index c0bf72b89eb0..83368f819242 100644 --- a/lib/matplotlib/tests/test_polar.py +++ b/lib/matplotlib/tests/test_polar.py @@ -328,7 +328,7 @@ def test_get_tightbbox_polar(): fig.canvas.draw() bb = ax.get_tightbbox(fig.canvas.get_renderer()) assert_allclose( - bb.extents, [107.7778, 29.2778, 539.7847, 450.7222], rtol=1e-03) + bb.extents, [108.27778, 28.7778, 539.7222, 451.2222], rtol=1e-03) @check_figures_equal() diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index 911de1d4a59a..9b894a650bcf 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -720,14 +720,14 @@ def test_wrap(x, rotation, halign): s = 'This is a very long text that should be wrapped multiple times.' text = subfig.text(x, 0.7, s, wrap=True, rotation=rotation, ha=halign) fig.canvas.draw() - assert text._get_wrapped_text() == ('This is a very long\n' - 'text that should be\n' + assert text._get_wrapped_text() == ('This is a very long text\n' + 'that should be\n' 'wrapped multiple\n' 'times.') def test_mathwrap(): - fig = plt.figure(figsize=(6, 4)) + fig = plt.figure(figsize=(5, 4)) s = r'This is a very $\overline{\mathrm{long}}$ line of Mathtext.' text = fig.text(0, 0.5, s, size=40, wrap=True) fig.canvas.draw() From 89c054dc80e61425f0e07f192a62801cf6a22cc1 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 27 Mar 2025 04:32:15 -0400 Subject: [PATCH 09/20] Update FreeType to 2.13.3 --- .pre-commit-config.yaml | 4 +- extern/meson.build | 13 +- lib/matplotlib/__init__.py | 2 +- lib/matplotlib/tests/test_bbox_tight.py | 2 +- lib/matplotlib/tests/test_figure.py | 5 +- lib/matplotlib/tests/test_ft2font.py | 6 +- lib/matplotlib/tests/test_mathtext.py | 16 +- lib/matplotlib/tests/test_text.py | 4 +- subprojects/freetype-2.13.3.wrap | 13 + subprojects/freetype-2.6.1.wrap | 10 - .../freetype-2.6.1-meson/LICENSE.build | 19 - .../builds/unix/ftconfig.h.in | 498 ---------- .../include/freetype/config/ftoption.h.in | 886 ------------------ .../freetype-2.6.1-meson/meson.build | 193 ---- .../freetype-2.6.1-meson/src/gzip/zconf.h | 284 ------ ...d655f1696da774b5cdd4c5effb312153232f.patch | 36 + 16 files changed, 80 insertions(+), 1911 deletions(-) create mode 100644 subprojects/freetype-2.13.3.wrap delete mode 100644 subprojects/freetype-2.6.1.wrap delete mode 100644 subprojects/packagefiles/freetype-2.6.1-meson/LICENSE.build delete mode 100644 subprojects/packagefiles/freetype-2.6.1-meson/builds/unix/ftconfig.h.in delete mode 100644 subprojects/packagefiles/freetype-2.6.1-meson/include/freetype/config/ftoption.h.in delete mode 100644 subprojects/packagefiles/freetype-2.6.1-meson/meson.build delete mode 100644 subprojects/packagefiles/freetype-2.6.1-meson/src/gzip/zconf.h create mode 100644 subprojects/packagefiles/freetype-34aed655f1696da774b5cdd4c5effb312153232f.patch diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 86a9a0f45440..595d69f65b4a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,13 +20,13 @@ repos: - id: check-docstring-first exclude: lib/matplotlib/typing.py # docstring used for attribute flagged by check - id: end-of-file-fixer - exclude_types: [svg] + exclude_types: [diff, svg] - id: mixed-line-ending - id: name-tests-test args: ["--pytest-test-first"] - id: no-commit-to-branch # Default is master and main. - id: trailing-whitespace - exclude_types: [svg] + exclude_types: [diff, svg] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.15.0 hooks: diff --git a/extern/meson.build b/extern/meson.build index 5463183a9099..7f7c2511c3d5 100644 --- a/extern/meson.build +++ b/extern/meson.build @@ -13,11 +13,20 @@ else # must match the value in `lib/matplotlib.__init__.py`. Also update the docs # in `docs/devel/dependencies.rst`. Bump the cache key in # `.circleci/config.yml` when changing requirements. - LOCAL_FREETYPE_VERSION = '2.6.1' + LOCAL_FREETYPE_VERSION = '2.13.3' freetype_proj = subproject( f'freetype-@LOCAL_FREETYPE_VERSION@', - default_options: ['default_library=static']) + default_options: [ + 'default_library=static', + 'brotli=disabled', + 'bzip2=disabled', + 'harfbuzz=disabled', + 'mmap=auto', + 'png=disabled', + 'tests=disabled', + 'zlib=internal', + ]) freetype_dep = freetype_proj.get_variable('freetype_dep') endif diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index e98e8ea07502..008d4de77a3b 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -1329,7 +1329,7 @@ def _val_or_rc(val, *rc_names): def _init_tests(): # The version of FreeType to install locally for running the tests. This must match # the value in `meson.build`. - LOCAL_FREETYPE_VERSION = '2.6.1' + LOCAL_FREETYPE_VERSION = '2.13.3' from matplotlib import ft2font if (ft2font.__freetype_version__ != LOCAL_FREETYPE_VERSION or diff --git a/lib/matplotlib/tests/test_bbox_tight.py b/lib/matplotlib/tests/test_bbox_tight.py index 431ca70bf7ea..2ae94abcd7b2 100644 --- a/lib/matplotlib/tests/test_bbox_tight.py +++ b/lib/matplotlib/tests/test_bbox_tight.py @@ -47,7 +47,7 @@ def test_bbox_inches_tight(text_placeholders): @image_comparison(['bbox_inches_tight_suptile_legend'], savefig_kwarg={'bbox_inches': 'tight'}, - tol=0 if platform.machine() == 'x86_64' else 0.02) + tol=0 if platform.machine() == 'x86_64' else 0.022) def test_bbox_inches_tight_suptile_legend(): plt.plot(np.arange(10), label='a straight line') plt.legend(bbox_to_anchor=(0.9, 1), loc='upper left') diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index cad77e2d00d7..900e184c6741 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -814,7 +814,7 @@ def test_tightbbox(): ax.set_xlim(0, 1) t = ax.text(1., 0.5, 'This dangles over end') renderer = fig.canvas.get_renderer() - x1Nom0 = 8.9375 # inches + x1Nom0 = 8.9875 # inches assert abs(t.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2 assert abs(ax.get_tightbbox(renderer).x1 - x1Nom0 * fig.dpi) < 2 assert abs(fig.get_tightbbox(renderer).x1 - x1Nom0) < 0.05 @@ -1376,7 +1376,8 @@ def test_subfigure_dpi(): @image_comparison(['test_subfigure_ss.png'], style='mpl20', - savefig_kwarg={'facecolor': 'teal'}, tol=0.02) + savefig_kwarg={'facecolor': 'teal'}, + tol=0.022) def test_subfigure_ss(): # test assigning the subfigure via subplotspec np.random.seed(19680801) diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index 8b448e17b7fd..0dc0667d0e84 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -708,10 +708,10 @@ def test_ft2font_get_sfnt_table(font_name, header): @pytest.mark.parametrize('left, right, unscaled, unfitted, default', [ # These are all the same class. - ('A', 'A', 57, 248, 256), ('A', 'À', 57, 248, 256), ('A', 'Á', 57, 248, 256), - ('A', 'Â', 57, 248, 256), ('A', 'Ã', 57, 248, 256), ('A', 'Ä', 57, 248, 256), + ('A', 'A', 57, 247, 256), ('A', 'À', 57, 247, 256), ('A', 'Á', 57, 247, 256), + ('A', 'Â', 57, 247, 256), ('A', 'Ã', 57, 247, 256), ('A', 'Ä', 57, 247, 256), # And a few other random ones. - ('D', 'A', -36, -156, -128), ('T', '.', -243, -1056, -1024), + ('D', 'A', -36, -156, -128), ('T', '.', -243, -1055, -1024), ('X', 'C', -149, -647, -640), ('-', 'J', 114, 495, 512), ]) def test_ft2font_get_kerning(left, right, unscaled, unfitted, default): diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index 39c28dc9228c..4fc04a627dd5 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -568,14 +568,14 @@ def test_box_repr(): _mathtext.DejaVuSansFonts(fm.FontProperties(), LoadFlags.NO_HINTING), fontsize=12, dpi=100)) assert s == textwrap.dedent("""\ - Hlist[ + Hlist[ Hlist[], - Hlist[ - Hlist[ - Vlist[ - HCentered[ + Hlist[ + Hlist[ + Vlist[ + HCentered[ Glue, - Hlist[ + Hlist[ `1`, k2.36, ], @@ -584,9 +584,9 @@ def test_box_repr(): Vbox, Hrule, Vbox, - HCentered[ + HCentered[ Glue, - Hlist[ + Hlist[ `2`, k2.02, ], diff --git a/lib/matplotlib/tests/test_text.py b/lib/matplotlib/tests/test_text.py index 9b894a650bcf..9d943fa9df13 100644 --- a/lib/matplotlib/tests/test_text.py +++ b/lib/matplotlib/tests/test_text.py @@ -720,8 +720,8 @@ def test_wrap(x, rotation, halign): s = 'This is a very long text that should be wrapped multiple times.' text = subfig.text(x, 0.7, s, wrap=True, rotation=rotation, ha=halign) fig.canvas.draw() - assert text._get_wrapped_text() == ('This is a very long text\n' - 'that should be\n' + assert text._get_wrapped_text() == ('This is a very long\n' + 'text that should be\n' 'wrapped multiple\n' 'times.') diff --git a/subprojects/freetype-2.13.3.wrap b/subprojects/freetype-2.13.3.wrap new file mode 100644 index 000000000000..68f688a35861 --- /dev/null +++ b/subprojects/freetype-2.13.3.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = freetype-2.13.3 +source_url = https://download.savannah.gnu.org/releases/freetype/freetype-2.13.3.tar.xz +source_fallback_url = https://downloads.sourceforge.net/project/freetype/freetype2/2.13.3/freetype-2.13.3.tar.xz +source_filename = freetype-2.13.3.tar.xz +source_hash = 0550350666d427c74daeb85d5ac7bb353acba5f76956395995311a9c6f063289 + +# https://gitlab.freedesktop.org/freetype/freetype/-/commit/34aed655f1696da774b5cdd4c5effb312153232f +diff_files = freetype-34aed655f1696da774b5cdd4c5effb312153232f.patch + +[provide] +freetype2 = freetype_dep +freetype = freetype_dep diff --git a/subprojects/freetype-2.6.1.wrap b/subprojects/freetype-2.6.1.wrap deleted file mode 100644 index 763362b84df0..000000000000 --- a/subprojects/freetype-2.6.1.wrap +++ /dev/null @@ -1,10 +0,0 @@ -[wrap-file] -source_url = https://download.savannah.gnu.org/releases/freetype/freetype-old/freetype-2.6.1.tar.gz -source_fallback_url = https://downloads.sourceforge.net/project/freetype/freetype2/2.6.1/freetype-2.6.1.tar.gz -source_filename = freetype-2.6.1.tar.gz -source_hash = 0a3c7dfbda6da1e8fce29232e8e96d987ababbbf71ebc8c75659e4132c367014 - -patch_directory = freetype-2.6.1-meson - -[provide] -freetype-2.6.1 = freetype_dep diff --git a/subprojects/packagefiles/freetype-2.6.1-meson/LICENSE.build b/subprojects/packagefiles/freetype-2.6.1-meson/LICENSE.build deleted file mode 100644 index ec288041f388..000000000000 --- a/subprojects/packagefiles/freetype-2.6.1-meson/LICENSE.build +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2018 The Meson development team - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/subprojects/packagefiles/freetype-2.6.1-meson/builds/unix/ftconfig.h.in b/subprojects/packagefiles/freetype-2.6.1-meson/builds/unix/ftconfig.h.in deleted file mode 100644 index 400f3a2a5bf2..000000000000 --- a/subprojects/packagefiles/freetype-2.6.1-meson/builds/unix/ftconfig.h.in +++ /dev/null @@ -1,498 +0,0 @@ -/***************************************************************************/ -/* */ -/* ftconfig.in */ -/* */ -/* UNIX-specific configuration file (specification only). */ -/* */ -/* Copyright 1996-2015 by */ -/* David Turner, Robert Wilhelm, and Werner Lemberg. */ -/* */ -/* This file is part of the FreeType project, and may only be used, */ -/* modified, and distributed under the terms of the FreeType project */ -/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ -/* this file you indicate that you have read the license and */ -/* understand and accept it fully. */ -/* */ -/***************************************************************************/ - - - /*************************************************************************/ - /* */ - /* This header file contains a number of macro definitions that are used */ - /* by the rest of the engine. Most of the macros here are automatically */ - /* determined at compile time, and you should not need to change it to */ - /* port FreeType, except to compile the library with a non-ANSI */ - /* compiler. */ - /* */ - /* Note however that if some specific modifications are needed, we */ - /* advise you to place a modified copy in your build directory. */ - /* */ - /* The build directory is usually `builds/', and contains */ - /* system-specific files that are always included first when building */ - /* the library. */ - /* */ - /*************************************************************************/ - -/* MESON: based on unix/ftconfig.in with but meson-friendly configuration defines */ - -#ifndef FTCONFIG_H_ -#define FTCONFIG_H_ - -#include -#include FT_CONFIG_OPTIONS_H -#include FT_CONFIG_STANDARD_LIBRARY_H - - -FT_BEGIN_HEADER - - - /*************************************************************************/ - /* */ - /* PLATFORM-SPECIFIC CONFIGURATION MACROS */ - /* */ - /* These macros can be toggled to suit a specific system. The current */ - /* ones are defaults used to compile FreeType in an ANSI C environment */ - /* (16bit compilers are also supported). Copy this file to your own */ - /* `builds/' directory, and edit it to port the engine. */ - /* */ - /*************************************************************************/ - - -#define HAVE_UNISTD_H @HAVE_UNISTD_H@ -#define HAVE_FCNTL_H @HAVE_FCNTL_H@ -#define HAVE_STDINT_H @HAVE_STDINT_H@ - - - /* There are systems (like the Texas Instruments 'C54x) where a `char' */ - /* has 16 bits. ANSI C says that sizeof(char) is always 1. Since an */ - /* `int' has 16 bits also for this system, sizeof(int) gives 1 which */ - /* is probably unexpected. */ - /* */ - /* `CHAR_BIT' (defined in limits.h) gives the number of bits in a */ - /* `char' type. */ - -#ifndef FT_CHAR_BIT -#define FT_CHAR_BIT CHAR_BIT -#endif - - -#undef FT_USE_AUTOCONF_SIZEOF_TYPES -#ifdef FT_USE_AUTOCONF_SIZEOF_TYPES - -#undef SIZEOF_INT -#undef SIZEOF_LONG -#define FT_SIZEOF_INT SIZEOF_INT -#define FT_SIZEOF_LONG SIZEOF_LONG - -#else /* !FT_USE_AUTOCONF_SIZEOF_TYPES */ - - /* Following cpp computation of the bit length of int and long */ - /* is copied from default include/freetype/config/ftconfig.h. */ - /* If any improvement is required for this file, it should be */ - /* applied to the original header file for the builders that */ - /* do not use configure script. */ - - /* The size of an `int' type. */ -#if FT_UINT_MAX == 0xFFFFUL -#define FT_SIZEOF_INT (16 / FT_CHAR_BIT) -#elif FT_UINT_MAX == 0xFFFFFFFFUL -#define FT_SIZEOF_INT (32 / FT_CHAR_BIT) -#elif FT_UINT_MAX > 0xFFFFFFFFUL && FT_UINT_MAX == 0xFFFFFFFFFFFFFFFFUL -#define FT_SIZEOF_INT (64 / FT_CHAR_BIT) -#else -#error "Unsupported size of `int' type!" -#endif - - /* The size of a `long' type. A five-byte `long' (as used e.g. on the */ - /* DM642) is recognized but avoided. */ -#if FT_ULONG_MAX == 0xFFFFFFFFUL -#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) -#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFUL -#define FT_SIZEOF_LONG (32 / FT_CHAR_BIT) -#elif FT_ULONG_MAX > 0xFFFFFFFFUL && FT_ULONG_MAX == 0xFFFFFFFFFFFFFFFFUL -#define FT_SIZEOF_LONG (64 / FT_CHAR_BIT) -#else -#error "Unsupported size of `long' type!" -#endif - -#endif /* !FT_USE_AUTOCONF_SIZEOF_TYPES */ - - - /* FT_UNUSED is a macro used to indicate that a given parameter is not */ - /* used -- this is only used to get rid of unpleasant compiler warnings */ -#ifndef FT_UNUSED -#define FT_UNUSED( arg ) ( (arg) = (arg) ) -#endif - - - /*************************************************************************/ - /* */ - /* AUTOMATIC CONFIGURATION MACROS */ - /* */ - /* These macros are computed from the ones defined above. Don't touch */ - /* their definition, unless you know precisely what you are doing. No */ - /* porter should need to mess with them. */ - /* */ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Mac support */ - /* */ - /* This is the only necessary change, so it is defined here instead */ - /* providing a new configuration file. */ - /* */ -#if defined( __APPLE__ ) || ( defined( __MWERKS__ ) && defined( macintosh ) ) - /* no Carbon frameworks for 64bit 10.4.x */ - /* AvailabilityMacros.h is available since Mac OS X 10.2, */ - /* so guess the system version by maximum errno before inclusion */ -#include -#ifdef ECANCELED /* defined since 10.2 */ -#include "AvailabilityMacros.h" -#endif -#if defined( __LP64__ ) && \ - ( MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 ) -/undef FT_MACINTOSH -#endif - -#elif defined( __SC__ ) || defined( __MRC__ ) - /* Classic MacOS compilers */ -#include "ConditionalMacros.h" -#if TARGET_OS_MAC -#define FT_MACINTOSH 1 -#endif - -#endif - - - /* Fix compiler warning with sgi compiler */ -#if defined( __sgi ) && !defined( __GNUC__ ) -#if defined( _COMPILER_VERSION ) && ( _COMPILER_VERSION >= 730 ) -#pragma set woff 3505 -#endif -#endif - - - /*************************************************************************/ - /* */ - /*

*/ - /* basic_types */ - /* */ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* */ - /* FT_Int16 */ - /* */ - /* */ - /* A typedef for a 16bit signed integer type. */ - /* */ - typedef signed short FT_Int16; - - - /*************************************************************************/ - /* */ - /* */ - /* FT_UInt16 */ - /* */ - /* */ - /* A typedef for a 16bit unsigned integer type. */ - /* */ - typedef unsigned short FT_UInt16; - - /* */ - - - /* this #if 0 ... #endif clause is for documentation purposes */ -#if 0 - - /*************************************************************************/ - /* */ - /* */ - /* FT_Int32 */ - /* */ - /* */ - /* A typedef for a 32bit signed integer type. The size depends on */ - /* the configuration. */ - /* */ - typedef signed XXX FT_Int32; - - - /*************************************************************************/ - /* */ - /* */ - /* FT_UInt32 */ - /* */ - /* A typedef for a 32bit unsigned integer type. The size depends on */ - /* the configuration. */ - /* */ - typedef unsigned XXX FT_UInt32; - - - /*************************************************************************/ - /* */ - /* */ - /* FT_Int64 */ - /* */ - /* A typedef for a 64bit signed integer type. The size depends on */ - /* the configuration. Only defined if there is real 64bit support; */ - /* otherwise, it gets emulated with a structure (if necessary). */ - /* */ - typedef signed XXX FT_Int64; - - - /*************************************************************************/ - /* */ - /* */ - /* FT_UInt64 */ - /* */ - /* A typedef for a 64bit unsigned integer type. The size depends on */ - /* the configuration. Only defined if there is real 64bit support; */ - /* otherwise, it gets emulated with a structure (if necessary). */ - /* */ - typedef unsigned XXX FT_UInt64; - - /* */ - -#endif - -#if FT_SIZEOF_INT == 4 - - typedef signed int FT_Int32; - typedef unsigned int FT_UInt32; - -#elif FT_SIZEOF_LONG == 4 - - typedef signed long FT_Int32; - typedef unsigned long FT_UInt32; - -#else -#error "no 32bit type found -- please check your configuration files" -#endif - - - /* look up an integer type that is at least 32 bits */ -#if FT_SIZEOF_INT >= 4 - - typedef int FT_Fast; - typedef unsigned int FT_UFast; - -#elif FT_SIZEOF_LONG >= 4 - - typedef long FT_Fast; - typedef unsigned long FT_UFast; - -#endif - - - /* determine whether we have a 64-bit int type for platforms without */ - /* Autoconf */ -#if FT_SIZEOF_LONG == 8 - - /* FT_LONG64 must be defined if a 64-bit type is available */ -#define FT_LONG64 -#define FT_INT64 long -#define FT_UINT64 unsigned long - - /*************************************************************************/ - /* */ - /* A 64-bit data type may create compilation problems if you compile */ - /* in strict ANSI mode. To avoid them, we disable other 64-bit data */ - /* types if __STDC__ is defined. You can however ignore this rule */ - /* by defining the FT_CONFIG_OPTION_FORCE_INT64 configuration macro. */ - /* */ -#elif !defined( __STDC__ ) || defined( FT_CONFIG_OPTION_FORCE_INT64 ) - -#if defined( _MSC_VER ) && _MSC_VER >= 900 /* Visual C++ (and Intel C++) */ - - /* this compiler provides the __int64 type */ -#define FT_LONG64 -#define FT_INT64 __int64 -#define FT_UINT64 unsigned __int64 - -#elif defined( __BORLANDC__ ) /* Borland C++ */ - - /* XXXX: We should probably check the value of __BORLANDC__ in order */ - /* to test the compiler version. */ - - /* this compiler provides the __int64 type */ -#define FT_LONG64 -#define FT_INT64 __int64 -#define FT_UINT64 unsigned __int64 - -#elif defined( __WATCOMC__ ) /* Watcom C++ */ - - /* Watcom doesn't provide 64-bit data types */ - -#elif defined( __MWERKS__ ) /* Metrowerks CodeWarrior */ - -#define FT_LONG64 -#define FT_INT64 long long int -#define FT_UINT64 unsigned long long int - -#elif defined( __GNUC__ ) - - /* GCC provides the `long long' type */ -#define FT_LONG64 -#define FT_INT64 long long int -#define FT_UINT64 unsigned long long int - -#endif /* _MSC_VER */ - -#endif /* FT_SIZEOF_LONG == 8 */ - -#ifdef FT_LONG64 - typedef FT_INT64 FT_Int64; - typedef FT_UINT64 FT_UInt64; -#endif - - - /*************************************************************************/ - /* */ - /* miscellaneous */ - /* */ - /*************************************************************************/ - - -#define FT_BEGIN_STMNT do { -#define FT_END_STMNT } while ( 0 ) -#define FT_DUMMY_STMNT FT_BEGIN_STMNT FT_END_STMNT - - - /* typeof condition taken from gnulib's `intprops.h' header file */ -#if ( __GNUC__ >= 2 || \ - defined( __IBM__TYPEOF__ ) || \ - ( __SUNPRO_C >= 0x5110 && !__STDC__ ) ) -#define FT_TYPEOF( type ) (__typeof__ (type)) -#else -#define FT_TYPEOF( type ) /* empty */ -#endif - - -#ifdef FT_MAKE_OPTION_SINGLE_OBJECT - -#define FT_LOCAL( x ) static x -#define FT_LOCAL_DEF( x ) static x - -#else - -#ifdef __cplusplus -#define FT_LOCAL( x ) extern "C" x -#define FT_LOCAL_DEF( x ) extern "C" x -#else -#define FT_LOCAL( x ) extern x -#define FT_LOCAL_DEF( x ) x -#endif - -#endif /* FT_MAKE_OPTION_SINGLE_OBJECT */ - -#define FT_LOCAL_ARRAY( x ) extern const x -#define FT_LOCAL_ARRAY_DEF( x ) const x - - -#ifndef FT_BASE - -#ifdef __cplusplus -#define FT_BASE( x ) extern "C" x -#else -#define FT_BASE( x ) extern x -#endif - -#endif /* !FT_BASE */ - - -#ifndef FT_BASE_DEF - -#ifdef __cplusplus -#define FT_BASE_DEF( x ) x -#else -#define FT_BASE_DEF( x ) x -#endif - -#endif /* !FT_BASE_DEF */ - - -#ifndef FT_EXPORT - -#ifdef __cplusplus -#define FT_EXPORT( x ) extern "C" x -#else -#define FT_EXPORT( x ) extern x -#endif - -#endif /* !FT_EXPORT */ - - -#ifndef FT_EXPORT_DEF - -#ifdef __cplusplus -#define FT_EXPORT_DEF( x ) extern "C" x -#else -#define FT_EXPORT_DEF( x ) extern x -#endif - -#endif /* !FT_EXPORT_DEF */ - - -#ifndef FT_EXPORT_VAR - -#ifdef __cplusplus -#define FT_EXPORT_VAR( x ) extern "C" x -#else -#define FT_EXPORT_VAR( x ) extern x -#endif - -#endif /* !FT_EXPORT_VAR */ - - /* The following macros are needed to compile the library with a */ - /* C++ compiler and with 16bit compilers. */ - /* */ - - /* This is special. Within C++, you must specify `extern "C"' for */ - /* functions which are used via function pointers, and you also */ - /* must do that for structures which contain function pointers to */ - /* assure C linkage -- it's not possible to have (local) anonymous */ - /* functions which are accessed by (global) function pointers. */ - /* */ - /* */ - /* FT_CALLBACK_DEF is used to _define_ a callback function. */ - /* */ - /* FT_CALLBACK_TABLE is used to _declare_ a constant variable that */ - /* contains pointers to callback functions. */ - /* */ - /* FT_CALLBACK_TABLE_DEF is used to _define_ a constant variable */ - /* that contains pointers to callback functions. */ - /* */ - /* */ - /* Some 16bit compilers have to redefine these macros to insert */ - /* the infamous `_cdecl' or `__fastcall' declarations. */ - /* */ -#ifndef FT_CALLBACK_DEF -#ifdef __cplusplus -#define FT_CALLBACK_DEF( x ) extern "C" x -#else -#define FT_CALLBACK_DEF( x ) static x -#endif -#endif /* FT_CALLBACK_DEF */ - -#ifndef FT_CALLBACK_TABLE -#ifdef __cplusplus -#define FT_CALLBACK_TABLE extern "C" -#define FT_CALLBACK_TABLE_DEF extern "C" -#else -#define FT_CALLBACK_TABLE extern -#define FT_CALLBACK_TABLE_DEF /* nothing */ -#endif -#endif /* FT_CALLBACK_TABLE */ - - -FT_END_HEADER - - -#endif /* FTCONFIG_H_ */ - - -/* END */ diff --git a/subprojects/packagefiles/freetype-2.6.1-meson/include/freetype/config/ftoption.h.in b/subprojects/packagefiles/freetype-2.6.1-meson/include/freetype/config/ftoption.h.in deleted file mode 100644 index 5df84c706800..000000000000 --- a/subprojects/packagefiles/freetype-2.6.1-meson/include/freetype/config/ftoption.h.in +++ /dev/null @@ -1,886 +0,0 @@ -/***************************************************************************/ -/* */ -/* ftoption.h */ -/* */ -/* User-selectable configuration macros (specification only). */ -/* */ -/* Copyright 1996-2015 by */ -/* David Turner, Robert Wilhelm, and Werner Lemberg. */ -/* */ -/* This file is part of the FreeType project, and may only be used, */ -/* modified, and distributed under the terms of the FreeType project */ -/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ -/* this file you indicate that you have read the license and */ -/* understand and accept it fully. */ -/* */ -/***************************************************************************/ - - -#ifndef FTOPTION_H_ -#define FTOPTION_H_ - - -#include - - -FT_BEGIN_HEADER - - /*************************************************************************/ - /* */ - /* USER-SELECTABLE CONFIGURATION MACROS */ - /* */ - /* This file contains the default configuration macro definitions for */ - /* a standard build of the FreeType library. There are three ways to */ - /* use this file to build project-specific versions of the library: */ - /* */ - /* - You can modify this file by hand, but this is not recommended in */ - /* cases where you would like to build several versions of the */ - /* library from a single source directory. */ - /* */ - /* - You can put a copy of this file in your build directory, more */ - /* precisely in `$BUILD/freetype/config/ftoption.h', where `$BUILD' */ - /* is the name of a directory that is included _before_ the FreeType */ - /* include path during compilation. */ - /* */ - /* The default FreeType Makefiles and Jamfiles use the build */ - /* directory `builds/' by default, but you can easily change */ - /* that for your own projects. */ - /* */ - /* - Copy the file to `$BUILD/ft2build.h' and modify it */ - /* slightly to pre-define the macro FT_CONFIG_OPTIONS_H used to */ - /* locate this file during the build. For example, */ - /* */ - /* #define FT_CONFIG_OPTIONS_H */ - /* #include */ - /* */ - /* will use `$BUILD/myftoptions.h' instead of this file for macro */ - /* definitions. */ - /* */ - /* Note also that you can similarly pre-define the macro */ - /* FT_CONFIG_MODULES_H used to locate the file listing of the modules */ - /* that are statically linked to the library at compile time. By */ - /* default, this file is . */ - /* */ - /* We highly recommend using the third method whenever possible. */ - /* */ - /*************************************************************************/ - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** G E N E R A L F R E E T Y P E 2 C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Uncomment the line below if you want to activate sub-pixel rendering */ - /* (a.k.a. LCD rendering, or ClearType) in this build of the library. */ - /* */ - /* Note that this feature is covered by several Microsoft patents */ - /* and should not be activated in any default build of the library. */ - /* */ - /* This macro has no impact on the FreeType API, only on its */ - /* _implementation_. For example, using FT_RENDER_MODE_LCD when calling */ - /* FT_Render_Glyph still generates a bitmap that is 3 times wider than */ - /* the original size in case this macro isn't defined; however, each */ - /* triplet of subpixels has R=G=B. */ - /* */ - /* This is done to allow FreeType clients to run unmodified, forcing */ - /* them to display normal gray-level anti-aliased glyphs. */ - /* */ -/* #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING */ - - - /*************************************************************************/ - /* */ - /* Many compilers provide a non-ANSI 64-bit data type that can be used */ - /* by FreeType to speed up some computations. However, this will create */ - /* some problems when compiling the library in strict ANSI mode. */ - /* */ - /* For this reason, the use of 64-bit integers is normally disabled when */ - /* the __STDC__ macro is defined. You can however disable this by */ - /* defining the macro FT_CONFIG_OPTION_FORCE_INT64 here. */ - /* */ - /* For most compilers, this will only create compilation warnings when */ - /* building the library. */ - /* */ - /* ObNote: The compiler-specific 64-bit integers are detected in the */ - /* file `ftconfig.h' either statically or through the */ - /* `configure' script on supported platforms. */ - /* */ -#undef FT_CONFIG_OPTION_FORCE_INT64 - - - /*************************************************************************/ - /* */ - /* If this macro is defined, do not try to use an assembler version of */ - /* performance-critical functions (e.g. FT_MulFix). You should only do */ - /* that to verify that the assembler function works properly, or to */ - /* execute benchmark tests of the various implementations. */ -/* #define FT_CONFIG_OPTION_NO_ASSEMBLER */ - - - /*************************************************************************/ - /* */ - /* If this macro is defined, try to use an inlined assembler version of */ - /* the `FT_MulFix' function, which is a `hotspot' when loading and */ - /* hinting glyphs, and which should be executed as fast as possible. */ - /* */ - /* Note that if your compiler or CPU is not supported, this will default */ - /* to the standard and portable implementation found in `ftcalc.c'. */ - /* */ -#define FT_CONFIG_OPTION_INLINE_MULFIX - - - /*************************************************************************/ - /* */ - /* LZW-compressed file support. */ - /* */ - /* FreeType now handles font files that have been compressed with the */ - /* `compress' program. This is mostly used to parse many of the PCF */ - /* files that come with various X11 distributions. The implementation */ - /* uses NetBSD's `zopen' to partially uncompress the file on the fly */ - /* (see src/lzw/ftgzip.c). */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -#define FT_CONFIG_OPTION_USE_LZW - - - /*************************************************************************/ - /* */ - /* Gzip-compressed file support. */ - /* */ - /* FreeType now handles font files that have been compressed with the */ - /* `gzip' program. This is mostly used to parse many of the PCF files */ - /* that come with XFree86. The implementation uses `zlib' to */ - /* partially uncompress the file on the fly (see src/gzip/ftgzip.c). */ - /* */ - /* Define this macro if you want to enable this `feature'. See also */ - /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ - /* */ -#define FT_CONFIG_OPTION_USE_ZLIB - - - /*************************************************************************/ - /* */ - /* ZLib library selection */ - /* */ - /* This macro is only used when FT_CONFIG_OPTION_USE_ZLIB is defined. */ - /* It allows FreeType's `ftgzip' component to link to the system's */ - /* installation of the ZLib library. This is useful on systems like */ - /* Unix or VMS where it generally is already available. */ - /* */ - /* If you let it undefined, the component will use its own copy */ - /* of the zlib sources instead. These have been modified to be */ - /* included directly within the component and *not* export external */ - /* function names. This allows you to link any program with FreeType */ - /* _and_ ZLib without linking conflicts. */ - /* */ - /* Do not #undef this macro here since the build system might define */ - /* it for certain configurations only. */ - /* */ -#mesondefine FT_CONFIG_OPTION_SYSTEM_ZLIB - - - /*************************************************************************/ - /* */ - /* Bzip2-compressed file support. */ - /* */ - /* FreeType now handles font files that have been compressed with the */ - /* `bzip2' program. This is mostly used to parse many of the PCF */ - /* files that come with XFree86. The implementation uses `libbz2' to */ - /* partially uncompress the file on the fly (see src/bzip2/ftbzip2.c). */ - /* Contrary to gzip, bzip2 currently is not included and need to use */ - /* the system available bzip2 implementation. */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -#mesondefine FT_CONFIG_OPTION_USE_BZIP2 - - - /*************************************************************************/ - /* */ - /* Define to disable the use of file stream functions and types, FILE, */ - /* fopen() etc. Enables the use of smaller system libraries on embedded */ - /* systems that have multiple system libraries, some with or without */ - /* file stream support, in the cases where file stream support is not */ - /* necessary such as memory loading of font files. */ - /* */ -/* #define FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */ - - - /*************************************************************************/ - /* */ - /* PNG bitmap support. */ - /* */ - /* FreeType now handles loading color bitmap glyphs in the PNG format. */ - /* This requires help from the external libpng library. Uncompressed */ - /* color bitmaps do not need any external libraries and will be */ - /* supported regardless of this configuration. */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -#mesondefine FT_CONFIG_OPTION_USE_PNG - - - /*************************************************************************/ - /* */ - /* HarfBuzz support. */ - /* */ - /* FreeType uses the HarfBuzz library to improve auto-hinting of */ - /* OpenType fonts. If available, many glyphs not directly addressable */ - /* by a font's character map will be hinted also. */ - /* */ - /* Define this macro if you want to enable this `feature'. */ - /* */ -#mesondefine FT_CONFIG_OPTION_USE_HARFBUZZ - - - /*************************************************************************/ - /* */ - /* DLL export compilation */ - /* */ - /* When compiling FreeType as a DLL, some systems/compilers need a */ - /* special keyword in front OR after the return type of function */ - /* declarations. */ - /* */ - /* Two macros are used within the FreeType source code to define */ - /* exported library functions: FT_EXPORT and FT_EXPORT_DEF. */ - /* */ - /* FT_EXPORT( return_type ) */ - /* */ - /* is used in a function declaration, as in */ - /* */ - /* FT_EXPORT( FT_Error ) */ - /* FT_Init_FreeType( FT_Library* alibrary ); */ - /* */ - /* */ - /* FT_EXPORT_DEF( return_type ) */ - /* */ - /* is used in a function definition, as in */ - /* */ - /* FT_EXPORT_DEF( FT_Error ) */ - /* FT_Init_FreeType( FT_Library* alibrary ) */ - /* { */ - /* ... some code ... */ - /* return FT_Err_Ok; */ - /* } */ - /* */ - /* You can provide your own implementation of FT_EXPORT and */ - /* FT_EXPORT_DEF here if you want. If you leave them undefined, they */ - /* will be later automatically defined as `extern return_type' to */ - /* allow normal compilation. */ - /* */ - /* Do not #undef these macros here since the build system might define */ - /* them for certain configurations only. */ - /* */ -/* #define FT_EXPORT(x) extern x */ -/* #define FT_EXPORT_DEF(x) x */ - - - /*************************************************************************/ - /* */ - /* Glyph Postscript Names handling */ - /* */ - /* By default, FreeType 2 is compiled with the `psnames' module. This */ - /* module is in charge of converting a glyph name string into a */ - /* Unicode value, or return a Macintosh standard glyph name for the */ - /* use with the TrueType `post' table. */ - /* */ - /* Undefine this macro if you do not want `psnames' compiled in your */ - /* build of FreeType. This has the following effects: */ - /* */ - /* - The TrueType driver will provide its own set of glyph names, */ - /* if you build it to support postscript names in the TrueType */ - /* `post' table. */ - /* */ - /* - The Type 1 driver will not be able to synthesize a Unicode */ - /* charmap out of the glyphs found in the fonts. */ - /* */ - /* You would normally undefine this configuration macro when building */ - /* a version of FreeType that doesn't contain a Type 1 or CFF driver. */ - /* */ -#define FT_CONFIG_OPTION_POSTSCRIPT_NAMES - - - /*************************************************************************/ - /* */ - /* Postscript Names to Unicode Values support */ - /* */ - /* By default, FreeType 2 is built with the `PSNames' module compiled */ - /* in. Among other things, the module is used to convert a glyph name */ - /* into a Unicode value. This is especially useful in order to */ - /* synthesize on the fly a Unicode charmap from the CFF/Type 1 driver */ - /* through a big table named the `Adobe Glyph List' (AGL). */ - /* */ - /* Undefine this macro if you do not want the Adobe Glyph List */ - /* compiled in your `PSNames' module. The Type 1 driver will not be */ - /* able to synthesize a Unicode charmap out of the glyphs found in the */ - /* fonts. */ - /* */ -#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST - - - /*************************************************************************/ - /* */ - /* Support for Mac fonts */ - /* */ - /* Define this macro if you want support for outline fonts in Mac */ - /* format (mac dfont, mac resource, macbinary containing a mac */ - /* resource) on non-Mac platforms. */ - /* */ - /* Note that the `FOND' resource isn't checked. */ - /* */ -#define FT_CONFIG_OPTION_MAC_FONTS - - - /*************************************************************************/ - /* */ - /* Guessing methods to access embedded resource forks */ - /* */ - /* Enable extra Mac fonts support on non-Mac platforms (e.g. */ - /* GNU/Linux). */ - /* */ - /* Resource forks which include fonts data are stored sometimes in */ - /* locations which users or developers don't expected. In some cases, */ - /* resource forks start with some offset from the head of a file. In */ - /* other cases, the actual resource fork is stored in file different */ - /* from what the user specifies. If this option is activated, */ - /* FreeType tries to guess whether such offsets or different file */ - /* names must be used. */ - /* */ - /* Note that normal, direct access of resource forks is controlled via */ - /* the FT_CONFIG_OPTION_MAC_FONTS option. */ - /* */ -#ifdef FT_CONFIG_OPTION_MAC_FONTS -#define FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK -#endif - - - /*************************************************************************/ - /* */ - /* Allow the use of FT_Incremental_Interface to load typefaces that */ - /* contain no glyph data, but supply it via a callback function. */ - /* This is required by clients supporting document formats which */ - /* supply font data incrementally as the document is parsed, such */ - /* as the Ghostscript interpreter for the PostScript language. */ - /* */ -#define FT_CONFIG_OPTION_INCREMENTAL - - - /*************************************************************************/ - /* */ - /* The size in bytes of the render pool used by the scan-line converter */ - /* to do all of its work. */ - /* */ -#define FT_RENDER_POOL_SIZE 16384L - - - /*************************************************************************/ - /* */ - /* FT_MAX_MODULES */ - /* */ - /* The maximum number of modules that can be registered in a single */ - /* FreeType library object. 32 is the default. */ - /* */ -#define FT_MAX_MODULES 32 - - - /*************************************************************************/ - /* */ - /* Debug level */ - /* */ - /* FreeType can be compiled in debug or trace mode. In debug mode, */ - /* errors are reported through the `ftdebug' component. In trace */ - /* mode, additional messages are sent to the standard output during */ - /* execution. */ - /* */ - /* Define FT_DEBUG_LEVEL_ERROR to build the library in debug mode. */ - /* Define FT_DEBUG_LEVEL_TRACE to build it in trace mode. */ - /* */ - /* Don't define any of these macros to compile in `release' mode! */ - /* */ - /* Do not #undef these macros here since the build system might define */ - /* them for certain configurations only. */ - /* */ -/* #define FT_DEBUG_LEVEL_ERROR */ -/* #define FT_DEBUG_LEVEL_TRACE */ - - - /*************************************************************************/ - /* */ - /* Autofitter debugging */ - /* */ - /* If FT_DEBUG_AUTOFIT is defined, FreeType provides some means to */ - /* control the autofitter behaviour for debugging purposes with global */ - /* boolean variables (consequently, you should *never* enable this */ - /* while compiling in `release' mode): */ - /* */ - /* _af_debug_disable_horz_hints */ - /* _af_debug_disable_vert_hints */ - /* _af_debug_disable_blue_hints */ - /* */ - /* Additionally, the following functions provide dumps of various */ - /* internal autofit structures to stdout (using `printf'): */ - /* */ - /* af_glyph_hints_dump_points */ - /* af_glyph_hints_dump_segments */ - /* af_glyph_hints_dump_edges */ - /* af_glyph_hints_get_num_segments */ - /* af_glyph_hints_get_segment_offset */ - /* */ - /* As an argument, they use another global variable: */ - /* */ - /* _af_debug_hints */ - /* */ - /* Please have a look at the `ftgrid' demo program to see how those */ - /* variables and macros should be used. */ - /* */ - /* Do not #undef these macros here since the build system might define */ - /* them for certain configurations only. */ - /* */ -/* #define FT_DEBUG_AUTOFIT */ - - - /*************************************************************************/ - /* */ - /* Memory Debugging */ - /* */ - /* FreeType now comes with an integrated memory debugger that is */ - /* capable of detecting simple errors like memory leaks or double */ - /* deletes. To compile it within your build of the library, you */ - /* should define FT_DEBUG_MEMORY here. */ - /* */ - /* Note that the memory debugger is only activated at runtime when */ - /* when the _environment_ variable `FT2_DEBUG_MEMORY' is defined also! */ - /* */ - /* Do not #undef this macro here since the build system might define */ - /* it for certain configurations only. */ - /* */ -/* #define FT_DEBUG_MEMORY */ - - - /*************************************************************************/ - /* */ - /* Module errors */ - /* */ - /* If this macro is set (which is _not_ the default), the higher byte */ - /* of an error code gives the module in which the error has occurred, */ - /* while the lower byte is the real error code. */ - /* */ - /* Setting this macro makes sense for debugging purposes only, since */ - /* it would break source compatibility of certain programs that use */ - /* FreeType 2. */ - /* */ - /* More details can be found in the files ftmoderr.h and fterrors.h. */ - /* */ -#undef FT_CONFIG_OPTION_USE_MODULE_ERRORS - - - /*************************************************************************/ - /* */ - /* Position Independent Code */ - /* */ - /* If this macro is set (which is _not_ the default), FreeType2 will */ - /* avoid creating constants that require address fixups. Instead the */ - /* constants will be moved into a struct and additional intialization */ - /* code will be used. */ - /* */ - /* Setting this macro is needed for systems that prohibit address */ - /* fixups, such as BREW. */ - /* */ -#mesondefine FT_CONFIG_OPTION_PIC - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** S F N T D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_EMBEDDED_BITMAPS if you want to support */ - /* embedded bitmaps in all formats using the SFNT module (namely */ - /* TrueType & OpenType). */ - /* */ -#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_POSTSCRIPT_NAMES if you want to be able to */ - /* load and enumerate the glyph Postscript names in a TrueType or */ - /* OpenType file. */ - /* */ - /* Note that when you do not compile the `PSNames' module by undefining */ - /* the above FT_CONFIG_OPTION_POSTSCRIPT_NAMES, the `sfnt' module will */ - /* contain additional code used to read the PS Names table from a font. */ - /* */ - /* (By default, the module uses `PSNames' to extract glyph names.) */ - /* */ -#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_SFNT_NAMES if your applications need to */ - /* access the internal name table in a SFNT-based format like TrueType */ - /* or OpenType. The name table contains various strings used to */ - /* describe the font, like family name, copyright, version, etc. It */ - /* does not contain any glyph name though. */ - /* */ - /* Accessing SFNT names is done through the functions declared in */ - /* `ftsnames.h'. */ - /* */ -#define TT_CONFIG_OPTION_SFNT_NAMES - - - /*************************************************************************/ - /* */ - /* TrueType CMap support */ - /* */ - /* Here you can fine-tune which TrueType CMap table format shall be */ - /* supported. */ -#define TT_CONFIG_CMAP_FORMAT_0 -#define TT_CONFIG_CMAP_FORMAT_2 -#define TT_CONFIG_CMAP_FORMAT_4 -#define TT_CONFIG_CMAP_FORMAT_6 -#define TT_CONFIG_CMAP_FORMAT_8 -#define TT_CONFIG_CMAP_FORMAT_10 -#define TT_CONFIG_CMAP_FORMAT_12 -#define TT_CONFIG_CMAP_FORMAT_13 -#define TT_CONFIG_CMAP_FORMAT_14 - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** T R U E T Y P E D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_BYTECODE_INTERPRETER if you want to compile */ - /* a bytecode interpreter in the TrueType driver. */ - /* */ - /* By undefining this, you will only compile the code necessary to load */ - /* TrueType glyphs without hinting. */ - /* */ - /* Do not #undef this macro here, since the build system might */ - /* define it for certain configurations only. */ - /* */ -#define TT_CONFIG_OPTION_BYTECODE_INTERPRETER - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_SUBPIXEL_HINTING if you want to compile */ - /* EXPERIMENTAL subpixel hinting support into the TrueType driver. This */ - /* replaces the native TrueType hinting mechanism when anything but */ - /* FT_RENDER_MODE_MONO is requested. */ - /* */ - /* Enabling this causes the TrueType driver to ignore instructions under */ - /* certain conditions. This is done in accordance with the guide here, */ - /* with some minor differences: */ - /* */ - /* http://www.microsoft.com/typography/cleartype/truetypecleartype.aspx */ - /* */ - /* By undefining this, you only compile the code necessary to hint */ - /* TrueType glyphs with native TT hinting. */ - /* */ - /* This option requires TT_CONFIG_OPTION_BYTECODE_INTERPRETER to be */ - /* defined. */ - /* */ -/* #define TT_CONFIG_OPTION_SUBPIXEL_HINTING */ - - - /*************************************************************************/ - /* */ - /* If you define TT_CONFIG_OPTION_UNPATENTED_HINTING, a special version */ - /* of the TrueType bytecode interpreter is used that doesn't implement */ - /* any of the patented opcodes and algorithms. The patents related to */ - /* TrueType hinting have expired worldwide since May 2010; this option */ - /* is now deprecated. */ - /* */ - /* Note that the TT_CONFIG_OPTION_UNPATENTED_HINTING macro is *ignored* */ - /* if you define TT_CONFIG_OPTION_BYTECODE_INTERPRETER; in other words, */ - /* either define TT_CONFIG_OPTION_BYTECODE_INTERPRETER or */ - /* TT_CONFIG_OPTION_UNPATENTED_HINTING but not both at the same time. */ - /* */ - /* This macro is only useful for a small number of font files (mostly */ - /* for Asian scripts) that require bytecode interpretation to properly */ - /* load glyphs. For all other fonts, this produces unpleasant results, */ - /* thus the unpatented interpreter is never used to load glyphs from */ - /* TrueType fonts unless one of the following two options is used. */ - /* */ - /* - The unpatented interpreter is explicitly activated by the user */ - /* through the FT_PARAM_TAG_UNPATENTED_HINTING parameter tag */ - /* when opening the FT_Face. */ - /* */ - /* - FreeType detects that the FT_Face corresponds to one of the */ - /* `trick' fonts (e.g., `Mingliu') it knows about. The font engine */ - /* contains a hard-coded list of font names and other matching */ - /* parameters (see function `tt_face_init' in file */ - /* `src/truetype/ttobjs.c'). */ - /* */ - /* Here a sample code snippet for using FT_PARAM_TAG_UNPATENTED_HINTING. */ - /* */ - /* { */ - /* FT_Parameter parameter; */ - /* FT_Open_Args open_args; */ - /* */ - /* */ - /* parameter.tag = FT_PARAM_TAG_UNPATENTED_HINTING; */ - /* */ - /* open_args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; */ - /* open_args.pathname = my_font_pathname; */ - /* open_args.num_params = 1; */ - /* open_args.params = ¶meter; */ - /* */ - /* error = FT_Open_Face( library, &open_args, index, &face ); */ - /* ... */ - /* } */ - /* */ -/* #define TT_CONFIG_OPTION_UNPATENTED_HINTING */ - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED to compile the */ - /* TrueType glyph loader to use Apple's definition of how to handle */ - /* component offsets in composite glyphs. */ - /* */ - /* Apple and MS disagree on the default behavior of component offsets */ - /* in composites. Apple says that they should be scaled by the scaling */ - /* factors in the transformation matrix (roughly, it's more complex) */ - /* while MS says they should not. OpenType defines two bits in the */ - /* composite flags array which can be used to disambiguate, but old */ - /* fonts will not have them. */ - /* */ - /* http://www.microsoft.com/typography/otspec/glyf.htm */ - /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6glyf.html */ - /* */ -#undef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_GX_VAR_SUPPORT if you want to include */ - /* support for Apple's distortable font technology (fvar, gvar, cvar, */ - /* and avar tables). This has many similarities to Type 1 Multiple */ - /* Masters support. */ - /* */ -#define TT_CONFIG_OPTION_GX_VAR_SUPPORT - - - /*************************************************************************/ - /* */ - /* Define TT_CONFIG_OPTION_BDF if you want to include support for */ - /* an embedded `BDF ' table within SFNT-based bitmap formats. */ - /* */ -#define TT_CONFIG_OPTION_BDF - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** T Y P E 1 D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* T1_MAX_DICT_DEPTH is the maximum depth of nest dictionaries and */ - /* arrays in the Type 1 stream (see t1load.c). A minimum of 4 is */ - /* required. */ - /* */ -#define T1_MAX_DICT_DEPTH 5 - - - /*************************************************************************/ - /* */ - /* T1_MAX_SUBRS_CALLS details the maximum number of nested sub-routine */ - /* calls during glyph loading. */ - /* */ -#define T1_MAX_SUBRS_CALLS 16 - - - /*************************************************************************/ - /* */ - /* T1_MAX_CHARSTRING_OPERANDS is the charstring stack's capacity. A */ - /* minimum of 16 is required. */ - /* */ - /* The Chinese font MingTiEG-Medium (CNS 11643 character set) needs 256. */ - /* */ -#define T1_MAX_CHARSTRINGS_OPERANDS 256 - - - /*************************************************************************/ - /* */ - /* Define this configuration macro if you want to prevent the */ - /* compilation of `t1afm', which is in charge of reading Type 1 AFM */ - /* files into an existing face. Note that if set, the T1 driver will be */ - /* unable to produce kerning distances. */ - /* */ -#undef T1_CONFIG_OPTION_NO_AFM - - - /*************************************************************************/ - /* */ - /* Define this configuration macro if you want to prevent the */ - /* compilation of the Multiple Masters font support in the Type 1 */ - /* driver. */ - /* */ -#undef T1_CONFIG_OPTION_NO_MM_SUPPORT - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** C F F D R I V E R C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Using CFF_CONFIG_OPTION_DARKENING_PARAMETER_{X,Y}{1,2,3,4} it is */ - /* possible to set up the default values of the four control points that */ - /* define the stem darkening behaviour of the (new) CFF engine. For */ - /* more details please read the documentation of the */ - /* `darkening-parameters' property of the cff driver module (file */ - /* `ftcffdrv.h'), which allows the control at run-time. */ - /* */ - /* Do *not* undefine these macros! */ - /* */ -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 500 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 400 - -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 1000 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 275 - -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 1667 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 275 - -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 2333 -#define CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 0 - - - /*************************************************************************/ - /* */ - /* CFF_CONFIG_OPTION_OLD_ENGINE controls whether the pre-Adobe CFF */ - /* engine gets compiled into FreeType. If defined, it is possible to */ - /* switch between the two engines using the `hinting-engine' property of */ - /* the cff driver module. */ - /* */ -/* #define CFF_CONFIG_OPTION_OLD_ENGINE */ - - - /*************************************************************************/ - /*************************************************************************/ - /**** ****/ - /**** A U T O F I T M O D U L E C O N F I G U R A T I O N ****/ - /**** ****/ - /*************************************************************************/ - /*************************************************************************/ - - - /*************************************************************************/ - /* */ - /* Compile autofit module with CJK (Chinese, Japanese, Korean) script */ - /* support. */ - /* */ -#define AF_CONFIG_OPTION_CJK - - /*************************************************************************/ - /* */ - /* Compile autofit module with Indic script support. */ - /* */ -#define AF_CONFIG_OPTION_INDIC - - /*************************************************************************/ - /* */ - /* Compile autofit module with warp hinting. The idea of the warping */ - /* code is to slightly scale and shift a glyph within a single dimension */ - /* so that as much of its segments are aligned (more or less) on the */ - /* grid. To find out the optimal scaling and shifting value, various */ - /* parameter combinations are tried and scored. */ - /* */ - /* This experimental option is active only if the rendering mode is */ - /* FT_RENDER_MODE_LIGHT; you can switch warping on and off with the */ - /* `warping' property of the auto-hinter (see file `ftautoh.h' for more */ - /* information; by default it is switched off). */ - /* */ -#define AF_CONFIG_OPTION_USE_WARPER - - /* */ - - - /* - * This macro is obsolete. Support has been removed in FreeType - * version 2.5. - */ -/* #define FT_CONFIG_OPTION_OLD_INTERNALS */ - - - /* - * This macro is defined if either unpatented or native TrueType - * hinting is requested by the definitions above. - */ -#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER -#define TT_USE_BYTECODE_INTERPRETER -#undef TT_CONFIG_OPTION_UNPATENTED_HINTING -#elif defined TT_CONFIG_OPTION_UNPATENTED_HINTING -#define TT_USE_BYTECODE_INTERPRETER -#endif - - - /* - * Check CFF darkening parameters. The checks are the same as in function - * `cff_property_set' in file `cffdrivr.c'. - */ -#if CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 < 0 || \ - \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 < 0 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 < 0 || \ - \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1 > \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2 > \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3 > \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4 || \ - \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1 > 500 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2 > 500 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3 > 500 || \ - CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4 > 500 -#error "Invalid CFF darkening parameters!" -#endif - -FT_END_HEADER - - -#endif /* FTOPTION_H_ */ - - -/* END */ diff --git a/subprojects/packagefiles/freetype-2.6.1-meson/meson.build b/subprojects/packagefiles/freetype-2.6.1-meson/meson.build deleted file mode 100644 index 9a5180ef7586..000000000000 --- a/subprojects/packagefiles/freetype-2.6.1-meson/meson.build +++ /dev/null @@ -1,193 +0,0 @@ -project('freetype2', 'c', - version: '2.6.1', - license: '(FTL OR GPL-2.0-or-later) AND BSD-3-Clause AND MIT AND MIT-Modern-Variant AND Zlib', - license_files: [ - 'docs/LICENSE.TXT', - 'docs/FTL.TXT', - 'docs/GPLv2.TXT', - ], - meson_version: '>=1.1.0') - -# NOTE about FreeType versions -# There are 3 versions numbers associated with each releases: -# - official release number (eg. 2.6.1) - accessible via -# FREETYPE_{MAJOR,MINOR,PATCH} macros from FT_FREETYPE_H -# - libtool-specific version number, this is what is returned by -# freetype-config --version / pkg-config --modversion (eg. 22.1.16) -# - the platform-specific shared object version number (eg. 6.16.1) -# See https://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/VERSIONS.TXT -# for more information -release_version = meson.project_version() -libtool_version = '18.1.12' -so_version = '6.12.1' -so_soversion = '6' - -pkgmod = import('pkgconfig') - -cc = meson.get_compiler('c') - -base_sources = [ - 'src/autofit/autofit.c', - 'src/base/ftbase.c', - 'src/base/ftbbox.c', - 'src/base/ftbdf.c', - 'src/base/ftbitmap.c', - 'src/base/ftcid.c', - 'src/base/ftfntfmt.c', - 'src/base/ftfstype.c', - 'src/base/ftgasp.c', - 'src/base/ftglyph.c', - 'src/base/ftgxval.c', - 'src/base/ftinit.c', - 'src/base/ftlcdfil.c', - 'src/base/ftmm.c', - 'src/base/ftotval.c', - 'src/base/ftpatent.c', - 'src/base/ftpfr.c', - 'src/base/ftstroke.c', - 'src/base/ftsynth.c', - 'src/base/ftsystem.c', - 'src/base/fttype1.c', - 'src/base/ftwinfnt.c', - 'src/bdf/bdf.c', - 'src/bzip2/ftbzip2.c', - 'src/cache/ftcache.c', - 'src/cff/cff.c', - 'src/cid/type1cid.c', - 'src/gzip/ftgzip.c', - 'src/lzw/ftlzw.c', - 'src/pcf/pcf.c', - 'src/pfr/pfr.c', - 'src/psaux/psaux.c', - 'src/pshinter/pshinter.c', - 'src/psnames/psnames.c', - 'src/raster/raster.c', - 'src/sfnt/sfnt.c', - 'src/smooth/smooth.c', - 'src/truetype/truetype.c', - 'src/type1/type1.c', - 'src/type42/type42.c', - 'src/winfonts/winfnt.c', -] - -ft2build_h = [ - 'include/ft2build.h', -] - -ft_headers = [ - 'include/freetype/freetype.h', - 'include/freetype/ftadvanc.h', - 'include/freetype/ftautoh.h', - 'include/freetype/ftbbox.h', - 'include/freetype/ftbdf.h', - 'include/freetype/ftbitmap.h', - 'include/freetype/ftbzip2.h', - 'include/freetype/ftcache.h', - 'include/freetype/ftcffdrv.h', - 'include/freetype/ftchapters.h', - 'include/freetype/ftcid.h', - 'include/freetype/fterrdef.h', - 'include/freetype/fterrors.h', - 'include/freetype/ftfntfmt.h', - 'include/freetype/ftgasp.h', - 'include/freetype/ftglyph.h', - 'include/freetype/ftgxval.h', - 'include/freetype/ftgzip.h', - 'include/freetype/ftimage.h', - 'include/freetype/ftincrem.h', - 'include/freetype/ftlcdfil.h', - 'include/freetype/ftlist.h', - 'include/freetype/ftlzw.h', - 'include/freetype/ftmac.h', - 'include/freetype/ftmm.h', - 'include/freetype/ftmodapi.h', - 'include/freetype/ftmoderr.h', - 'include/freetype/ftotval.h', - 'include/freetype/ftoutln.h', - 'include/freetype/ftpfr.h', - 'include/freetype/ftrender.h', - 'include/freetype/ftsizes.h', - 'include/freetype/ftsnames.h', - 'include/freetype/ftstroke.h', - 'include/freetype/ftsynth.h', - 'include/freetype/ftsystem.h', - 'include/freetype/fttrigon.h', - 'include/freetype/ftttdrv.h', - 'include/freetype/fttypes.h', - 'include/freetype/ftwinfnt.h', - 'include/freetype/t1tables.h', - 'include/freetype/ttnameid.h', - 'include/freetype/tttables.h', - 'include/freetype/tttags.h', - 'include/freetype/ttunpat.h', -] - -ft_config_headers = [ - 'include/freetype/config/ftconfig.h', - 'include/freetype/config/ftheader.h', - 'include/freetype/config/ftmodule.h', - 'include/freetype/config/ftoption.h', - 'include/freetype/config/ftstdlib.h', -] - -if host_machine.system() == 'windows' - base_sources += [ - 'builds/windows/ftdebug.c', - ] -else - base_sources += [ - 'src/base/ftdebug.c', - ] -endif - -c_args = [ - '-DFT2_BUILD_LIBRARY', - '-DFT_CONFIG_CONFIG_H=', - '-DFT_CONFIG_OPTIONS_H=' -] - -check_headers = [] - -if ['linux', 'darwin', 'cygwin'].contains(host_machine.system()) - check_headers += [ - ['unistd.h'], - ['fcntl.h'], - ['stdint.h'], - ] - ftconfig_h_in = files('builds/unix/ftconfig.h.in') -else - ftconfig_h_in = files('include/freetype/config/ftconfig.h') -endif - -conf = configuration_data() -deps = [] -incbase = include_directories(['include']) - -foreach check : check_headers - name = check[0] - - if cc.has_header(name) - conf.set('HAVE_@0@'.format(name.to_upper().underscorify()), 1) - endif -endforeach - -configure_file(input: ftconfig_h_in, - output: 'ftconfig.h', - configuration: conf) - -ft_config_headers += [configure_file(input: 'include/freetype/config/ftoption.h.in', - output: 'ftoption.h', - configuration: conf)] - -libfreetype = static_library('freetype', base_sources, - include_directories: incbase, - dependencies: deps, - c_args: c_args, - gnu_symbol_visibility: 'inlineshidden', -) - -freetype_dep = declare_dependency( - link_with: libfreetype, - include_directories : incbase, - dependencies: deps, -) diff --git a/subprojects/packagefiles/freetype-2.6.1-meson/src/gzip/zconf.h b/subprojects/packagefiles/freetype-2.6.1-meson/src/gzip/zconf.h deleted file mode 100644 index d88a82a2eec8..000000000000 --- a/subprojects/packagefiles/freetype-2.6.1-meson/src/gzip/zconf.h +++ /dev/null @@ -1,284 +0,0 @@ -/* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2002 Jean-loup Gailly. - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* @(#) $Id$ */ - -#ifndef _ZCONF_H -#define _ZCONF_H - -/* - * If you *really* need a unique prefix for all types and library functions, - * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. - */ -#ifdef Z_PREFIX -# define deflateInit_ z_deflateInit_ -# define deflate z_deflate -# define deflateEnd z_deflateEnd -# define inflateInit_ z_inflateInit_ -# define inflate z_inflate -# define inflateEnd z_inflateEnd -# define deflateInit2_ z_deflateInit2_ -# define deflateSetDictionary z_deflateSetDictionary -# define deflateCopy z_deflateCopy -# define deflateReset z_deflateReset -# define deflateParams z_deflateParams -# define inflateInit2_ z_inflateInit2_ -# define inflateSetDictionary z_inflateSetDictionary -# define inflateSync z_inflateSync -# define inflateSyncPoint z_inflateSyncPoint -# define inflateReset z_inflateReset -# define compress z_compress -# define compress2 z_compress2 -# define uncompress z_uncompress -# define adler32 z_adler32 -# define crc32 z_crc32 -# define get_crc_table z_get_crc_table - -# define Byte z_Byte -# define uInt z_uInt -# define uLong z_uLong -# define Bytef z_Bytef -# define charf z_charf -# define intf z_intf -# define uIntf z_uIntf -# define uLongf z_uLongf -# define voidpf z_voidpf -# define voidp z_voidp -#endif - -#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -# define WIN32 -#endif -#if defined(__GNUC__) || defined(WIN32) || defined(__386__) || defined(i386) -# ifndef __32BIT__ -# define __32BIT__ -# endif -#endif -#if defined(__MSDOS__) && !defined(MSDOS) -# define MSDOS -#endif - -/* WinCE doesn't have errno.h */ -#ifdef _WIN32_WCE -# define NO_ERRNO_H -#endif - - -/* - * Compile with -DMAXSEG_64K if the alloc function cannot allocate more - * than 64k bytes at a time (needed on systems with 16-bit int). - */ -#if defined(MSDOS) && !defined(__32BIT__) -# define MAXSEG_64K -#endif -#ifdef MSDOS -# define UNALIGNED_OK -#endif - -#if (defined(MSDOS) || defined(_WINDOWS) || defined(WIN32)) && !defined(STDC) -# define STDC -#endif -#if defined(__STDC__) || defined(__cplusplus) || defined(__OS2__) -# ifndef STDC -# define STDC -# endif -#endif - -#ifndef STDC -# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ -# define const -# endif -#endif - -/* Some Mac compilers merge all .h files incorrectly: */ -#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__) -# define NO_DUMMY_DECL -#endif - -/* Old Borland C and LCC incorrectly complains about missing returns: */ -#if defined(__BORLANDC__) && (__BORLANDC__ < 0x500) -# define NEED_DUMMY_RETURN -#endif - -#if defined(__LCC__) -# define NEED_DUMMY_RETURN -#endif - -/* Maximum value for memLevel in deflateInit2 */ -#ifndef MAX_MEM_LEVEL -# ifdef MAXSEG_64K -# define MAX_MEM_LEVEL 8 -# else -# define MAX_MEM_LEVEL 9 -# endif -#endif - -/* Maximum value for windowBits in deflateInit2 and inflateInit2. - * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files - * created by gzip. (Files created by minigzip can still be extracted by - * gzip.) - */ -#ifndef MAX_WBITS -# define MAX_WBITS 15 /* 32K LZ77 window */ -#endif - -/* The memory requirements for deflate are (in bytes): - (1 << (windowBits+2)) + (1 << (memLevel+9)) - that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) - plus a few kilobytes for small objects. For example, if you want to reduce - the default memory requirements from 256K to 128K, compile with - make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" - Of course this will generally degrade compression (there's no free lunch). - - The memory requirements for inflate are (in bytes) 1 << windowBits - that is, 32K for windowBits=15 (default value) plus a few kilobytes - for small objects. -*/ - - /* Type declarations */ - -#ifndef OF /* function prototypes */ -# ifdef STDC -# define OF(args) args -# else -# define OF(args) () -# endif -#endif - -/* The following definitions for FAR are needed only for MSDOS mixed - * model programming (small or medium model with some far allocations). - * This was tested only with MSC; for other MSDOS compilers you may have - * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, - * just define FAR to be empty. - */ -#if (defined(M_I86SM) || defined(M_I86MM)) && !defined(__32BIT__) - /* MSC small or medium model */ -# define SMALL_MEDIUM -# ifdef _MSC_VER -# define FAR _far -# else -# define FAR far -# endif -#endif -#if defined(__BORLANDC__) && (defined(__SMALL__) || defined(__MEDIUM__)) -# ifndef __32BIT__ -# define SMALL_MEDIUM -# define FAR _far -# endif -#endif - -/* Compile with -DZLIB_DLL for Windows DLL support */ -#if defined(ZLIB_DLL) -# if defined(_WINDOWS) || defined(WINDOWS) -# ifdef FAR -# undef FAR -# endif -# include -# define ZEXPORT(x) x WINAPI -# ifdef WIN32 -# define ZEXPORTVA(x) x WINAPIV -# else -# define ZEXPORTVA(x) x FAR _cdecl _export -# endif -# endif -# if defined (__BORLANDC__) -# if (__BORLANDC__ >= 0x0500) && defined (WIN32) -# include -# define ZEXPORT(x) x __declspec(dllexport) WINAPI -# define ZEXPORTRVA(x) x __declspec(dllexport) WINAPIV -# else -# if defined (_Windows) && defined (__DLL__) -# define ZEXPORT(x) x _export -# define ZEXPORTVA(x) x _export -# endif -# endif -# endif -#endif - - -#ifndef ZEXPORT -# define ZEXPORT(x) static x -#endif -#ifndef ZEXPORTVA -# define ZEXPORTVA(x) static x -#endif -#ifndef ZEXTERN -# define ZEXTERN(x) static x -#endif -#ifndef ZEXTERNDEF -# define ZEXTERNDEF(x) static x -#endif - -#ifndef FAR -# define FAR -#endif - -#if !defined(__MACTYPES__) -typedef unsigned char Byte; /* 8 bits */ -#endif -typedef unsigned int uInt; /* 16 bits or more */ -typedef unsigned long uLong; /* 32 bits or more */ - -#ifdef SMALL_MEDIUM - /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ -# define Bytef Byte FAR -#else - typedef Byte FAR Bytef; -#endif -typedef char FAR charf; -typedef int FAR intf; -typedef uInt FAR uIntf; -typedef uLong FAR uLongf; - -#ifdef STDC - typedef void FAR *voidpf; - typedef void *voidp; -#else - typedef Byte FAR *voidpf; - typedef Byte *voidp; -#endif - -#ifdef HAVE_UNISTD_H -# include /* for off_t */ -# include /* for SEEK_* and off_t */ -# define z_off_t off_t -#endif -#ifndef SEEK_SET -# define SEEK_SET 0 /* Seek from beginning of file. */ -# define SEEK_CUR 1 /* Seek from current position. */ -# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ -#endif -#ifndef z_off_t -# define z_off_t long -#endif - -/* MVS linker does not support external names larger than 8 bytes */ -#if defined(__MVS__) -# pragma map(deflateInit_,"DEIN") -# pragma map(deflateInit2_,"DEIN2") -# pragma map(deflateEnd,"DEEND") -# pragma map(inflateInit_,"ININ") -# pragma map(inflateInit2_,"ININ2") -# pragma map(inflateEnd,"INEND") -# pragma map(inflateSync,"INSY") -# pragma map(inflateSetDictionary,"INSEDI") -# pragma map(inflate_blocks,"INBL") -# pragma map(inflate_blocks_new,"INBLNE") -# pragma map(inflate_blocks_free,"INBLFR") -# pragma map(inflate_blocks_reset,"INBLRE") -# pragma map(inflate_codes_free,"INCOFR") -# pragma map(inflate_codes,"INCO") -# pragma map(inflate_fast,"INFA") -# pragma map(inflate_flush,"INFLU") -# pragma map(inflate_mask,"INMA") -# pragma map(inflate_set_dictionary,"INSEDI2") -# pragma map(inflate_copyright,"INCOPY") -# pragma map(inflate_trees_bits,"INTRBI") -# pragma map(inflate_trees_dynamic,"INTRDY") -# pragma map(inflate_trees_fixed,"INTRFI") -# pragma map(inflate_trees_free,"INTRFR") -#endif - -#endif /* _ZCONF_H */ diff --git a/subprojects/packagefiles/freetype-34aed655f1696da774b5cdd4c5effb312153232f.patch b/subprojects/packagefiles/freetype-34aed655f1696da774b5cdd4c5effb312153232f.patch new file mode 100644 index 000000000000..c00baa702f65 --- /dev/null +++ b/subprojects/packagefiles/freetype-34aed655f1696da774b5cdd4c5effb312153232f.patch @@ -0,0 +1,36 @@ +From 34aed655f1696da774b5cdd4c5effb312153232f Mon Sep 17 00:00:00 2001 +From: Benoit Pierre +Date: Sat, 12 Oct 2024 10:49:46 +0000 +Subject: [PATCH] * meson.build: Fix `bzip2` option handling. + +--- + meson.build | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/meson.build b/meson.build +index 72b7f9900..2e8d5355e 100644 +--- a/meson.build ++++ b/meson.build +@@ -320,11 +320,16 @@ else + endif + + # BZip2 support. +-bzip2_dep = dependency('bzip2', required: false) ++bzip2_dep = dependency( ++ 'bzip2', ++ required: get_option('bzip2').disabled() ? get_option('bzip2') : false, ++) + if not bzip2_dep.found() +- bzip2_dep = cc.find_library('bz2', ++ bzip2_dep = cc.find_library( ++ 'bz2', + has_headers: ['bzlib.h'], +- required: get_option('bzip2')) ++ required: get_option('bzip2'), ++ ) + endif + + if bzip2_dep.found() +-- +GitLab + From 7787153a524475e60350ca2d87b2caf00905c6aa Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 23 Apr 2025 05:36:31 -0400 Subject: [PATCH 10/20] Bump minimum meson-python to 0.13.2 --- pyproject.toml | 4 ++-- requirements/testing/minver.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b580feff930e..18d99e3111e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ requires-python = ">=3.11" [project.optional-dependencies] # Should be a copy of the build dependencies below. dev = [ - "meson-python>=0.13.1,!=0.17.*", + "meson-python>=0.13.2,!=0.17.*", "pybind11>=2.13.2,!=2.13.3", "setuptools_scm>=7", # Not required by us but setuptools_scm without a version, cso _if_ @@ -72,7 +72,7 @@ build-backend = "mesonpy" requires = [ # meson-python 0.17.x breaks symlinks in sdists. You can remove this pin if # you really need it and aren't using an sdist. - "meson-python>=0.13.1,!=0.17.*", + "meson-python>=0.13.2,!=0.17.*", "pybind11>=2.13.2,!=2.13.3", "setuptools_scm>=7", ] diff --git a/requirements/testing/minver.txt b/requirements/testing/minver.txt index ee55f6c7b1bf..3b6aea9e7ca3 100644 --- a/requirements/testing/minver.txt +++ b/requirements/testing/minver.txt @@ -5,7 +5,7 @@ cycler==0.10 fonttools==4.22.0 importlib-resources==3.2.0 kiwisolver==1.3.2 -meson-python==0.13.1 +meson-python==0.13.2 meson==1.1.0 numpy==1.25.0 packaging==20.0 From 972a82173fba27b00696fbcb5cc4a20caacb6bd0 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 2 Jul 2025 17:36:02 -0400 Subject: [PATCH 11/20] ci: Purge Strawberry Perl from Windows builders We don't use Perl, and it includes a completely busted version of `patch`. --- .github/workflows/cibuildwheel.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cibuildwheel.yml b/.github/workflows/cibuildwheel.yml index fececb0dfc40..dc05ec5483fd 100644 --- a/.github/workflows/cibuildwheel.yml +++ b/.github/workflows/cibuildwheel.yml @@ -140,6 +140,10 @@ jobs: name: cibw-sdist path: dist/ + - name: Purge Strawberry Perl + if: startsWith(matrix.os, 'windows-') + run: Remove-Item -Recurse C:\Strawberry + - name: Build wheels for CPython 3.14 uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 # v3.0.0 with: From 5fc955939dd074beea4554b6e2b668061fb46073 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 16 Jul 2025 04:42:42 -0400 Subject: [PATCH 12/20] Don't set a default size for FT2Font In the interest of handling non-scalable fonts and reducing font initialization, drop the default size from the `FT2Font` constructor. Non-scalable fonts are sometimes used for bitmap-backed emoji fonts. When we start supporting collection fonts (`.ttc`), then setting a size is a waste, as we will just need to read the count of fonts within. The renderer method `Renderer.draw_text` always sets a size immediately after creating the font object, so this doesn't affect anything in most cases. Only the direct `FT2Font` tests need changes. --- doc/api/next_api_changes/behavior/30318-ES.rst | 9 +++++++++ lib/matplotlib/tests/test_ft2font.py | 7 ++++++- src/ft2font.cpp | 6 ------ 3 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 doc/api/next_api_changes/behavior/30318-ES.rst diff --git a/doc/api/next_api_changes/behavior/30318-ES.rst b/doc/api/next_api_changes/behavior/30318-ES.rst new file mode 100644 index 000000000000..805901dcb21d --- /dev/null +++ b/doc/api/next_api_changes/behavior/30318-ES.rst @@ -0,0 +1,9 @@ +FT2Font no longer sets a default size +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the interest of handling non-scalable fonts and reducing font initialization, the +`.FT2Font` constructor no longer sets a default size. Non-scalable fonts are sometimes +used for bitmap-backed emoji fonts. + +If metrics are important (i.e., if you are loading character glyphs, or setting a text +string), then explicitly call `.FT2Font.set_size` beforehand. diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index 0dc0667d0e84..b39df1f52996 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -188,8 +188,8 @@ def test_ft2font_clear(): def test_ft2font_set_size(): file = fm.findfont('DejaVu Sans') - # Default is 12pt @ 72 dpi. font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=1) + font.set_size(12, 72) font.set_text('ABabCDcd') orig = font.get_width_height() font.set_size(24, 72) @@ -757,6 +757,7 @@ def test_ft2font_get_kerning(left, right, unscaled, unfitted, default): def test_ft2font_set_text(): file = fm.findfont('DejaVu Sans') font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font.set_size(12, 72) xys = font.set_text('') np.testing.assert_array_equal(xys, np.empty((0, 2))) assert font.get_width_height() == (0, 0) @@ -778,6 +779,7 @@ def test_ft2font_set_text(): def test_ft2font_loading(): file = fm.findfont('DejaVu Sans') font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font.set_size(12, 72) for glyph in [font.load_char(ord('M')), font.load_glyph(font.get_char_index(ord('M')))]: assert glyph is not None @@ -818,11 +820,13 @@ def test_ft2font_drawing(): expected *= 255 file = fm.findfont('DejaVu Sans') font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font.set_size(12, 72) font.set_text('M') font.draw_glyphs_to_bitmap(antialiased=False) image = font.get_image() np.testing.assert_array_equal(image, expected) font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font.set_size(12, 72) glyph = font.load_char(ord('M')) image = np.zeros(expected.shape, np.uint8) font.draw_glyph_to_bitmap(image, -1, 1, glyph, antialiased=False) @@ -832,6 +836,7 @@ def test_ft2font_drawing(): def test_ft2font_get_path(): file = fm.findfont('DejaVu Sans') font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font.set_size(12, 72) vertices, codes = font.get_path() assert vertices.shape == (0, 2) assert codes.shape == (0, ) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index ca8881d98c50..1d03ecf10b56 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -221,12 +221,6 @@ FT2Font::FT2Font(FT_Open_Args &open_args, if (open_args.stream != nullptr) { face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM; } - try { - set_size(12., 72.); // Set a default fontsize 12 pt at 72dpi. - } catch (...) { - FT_Done_Face(face); - throw; - } // Set fallbacks std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks)); } From 7a628e5b752da6e5b549a5180e3e1ebb4f142b9d Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 18 Jul 2025 02:25:58 -0400 Subject: [PATCH 13/20] Deprecate font_manager.is_opentype_cff_font According to the docs, it was used for PostScript and PDF which "cannot subset those fonts". However, that is no longer true, and there are no users of this function. --- doc/api/next_api_changes/deprecations/30329-ES.rst | 4 ++++ lib/matplotlib/font_manager.py | 2 +- lib/matplotlib/tests/test_font_manager.py | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/30329-ES.rst diff --git a/doc/api/next_api_changes/deprecations/30329-ES.rst b/doc/api/next_api_changes/deprecations/30329-ES.rst new file mode 100644 index 000000000000..8d5060c4821b --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30329-ES.rst @@ -0,0 +1,4 @@ +``font_manager.is_opentype_cff_font`` is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There is no replacement. diff --git a/lib/matplotlib/font_manager.py b/lib/matplotlib/font_manager.py index ab6b495631de..79e088b85998 100644 --- a/lib/matplotlib/font_manager.py +++ b/lib/matplotlib/font_manager.py @@ -1539,7 +1539,7 @@ def _findfont_cached(self, prop, fontext, directory, fallback_to_default, return _cached_realpath(result) -@lru_cache +@_api.deprecated("3.11") def is_opentype_cff_font(filename): """ Return whether the given font is a Postscript Compact Font Format Font diff --git a/lib/matplotlib/tests/test_font_manager.py b/lib/matplotlib/tests/test_font_manager.py index 24421b8e30b3..b15647644e04 100644 --- a/lib/matplotlib/tests/test_font_manager.py +++ b/lib/matplotlib/tests/test_font_manager.py @@ -67,12 +67,14 @@ def test_json_serialization(tmp_path): def test_otf(): fname = '/usr/share/fonts/opentype/freefont/FreeMono.otf' if Path(fname).exists(): - assert is_opentype_cff_font(fname) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + assert is_opentype_cff_font(fname) for f in fontManager.ttflist: if 'otf' in f.fname: with open(f.fname, 'rb') as fd: res = fd.read(4) == b'OTTO' - assert res == is_opentype_cff_font(f.fname) + with pytest.warns(mpl.MatplotlibDeprecationWarning): + assert res == is_opentype_cff_font(f.fname) @pytest.mark.skipif(sys.platform == "win32" or not has_fclist, From 42c108a850fb88e78b9a3d990d826ce536a0d6e0 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 16 Jul 2025 20:43:18 -0400 Subject: [PATCH 14/20] Deprecate setting text kerning factor to any non-None value This factor existed only to preserve test images, but as of #29816, it is set to 0 (i.e., disabled and providing default behaviour). In the future, with libraqm, it will have no effect no matter its setting (because we won't be applying kerning ourselves at all.) --- .../deprecations/30322-ES.rst | 7 +++++ doc/users/prev_whats_new/whats_new_3.2.0.rst | 29 ++++++------------- lib/matplotlib/__init__.py | 2 ++ lib/matplotlib/ft2font.pyi | 2 +- lib/matplotlib/mpl-data/matplotlibrc | 7 ++--- lib/matplotlib/rcsetup.py | 2 +- lib/matplotlib/tests/test_ft2font.py | 20 ++++++++----- src/ft2font_wrapper.cpp | 20 +++++++------ 8 files changed, 47 insertions(+), 42 deletions(-) create mode 100644 doc/api/next_api_changes/deprecations/30322-ES.rst diff --git a/doc/api/next_api_changes/deprecations/30322-ES.rst b/doc/api/next_api_changes/deprecations/30322-ES.rst new file mode 100644 index 000000000000..b9c4964e58c8 --- /dev/null +++ b/doc/api/next_api_changes/deprecations/30322-ES.rst @@ -0,0 +1,7 @@ +Font kerning factor is deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Due to internal changes to support complex text rendering, the kerning factor on fonts is +no longer used. Setting the ``text.kerning_factor`` rcParam (which existed only for +backwards-compatibility) to any value other than None is deprecated, and the rcParam will +be removed in the future. diff --git a/doc/users/prev_whats_new/whats_new_3.2.0.rst b/doc/users/prev_whats_new/whats_new_3.2.0.rst index 12d7fab3af90..3519245642a8 100644 --- a/doc/users/prev_whats_new/whats_new_3.2.0.rst +++ b/doc/users/prev_whats_new/whats_new_3.2.0.rst @@ -52,26 +52,15 @@ triangle meshes. Kerning adjustments now use correct values ------------------------------------------ -Due to an error in how kerning adjustments were applied, previous versions of -Matplotlib would under-correct kerning. This version will now correctly apply -kerning (for fonts supported by FreeType). To restore the old behavior (e.g., -for test images), you may set :rc:`text.kerning_factor` to 6 (instead of 0). -Other values have undefined behavior. - -.. plot:: - - import matplotlib.pyplot as plt - - # Use old kerning values: - plt.rcParams['text.kerning_factor'] = 6 - fig, ax = plt.subplots() - ax.text(0.0, 0.05, 'BRAVO\nAWKWARD\nVAT\nW.Test', fontsize=56) - ax.set_title('Before (text.kerning_factor = 6)') - -Note how the spacing between characters is uniform between their bounding boxes -(above). With corrected kerning (below), slanted characters (e.g., AV or VA) -will be spaced closer together, as well as various other character pairs, -depending on font support (e.g., T and e, or the period after the W). +Due to an error in how kerning adjustments were applied, previous versions of Matplotlib +would under-correct kerning. This version will now correctly apply kerning (for fonts +supported by FreeType). To restore the old behavior (e.g., for test images), you may set +the ``text.kerning_factor`` rcParam to 6 (instead of 0). Other values have undefined +behavior. + +With corrected kerning (below), slanted characters (e.g., AV or VA) will be spaced closer +together, as well as various other character pairs, depending on font support (e.g., T +and e, or the period after the W). .. plot:: diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index 008d4de77a3b..e343e60b5fa1 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -751,6 +751,8 @@ def __setitem__(self, key, val): f"a list of valid parameters)") from err except ValueError as ve: raise ValueError(f"Key {key}: {ve}") from None + if key == "text.kerning_factor" and cval is not None: + _api.warn_deprecated("3.11", name="text.kerning_factor", obj_type="rcParam") self._set(key, cval) def __getitem__(self, key): diff --git a/lib/matplotlib/ft2font.pyi b/lib/matplotlib/ft2font.pyi index a413cd3c1a76..5257893b380a 100644 --- a/lib/matplotlib/ft2font.pyi +++ b/lib/matplotlib/ft2font.pyi @@ -191,7 +191,7 @@ class FT2Font(Buffer): hinting_factor: int = ..., *, _fallback_list: list[FT2Font] | None = ..., - _kerning_factor: int = ... + _kerning_factor: int | None = ... ) -> None: ... if sys.version_info[:2] >= (3, 12): def __buffer__(self, flags: int) -> memoryview: ... diff --git a/lib/matplotlib/mpl-data/matplotlibrc b/lib/matplotlib/mpl-data/matplotlibrc index 0ab5cfadf291..fb96656c5303 100644 --- a/lib/matplotlib/mpl-data/matplotlibrc +++ b/lib/matplotlib/mpl-data/matplotlibrc @@ -306,10 +306,9 @@ #text.hinting_factor: 1 # Specifies the amount of softness for hinting in the # horizontal direction. A value of 1 will hint to full # pixels. A value of 2 will hint to half pixels etc. -#text.kerning_factor: 0 # Specifies the scaling factor for kerning values. This - # is provided solely to allow old test images to remain - # unchanged. Set to 6 to obtain previous behavior. - # Values other than 0 or 6 have no defined meaning. +#text.kerning_factor: None # Specifies the scaling factor for kerning values. Values + # other than 0, 6, or None have no defined meaning. + # This setting is deprecated. #text.antialiased: True # If True (default), the text will be antialiased. # This only affects raster outputs. #text.parse_math: True # Use mathtext if there is an even number of unescaped diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 80d25659888e..b4224d169815 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1042,7 +1042,7 @@ def _convert_validator_spec(key, conv): "text.hinting": ["default", "no_autohint", "force_autohint", "no_hinting", "auto", "native", "either", "none"], "text.hinting_factor": validate_int, - "text.kerning_factor": validate_int, + "text.kerning_factor": validate_int_or_None, "text.antialiased": validate_bool, "text.parse_math": validate_bool, diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index b39df1f52996..5dd96ce9cafe 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -168,6 +168,12 @@ def test_ft2font_invalid_args(tmp_path): # kerning_factor argument. with pytest.raises(TypeError, match='incompatible constructor arguments'): ft2font.FT2Font(file, _kerning_factor=1.3) + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='text.kerning_factor rcParam was deprecated .+ 3.11'): + mpl.rcParams['text.kerning_factor'] = 0 + with pytest.warns(mpl.MatplotlibDeprecationWarning, + match='_kerning_factor parameter was deprecated .+ 3.11'): + ft2font.FT2Font(file, _kerning_factor=123) def test_ft2font_clear(): @@ -188,7 +194,7 @@ def test_ft2font_clear(): def test_ft2font_set_size(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=1) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) font.set_text('ABabCDcd') orig = font.get_width_height() @@ -717,7 +723,7 @@ def test_ft2font_get_sfnt_table(font_name, header): def test_ft2font_get_kerning(left, right, unscaled, unfitted, default): file = fm.findfont('DejaVu Sans') # With unscaled, these settings should produce exact values found in FontForge. - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(100, 100) assert font.get_kerning(font.get_char_index(ord(left)), font.get_char_index(ord(right)), @@ -756,7 +762,7 @@ def test_ft2font_get_kerning(left, right, unscaled, unfitted, default): def test_ft2font_set_text(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) xys = font.set_text('') np.testing.assert_array_equal(xys, np.empty((0, 2))) @@ -778,7 +784,7 @@ def test_ft2font_set_text(): def test_ft2font_loading(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) for glyph in [font.load_char(ord('M')), font.load_glyph(font.get_char_index(ord('M')))]: @@ -819,13 +825,13 @@ def test_ft2font_drawing(): ]) expected *= 255 file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) font.set_text('M') font.draw_glyphs_to_bitmap(antialiased=False) image = font.get_image() np.testing.assert_array_equal(image, expected) - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) glyph = font.load_char(ord('M')) image = np.zeros(expected.shape, np.uint8) @@ -835,7 +841,7 @@ def test_ft2font_drawing(): def test_ft2font_get_path(): file = fm.findfont('DejaVu Sans') - font = ft2font.FT2Font(file, hinting_factor=1, _kerning_factor=0) + font = ft2font.FT2Font(file, hinting_factor=1) font.set_size(12, 72) vertices, codes = font.get_path() assert vertices.shape == (0, 2) diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index cb816efff9a9..5ba4bec36874 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -432,12 +432,6 @@ const char *PyFT2Font_init__doc__ = R"""( .. warning:: This API is both private and provisional: do not use it directly. - _kerning_factor : int, optional - Used to adjust the degree of kerning. - - .. warning:: - This API is private: do not use it directly. - _warn_if_used : bool, optional Used to trigger missing glyph warnings. @@ -448,11 +442,19 @@ const char *PyFT2Font_init__doc__ = R"""( static PyFT2Font * PyFT2Font_init(py::object filename, long hinting_factor = 8, std::optional> fallback_list = std::nullopt, - int kerning_factor = 0, bool warn_if_used = false) + std::optional kerning_factor = std::nullopt, + bool warn_if_used = false) { if (hinting_factor <= 0) { throw py::value_error("hinting_factor must be greater than 0"); } + if (kerning_factor) { + auto api = py::module_::import("matplotlib._api"); + auto warn = api.attr("warn_deprecated"); + warn("since"_a="3.11", "name"_a="_kerning_factor", "obj_type"_a="parameter"); + } else { + kerning_factor = 0; + } PyFT2Font *self = new PyFT2Font(); self->x = nullptr; @@ -500,7 +502,7 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, self->x = new FT2Font(open_args, hinting_factor, fallback_fonts, ft_glyph_warn, warn_if_used); - self->x->set_kerning_factor(kerning_factor); + self->x->set_kerning_factor(*kerning_factor); return self; } @@ -1605,7 +1607,7 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) PyFT2Font__doc__) .def(py::init(&PyFT2Font_init), "filename"_a, "hinting_factor"_a=8, py::kw_only(), - "_fallback_list"_a=py::none(), "_kerning_factor"_a=0, + "_fallback_list"_a=py::none(), "_kerning_factor"_a=py::none(), "_warn_if_used"_a=false, PyFT2Font_init__doc__) .def("clear", &PyFT2Font_clear, PyFT2Font_clear__doc__) From 3a0a7734466db95809e72d7912d7a075b0ee4d12 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Tue, 3 Jun 2025 18:59:00 -0400 Subject: [PATCH 15/20] TYP: Make glyph indices distinct from character codes Previously, these were both typed as `int`, which means you could mix and match them erroneously. While the character code can't be made a distinct type (because it's used for `chr`/`ord`), typing glyph indices as a distinct type means these can't be fully swapped. Unfortunately, you can still go back to the base type, so glyph indices still work as character codes. But this is still sufficient to catch errors such as the wrong call to `FT2Font.get_kerning` in `_mathtext.py`. --- .../next_api_changes/development/30143-ES.rst | 7 +++++ lib/matplotlib/_afm.py | 19 +++++++------ lib/matplotlib/_mathtext.py | 28 +++++++++---------- lib/matplotlib/_mathtext_data.py | 18 +++++++----- lib/matplotlib/_text_helpers.py | 4 +-- lib/matplotlib/dviread.pyi | 7 +++-- lib/matplotlib/ft2font.pyi | 22 +++++++++------ lib/matplotlib/tests/test_ft2font.py | 5 ++-- src/ft2font_wrapper.cpp | 3 ++ 9 files changed, 70 insertions(+), 43 deletions(-) create mode 100644 doc/api/next_api_changes/development/30143-ES.rst diff --git a/doc/api/next_api_changes/development/30143-ES.rst b/doc/api/next_api_changes/development/30143-ES.rst new file mode 100644 index 000000000000..2d79ad6bbe9d --- /dev/null +++ b/doc/api/next_api_changes/development/30143-ES.rst @@ -0,0 +1,7 @@ +Glyph indices now typed distinctly from character codes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Previously, character codes and glyph indices were both typed as `int`, which means you +could mix and match them erroneously. While the character code can't be made a distinct +type (because it's used for `chr`/`ord`), typing glyph indices as a distinct type means +these can't be fully swapped. diff --git a/lib/matplotlib/_afm.py b/lib/matplotlib/_afm.py index 352d3c42247e..3d7f7a44baca 100644 --- a/lib/matplotlib/_afm.py +++ b/lib/matplotlib/_afm.py @@ -30,9 +30,10 @@ import inspect import logging import re -from typing import BinaryIO, NamedTuple, TypedDict +from typing import BinaryIO, NamedTuple, TypedDict, cast from ._mathtext_data import uni2type1 +from .ft2font import CharacterCodeType, GlyphIndexType _log = logging.getLogger(__name__) @@ -197,7 +198,7 @@ class CharMetrics(NamedTuple): The bbox of the character (B) as a tuple (*llx*, *lly*, *urx*, *ury*).""" -def _parse_char_metrics(fh: BinaryIO) -> tuple[dict[int, CharMetrics], +def _parse_char_metrics(fh: BinaryIO) -> tuple[dict[CharacterCodeType, CharMetrics], dict[str, CharMetrics]]: """ Parse the given filehandle for character metrics information. @@ -218,7 +219,7 @@ def _parse_char_metrics(fh: BinaryIO) -> tuple[dict[int, CharMetrics], """ required_keys = {'C', 'WX', 'N', 'B'} - ascii_d: dict[int, CharMetrics] = {} + ascii_d: dict[CharacterCodeType, CharMetrics] = {} name_d: dict[str, CharMetrics] = {} for bline in fh: # We are defensively letting values be utf8. The spec requires @@ -409,19 +410,21 @@ def get_str_bbox_and_descent(self, s: str) -> tuple[int, int, float, int, int]: return left, miny, total_width, maxy - miny, -miny - def get_glyph_name(self, glyph_ind: int) -> str: # For consistency with FT2Font. + def get_glyph_name(self, # For consistency with FT2Font. + glyph_ind: GlyphIndexType) -> str: """Get the name of the glyph, i.e., ord(';') is 'semicolon'.""" - return self._metrics[glyph_ind].name + return self._metrics[cast(CharacterCodeType, glyph_ind)].name - def get_char_index(self, c: int) -> int: # For consistency with FT2Font. + def get_char_index(self, # For consistency with FT2Font. + c: CharacterCodeType) -> GlyphIndexType: """ Return the glyph index corresponding to a character code point. Note, for AFM fonts, we treat the glyph index the same as the codepoint. """ - return c + return cast(GlyphIndexType, c) - def get_width_char(self, c: int) -> float: + def get_width_char(self, c: CharacterCodeType) -> float: """Get the width of the character code from the character metric WX field.""" return self._metrics[c].width diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index 19ddbb6d0883..afaa9ade6018 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -37,7 +37,8 @@ if T.TYPE_CHECKING: from collections.abc import Iterable - from .ft2font import Glyph + from .ft2font import CharacterCodeType, Glyph + ParserElement.enable_packrat() _log = logging.getLogger("matplotlib.mathtext") @@ -47,7 +48,7 @@ # FONTS -def get_unicode_index(symbol: str) -> int: # Publicly exported. +def get_unicode_index(symbol: str) -> CharacterCodeType: # Publicly exported. r""" Return the integer index (from the Unicode table) of *symbol*. @@ -85,7 +86,7 @@ class VectorParse(NamedTuple): width: float height: float depth: float - glyphs: list[tuple[FT2Font, float, int, float, float]] + glyphs: list[tuple[FT2Font, float, CharacterCodeType, float, float]] rects: list[tuple[float, float, float, float]] VectorParse.__module__ = "matplotlib.mathtext" @@ -212,7 +213,7 @@ class FontInfo(NamedTuple): fontsize: float postscript_name: str metrics: FontMetrics - num: int + num: CharacterCodeType glyph: Glyph offset: float @@ -365,7 +366,7 @@ def _get_offset(self, font: FT2Font, glyph: Glyph, fontsize: float, return 0. def _get_glyph(self, fontname: str, font_class: str, - sym: str) -> tuple[FT2Font, int, bool]: + sym: str) -> tuple[FT2Font, CharacterCodeType, bool]: raise NotImplementedError # The return value of _get_info is cached per-instance. @@ -459,7 +460,7 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag _slanted_symbols = set(r"\int \oint".split()) def _get_glyph(self, fontname: str, font_class: str, - sym: str) -> tuple[FT2Font, int, bool]: + sym: str) -> tuple[FT2Font, CharacterCodeType, bool]: font = None if fontname in self.fontmap and sym in latex_to_bakoma: basename, num = latex_to_bakoma[sym] @@ -551,7 +552,7 @@ class UnicodeFonts(TruetypeFonts): # Some glyphs are not present in the `cmr10` font, and must be brought in # from `cmsy10`. Map the Unicode indices of those glyphs to the indices at # which they are found in `cmsy10`. - _cmr10_substitutions = { + _cmr10_substitutions: dict[CharacterCodeType, CharacterCodeType] = { 0x00D7: 0x00A3, # Multiplication sign. 0x2212: 0x00A1, # Minus sign. } @@ -594,11 +595,11 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag _slanted_symbols = set(r"\int \oint".split()) def _map_virtual_font(self, fontname: str, font_class: str, - uniindex: int) -> tuple[str, int]: + uniindex: CharacterCodeType) -> tuple[str, CharacterCodeType]: return fontname, uniindex def _get_glyph(self, fontname: str, font_class: str, - sym: str) -> tuple[FT2Font, int, bool]: + sym: str) -> tuple[FT2Font, CharacterCodeType, bool]: try: uniindex = get_unicode_index(sym) found_symbol = True @@ -607,8 +608,7 @@ def _get_glyph(self, fontname: str, font_class: str, found_symbol = False _log.warning("No TeX to Unicode mapping for %a.", sym) - fontname, uniindex = self._map_virtual_font( - fontname, font_class, uniindex) + fontname, uniindex = self._map_virtual_font(fontname, font_class, uniindex) new_fontname = fontname @@ -693,7 +693,7 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag self.fontmap[name] = fullpath def _get_glyph(self, fontname: str, font_class: str, - sym: str) -> tuple[FT2Font, int, bool]: + sym: str) -> tuple[FT2Font, CharacterCodeType, bool]: # Override prime symbol to use Bakoma. if sym == r'\prime': return self.bakoma._get_glyph(fontname, font_class, sym) @@ -783,7 +783,7 @@ def __init__(self, default_font_prop: FontProperties, load_glyph_flags: LoadFlag self.fontmap[name] = fullpath def _map_virtual_font(self, fontname: str, font_class: str, - uniindex: int) -> tuple[str, int]: + uniindex: CharacterCodeType) -> tuple[str, CharacterCodeType]: # Handle these "fonts" that are actually embedded in # other fonts. font_mapping = stix_virtual_fonts.get(fontname) @@ -1170,7 +1170,7 @@ def __init__(self, elements: T.Sequence[Node]): self.glue_sign = 0 # 0: normal, -1: shrinking, 1: stretching self.glue_order = 0 # The order of infinity (0 - 3) for the glue - def __repr__(self): + def __repr__(self) -> str: return "{}[{}]".format( super().__repr__(), self.width, self.height, diff --git a/lib/matplotlib/_mathtext_data.py b/lib/matplotlib/_mathtext_data.py index 5819ee743044..0451791e9f26 100644 --- a/lib/matplotlib/_mathtext_data.py +++ b/lib/matplotlib/_mathtext_data.py @@ -3,9 +3,12 @@ """ from __future__ import annotations -from typing import overload +from typing import TypeAlias, overload -latex_to_bakoma = { +from .ft2font import CharacterCodeType + + +latex_to_bakoma: dict[str, tuple[str, CharacterCodeType]] = { '\\__sqrt__' : ('cmex10', 0x70), '\\bigcap' : ('cmex10', 0x5c), '\\bigcup' : ('cmex10', 0x5b), @@ -241,7 +244,7 @@ # Automatically generated. -type12uni = { +type12uni: dict[str, CharacterCodeType] = { 'aring' : 229, 'quotedblright' : 8221, 'V' : 86, @@ -475,7 +478,7 @@ # for key in sd: # print("{0:24} : {1: dict[str, float]: ... @property - def index(self) -> int: ... # type: ignore[override] + def index(self) -> GlyphIndexType: ... # type: ignore[override] @property - def glyph_name_or_index(self) -> int | str: ... + def glyph_name_or_index(self) -> GlyphIndexType | str: ... class Dvi: file: io.BufferedReader diff --git a/lib/matplotlib/ft2font.pyi b/lib/matplotlib/ft2font.pyi index 5257893b380a..a4ddc84358c1 100644 --- a/lib/matplotlib/ft2font.pyi +++ b/lib/matplotlib/ft2font.pyi @@ -1,6 +1,6 @@ from enum import Enum, Flag import sys -from typing import BinaryIO, Literal, TypedDict, final, overload, cast +from typing import BinaryIO, Literal, NewType, TypeAlias, TypedDict, cast, final, overload from typing_extensions import Buffer # < Py 3.12 import numpy as np @@ -9,6 +9,12 @@ from numpy.typing import NDArray __freetype_build_type__: str __freetype_version__: str +# We can't change the type hints for standard library chr/ord, so character codes are a +# simple type alias. +CharacterCodeType: TypeAlias = int +# But glyph indices are internal, so use a distinct type hint. +GlyphIndexType = NewType('GlyphIndexType', int) + class FaceFlags(Flag): SCALABLE = cast(int, ...) FIXED_SIZES = cast(int, ...) @@ -202,13 +208,13 @@ class FT2Font(Buffer): ) -> None: ... def draw_glyphs_to_bitmap(self, antialiased: bool = ...) -> None: ... def get_bitmap_offset(self) -> tuple[int, int]: ... - def get_char_index(self, codepoint: int) -> int: ... - def get_charmap(self) -> dict[int, int]: ... + def get_char_index(self, codepoint: CharacterCodeType) -> GlyphIndexType: ... + def get_charmap(self) -> dict[CharacterCodeType, GlyphIndexType]: ... def get_descent(self) -> int: ... - def get_glyph_name(self, index: int) -> str: ... + def get_glyph_name(self, index: GlyphIndexType) -> str: ... def get_image(self) -> NDArray[np.uint8]: ... - def get_kerning(self, left: int, right: int, mode: Kerning) -> int: ... - def get_name_index(self, name: str) -> int: ... + def get_kerning(self, left: GlyphIndexType, right: GlyphIndexType, mode: Kerning) -> int: ... + def get_name_index(self, name: str) -> GlyphIndexType: ... def get_num_glyphs(self) -> int: ... def get_path(self) -> tuple[NDArray[np.float64], NDArray[np.int8]]: ... def get_ps_font_info( @@ -230,8 +236,8 @@ class FT2Font(Buffer): @overload def get_sfnt_table(self, name: Literal["pclt"]) -> _SfntPcltDict | None: ... def get_width_height(self) -> tuple[int, int]: ... - def load_char(self, charcode: int, flags: LoadFlags = ...) -> Glyph: ... - def load_glyph(self, glyphindex: int, flags: LoadFlags = ...) -> Glyph: ... + def load_char(self, charcode: CharacterCodeType, flags: LoadFlags = ...) -> Glyph: ... + def load_glyph(self, glyphindex: GlyphIndexType, flags: LoadFlags = ...) -> Glyph: ... def select_charmap(self, i: int) -> None: ... def set_charmap(self, i: int) -> None: ... def set_size(self, ptsize: float, dpi: float) -> None: ... diff --git a/lib/matplotlib/tests/test_ft2font.py b/lib/matplotlib/tests/test_ft2font.py index 5dd96ce9cafe..6b405287e5d7 100644 --- a/lib/matplotlib/tests/test_ft2font.py +++ b/lib/matplotlib/tests/test_ft2font.py @@ -1,6 +1,7 @@ import itertools import io from pathlib import Path +from typing import cast import numpy as np import pytest @@ -241,7 +242,7 @@ def enc(name): assert unic == after # This is just a random sample from FontForge. - glyph_names = { + glyph_names = cast(dict[str, ft2font.GlyphIndexType], { 'non-existent-glyph-name': 0, 'plusminus': 115, 'Racute': 278, @@ -253,7 +254,7 @@ def enc(name): 'uni2A02': 4464, 'u1D305': 5410, 'u1F0A1': 5784, - } + }) for name, index in glyph_names.items(): assert font.get_name_index(name) == index if name == 'non-existent-glyph-name': diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 5ba4bec36874..31202f018e42 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -1774,5 +1774,8 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) m.attr("__freetype_version__") = version_string; m.attr("__freetype_build_type__") = FREETYPE_BUILD_TYPE; + auto py_int = py::module_::import("builtins").attr("int"); + m.attr("CharacterCodeType") = py_int; + m.attr("GlyphIndexType") = py_int; m.def("__getattr__", ft2font__getattr__); } From c6e690489637b25e8bd883cc9299751462a1da96 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 5 Jun 2025 23:18:52 -0400 Subject: [PATCH 16/20] Fix kerning of mathtext The `FontInfo.num` value returned by `TruetypeFonts._get_info` is a character code, but `FT2Font.get_kerning` takes *glyph indices*, meaning that kerning was likely off in most cases. --- lib/matplotlib/_mathtext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index afaa9ade6018..78f8913cd65a 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -426,7 +426,9 @@ def get_kern(self, font1: str, fontclass1: str, sym1: str, fontsize1: float, info1 = self._get_info(font1, fontclass1, sym1, fontsize1, dpi) info2 = self._get_info(font2, fontclass2, sym2, fontsize2, dpi) font = info1.font - return font.get_kerning(info1.num, info2.num, Kerning.DEFAULT) / 64 + return font.get_kerning(font.get_char_index(info1.num), + font.get_char_index(info2.num), + Kerning.DEFAULT) / 64 return super().get_kern(font1, fontclass1, sym1, fontsize1, font2, fontclass2, sym2, fontsize2, dpi) From 733cd7d99a3534be25dd46cd2c05b93749ec1b39 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 24 Jul 2025 15:46:13 -0400 Subject: [PATCH 17/20] Update test images for previous changes --- .../test_mathtext/mathtext_cm_21.svg | 1476 +++++++++-------- .../test_mathtext/mathtext_cm_23.png | Bin 3144 -> 2823 bytes .../test_mathtext/mathtext_cm_23.svg | 599 +++---- .../test_mathtext/mathtext_dejavusans_21.svg | 907 +++++----- .../test_mathtext/mathtext_dejavusans_23.png | Bin 3122 -> 2822 bytes .../test_mathtext/mathtext_dejavusans_23.svg | 537 +++--- .../test_mathtext/mathtext_dejavusans_27.svg | 383 +++-- .../test_mathtext/mathtext_dejavusans_46.svg | 229 +-- .../test_mathtext/mathtext_dejavusans_49.svg | 211 +-- .../test_mathtext/mathtext_dejavusans_60.svg | 418 ++--- .../test_mathtext/mathtext_dejavuserif_21.svg | 1020 ++++++------ .../test_mathtext/mathtext_dejavuserif_23.png | Bin 3125 -> 2853 bytes .../test_mathtext/mathtext_dejavuserif_23.svg | 559 ++++--- .../test_mathtext/mathtext_dejavuserif_60.svg | 444 ++--- .../test_mathtext/mathtext_stix_21.svg | 1096 ++++++------ .../test_mathtext/mathtext_stix_23.png | Bin 3135 -> 2826 bytes .../test_mathtext/mathtext_stix_23.svg | 573 ++++--- .../test_mathtext/mathtext_stixsans_21.svg | 904 +++++----- .../test_mathtext/mathtext_stixsans_23.png | Bin 3099 -> 2808 bytes .../test_mathtext/mathtext_stixsans_23.svg | 539 +++--- 20 files changed, 5180 insertions(+), 4715 deletions(-) diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg index 6967f80a1186..a7195c665c14 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:36.846948 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,721 +26,752 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.png index 0317cb99e1c00d2d126a11b341ffe67100702976..c2076843da4aa5e8c399a405e432f818e61b17b9 100644 GIT binary patch literal 2823 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 VBU01<;(}=nVq@uGK{CI3`ybYCRIUI3 literal 3144 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 diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg index 9d57faac5f18..09dd81f56563 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_cm_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:33.031781 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,297 +26,317 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - - + - - - - + - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg index 90f9b2cec969..b6236288603d 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:40.551077 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,437 +26,467 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.png index d6802f84bfda1b1eb62f0b43e1655f44b1b690b2..5a615d92e166a98248600a46f67c6770f06c47e8 100644 GIT binary patch literal 2822 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 H&N2T6^r6IS diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg index 77ded780c3f1..4d7fdcc30954 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:40.658346 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,268 +26,288 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - + - - + - - + - + - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg index 7a7b7ec42c25..e73b1c5e872b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_27.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:39.585869 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,190 +26,202 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - + - - - + - - - + - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg index 0846b552246a..ca0439485b08 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_46.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:40.642097 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,115 +26,121 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - - - + - - - - - - +M 1381 2969 +Q 1594 3256 1914 3420 +Q 2234 3584 2584 3584 +Q 3122 3584 3439 3221 +Q 3756 2859 3756 2241 +Q 3756 1734 3570 1259 +Q 3384 784 3041 416 +Q 2816 172 2522 40 +Q 2228 -91 1906 -91 +Q 1566 -91 1316 65 +Q 1066 222 909 531 +L 806 0 +L 231 0 +L 1178 4863 +L 1753 4863 +L 1381 2969 +z +" transform="scale(0.015625)"/> + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg index 24db824fd37c..8287a2338258 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_49.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:40.896681 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,103 +26,109 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - + + - + - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg index 189491319c10..0bbef213526f 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavusans_60.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:39.508765 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,209 +26,220 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + + - + - - - + - - + - - - - - - - - - - - - - +M 1959 2075 +Q 2384 2075 2632 2365 +Q 2881 2656 2881 3163 +Q 2881 3666 2632 3958 +Q 2384 4250 1959 4250 +Q 1534 4250 1286 3958 +Q 1038 3666 1038 3163 +Q 1038 2656 1286 2365 +Q 1534 2075 1959 2075 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg index e0721c9e47a4..3e7c6dc1c42c 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:40.642033 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,493 +26,524 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_23.png index b405dd438309c2f44abc7e9ee3bbf115dd84c202..c8bae48383aa5503ea433fd5f57972daf62d20ef 100644 GIT binary patch literal 2853 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 F@L%p)Jz)R< literal 3125 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 h|4(%OSDJW7 - - + + + + + + 2025-07-24T15:42:40.725227 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,278 +26,298 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - + - - - + - - + - - - - - - - - - - - - - - - - - - - - - +M 3022 2063 +Q 3016 2534 2758 2815 +Q 2500 3097 2075 3097 +Q 1594 3097 1305 2825 +Q 1016 2553 972 2059 +L 3022 2063 +z +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg index a4fb4be582a4..cd7dfc34183b 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_dejavuserif_60.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:42.172241 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,223 +26,234 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - + - - - + - - + - + - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg index 4623754e2963..045cc829e0cf 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:38.874726 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,531 +26,562 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stix_23.png index 1923648b80a3d4e1b4098614faa2b8b4fd08c3ad..d0233a4ee7a0136ab93656d81e101a97dfb49807 100644 GIT binary patch literal 2826 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 V?H43i=Rn&AF*iots5ZQv@E=cIIIsW! literal 3135 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 he?&|Fi#1P2Z4VcyJD7HQfj - - + + + + + + 2025-07-24T15:42:38.959357 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,284 +26,304 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - - + - - + - - - + - - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg index d61317816ad6..c3dd8722b044 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_21.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:38.040182 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,435 +26,466 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.png index a86119004e62ecf23497cd90cf563911bc3679d9..6f816c2ee723ac761b49e407d6a8eea1e088d8df 100644 GIT binary patch literal 2808 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 CAyDc7 literal 3099 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 B_&ERo diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg index 4e129aa6c87d..50bdb38d37b1 100644 --- a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg +++ b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext_stixsans_23.svg @@ -1,12 +1,23 @@ - - + + + + + + 2025-07-24T15:42:38.119948 + image/svg+xml + + + Matplotlib v3.11.0.dev1119+gc6e6904896.d20250724, https://matplotlib.org/ + + + + + - + @@ -15,269 +26,289 @@ L 378 54 L 378 0 L 0 0 z -" style="fill:#ffffff;"/> +" style="fill: #ffffff"/> - - + + - + - - - + - + - - + - - - + - - + - - - - - - - - - - - - - - - - - - - - +" transform="scale(0.015625)"/> + + + + + + + + + + + + + + + + + + + + + + + + + From fa62956b9e6b66fe6637e82bcde06e1bc04c6bb7 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Wed, 16 Jul 2025 05:05:01 -0400 Subject: [PATCH 18/20] Split font opening/closing out of FT2Font constructor/destructor This makes it easier to do later refactors. --- src/ft2font.cpp | 25 ++++++++++++++++++------- src/ft2font.h | 5 +++-- src/ft2font_wrapper.cpp | 4 ++-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index 1d03ecf10b56..cc8e6f26caff 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -207,9 +206,7 @@ FT2Font::get_path(std::vector &vertices, std::vector &cod codes.push_back(CLOSEPOLY); } -FT2Font::FT2Font(FT_Open_Args &open_args, - long hinting_factor_, - std::vector &fallback_list, +FT2Font::FT2Font(long hinting_factor_, std::vector &fallback_list, FT2Font::WarnFunc warn, bool warn_if_used) : ft_glyph_warn(warn), warn_if_used(warn_if_used), image({1, 1}), face(nullptr), hinting_factor(hinting_factor_), @@ -217,22 +214,36 @@ FT2Font::FT2Font(FT_Open_Args &open_args, kerning_factor(0) { clear(); + // Set fallbacks + std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks)); +} + +FT2Font::~FT2Font() +{ + close(); +} + +void FT2Font::open(FT_Open_Args &open_args) +{ FT_CHECK(FT_Open_Face, _ft2Library, &open_args, 0, &face); if (open_args.stream != nullptr) { face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM; } - // Set fallbacks - std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks)); } -FT2Font::~FT2Font() +void FT2Font::close() { + // This should be idempotent, in case a user manually calls close before the + // destructor does. + for (auto & glyph : glyphs) { FT_Done_Glyph(glyph); } + glyphs.clear(); if (face) { FT_Done_Face(face); + face = nullptr; } } diff --git a/src/ft2font.h b/src/ft2font.h index 0881693e7557..a4443a0cd74d 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -99,10 +99,11 @@ class FT2Font typedef void (*WarnFunc)(FT_ULong charcode, std::set family_names); public: - FT2Font(FT_Open_Args &open_args, long hinting_factor, - std::vector &fallback_list, + FT2Font(long hinting_factor, std::vector &fallback_list, WarnFunc warn, bool warn_if_used); virtual ~FT2Font(); + void open(FT_Open_Args &open_args); + void close(); void clear(); void set_size(double ptsize, double dpi); void set_charmap(int i); diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 31202f018e42..3678370b4c3a 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -499,10 +499,10 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, self->stream.close = nullptr; } - self->x = new FT2Font(open_args, hinting_factor, fallback_fonts, ft_glyph_warn, + self->x = new FT2Font(hinting_factor, fallback_fonts, ft_glyph_warn, warn_if_used); - self->x->set_kerning_factor(*kerning_factor); + self->x->open(open_args); return self; } From db17bafa6111ff27640fdb3b567be9f8ceb05859 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 17 Jul 2025 01:15:38 -0400 Subject: [PATCH 19/20] Make PyFT2Font a subclass of FT2Font --- src/ft2font.cpp | 26 ++-- src/ft2font.h | 14 +-- src/ft2font_wrapper.cpp | 272 ++++++++++++++-------------------------- 3 files changed, 115 insertions(+), 197 deletions(-) diff --git a/src/ft2font.cpp b/src/ft2font.cpp index cc8e6f26caff..ebb7d5204d80 100644 --- a/src/ft2font.cpp +++ b/src/ft2font.cpp @@ -207,15 +207,13 @@ FT2Font::get_path(std::vector &vertices, std::vector &cod } FT2Font::FT2Font(long hinting_factor_, std::vector &fallback_list, - FT2Font::WarnFunc warn, bool warn_if_used) - : ft_glyph_warn(warn), warn_if_used(warn_if_used), image({1, 1}), face(nullptr), + bool warn_if_used) + : warn_if_used(warn_if_used), image({1, 1}), face(nullptr), fallbacks(fallback_list), hinting_factor(hinting_factor_), // set default kerning factor to 0, i.e., no kerning manipulation kerning_factor(0) { clear(); - // Set fallbacks - std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks)); } FT2Font::~FT2Font() @@ -234,7 +232,8 @@ void FT2Font::open(FT_Open_Args &open_args) void FT2Font::close() { // This should be idempotent, in case a user manually calls close before the - // destructor does. + // destructor does. Note for example, that PyFT2Font _does_ call this before the + // base destructor to ensure internal pointers are cleared early enough. for (auto & glyph : glyphs) { FT_Done_Glyph(glyph); @@ -544,10 +543,9 @@ FT_UInt FT2Font::get_char_index(FT_ULong charcode, bool fallback = false) return FT_Get_Char_Index(ft_object->get_face(), charcode); } -void FT2Font::get_width_height(long *width, long *height) +std::tuple FT2Font::get_width_height() { - *width = advance; - *height = bbox.yMax - bbox.yMin; + return {advance, bbox.yMax - bbox.yMin}; } long FT2Font::get_descent() @@ -555,10 +553,9 @@ long FT2Font::get_descent() return -bbox.yMin; } -void FT2Font::get_bitmap_offset(long *x, long *y) +std::tuple FT2Font::get_bitmap_offset() { - *x = bbox.xMin; - *y = 0; + return {bbox.xMin, 0}; } void FT2Font::draw_glyphs_to_bitmap(bool antialiased) @@ -607,8 +604,11 @@ void FT2Font::draw_glyph_to_bitmap( draw_bitmap(im, &bitmap->bitmap, x + bitmap->left, y); } -void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer) +std::string FT2Font::get_glyph_name(unsigned int glyph_number) { + std::string buffer; + buffer.resize(128); + if (!FT_HAS_GLYPH_NAMES(face)) { /* Note that this generated name must match the name that is generated by ttconv in ttfont_CharStrings_getname. */ @@ -625,6 +625,8 @@ void FT2Font::get_glyph_name(unsigned int glyph_number, std::string &buffer) buffer.resize(len); } } + + return buffer; } long FT2Font::get_name_index(char *name) diff --git a/src/ft2font.h b/src/ft2font.h index a4443a0cd74d..80bc490f4bad 100644 --- a/src/ft2font.h +++ b/src/ft2font.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -96,11 +97,9 @@ extern FT_Library _ft2Library; class FT2Font { - typedef void (*WarnFunc)(FT_ULong charcode, std::set family_names); - public: FT2Font(long hinting_factor, std::vector &fallback_list, - WarnFunc warn, bool warn_if_used); + bool warn_if_used); virtual ~FT2Font(); void open(FT_Open_Args &open_args); void close(); @@ -124,14 +123,14 @@ class FT2Font std::set &glyph_seen_fonts, bool override); void load_glyph(FT_UInt glyph_index, FT_Int32 flags); - void get_width_height(long *width, long *height); - void get_bitmap_offset(long *x, long *y); + std::tuple get_width_height(); + std::tuple get_bitmap_offset(); long get_descent(); void draw_glyphs_to_bitmap(bool antialiased); void draw_glyph_to_bitmap( py::array_t im, int x, int y, size_t glyphInd, bool antialiased); - void get_glyph_name(unsigned int glyph_number, std::string &buffer); + std::string get_glyph_name(unsigned int glyph_number); long get_name_index(char *name); FT_UInt get_char_index(FT_ULong charcode, bool fallback); void get_path(std::vector &vertices, std::vector &codes); @@ -167,8 +166,9 @@ class FT2Font return FT_HAS_KERNING(face); } + protected: + virtual void ft_glyph_warn(FT_ULong charcode, std::set family_names) = 0; private: - WarnFunc ft_glyph_warn; bool warn_if_used; py::array_t image; FT_Face face; diff --git a/src/ft2font_wrapper.cpp b/src/ft2font_wrapper.cpp index 3678370b4c3a..99023836b001 100644 --- a/src/ft2font_wrapper.cpp +++ b/src/ft2font_wrapper.cpp @@ -331,16 +331,35 @@ PyGlyph_get_bbox(PyGlyph *self) * FT2Font * */ -struct PyFT2Font +class PyFT2Font final : public FT2Font { - FT2Font *x; + public: + using FT2Font::FT2Font; + py::object py_file; FT_StreamRec stream; py::list fallbacks; ~PyFT2Font() { - delete this->x; + // Because destructors are called from subclass up to base class, we need to + // explicitly close the font here. Otherwise, the instance attributes here will + // be destroyed before the font itself, but those are used in the close callback. + close(); + } + + void ft_glyph_warn(FT_ULong charcode, std::set family_names) + { + std::set::iterator it = family_names.begin(); + std::stringstream ss; + ss<<*it; + while(++it != family_names.end()){ + ss<<", "<<*it; + } + + auto text_helpers = py::module_::import("matplotlib._text_helpers"); + auto warn_on_missing_glyph = text_helpers.attr("warn_on_missing_glyph"); + warn_on_missing_glyph(charcode, ss.str()); } }; @@ -402,21 +421,6 @@ close_file_callback(FT_Stream stream) PyErr_Restore(type, value, traceback); } -static void -ft_glyph_warn(FT_ULong charcode, std::set family_names) -{ - std::set::iterator it = family_names.begin(); - std::stringstream ss; - ss<<*it; - while(++it != family_names.end()){ - ss<<", "<<*it; - } - - auto text_helpers = py::module_::import("matplotlib._text_helpers"); - auto warn_on_missing_glyph = text_helpers.attr("warn_on_missing_glyph"); - warn_on_missing_glyph(charcode, ss.str()); -} - const char *PyFT2Font_init__doc__ = R"""( Parameters ---------- @@ -456,8 +460,23 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, kerning_factor = 0; } - PyFT2Font *self = new PyFT2Font(); - self->x = nullptr; + std::vector fallback_fonts; + if (fallback_list) { + // go through fallbacks to add them to our lists + std::copy(fallback_list->begin(), fallback_list->end(), + std::back_inserter(fallback_fonts)); + } + + auto self = new PyFT2Font(hinting_factor, fallback_fonts, warn_if_used); + self->set_kerning_factor(*kerning_factor); + + if (fallback_list) { + // go through fallbacks to add them to our lists + for (auto item : *fallback_list) { + self->fallbacks.append(item); + } + } + memset(&self->stream, 0, sizeof(FT_StreamRec)); self->stream.base = nullptr; self->stream.size = 0x7fffffff; // Unknown size. @@ -469,18 +488,6 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, open_args.flags = FT_OPEN_STREAM; open_args.stream = &self->stream; - std::vector fallback_fonts; - if (fallback_list) { - // go through fallbacks to add them to our lists - for (auto item : *fallback_list) { - self->fallbacks.append(item); - // Also (locally) cache the underlying FT2Font objects. As long as - // the Python objects are kept alive, these pointer are good. - FT2Font *fback = item->x; - fallback_fonts.push_back(fback); - } - } - if (py::isinstance(filename) || py::isinstance(filename)) { self->py_file = py::module_::import("io").attr("open")(filename, "rb"); self->stream.close = &close_file_callback; @@ -499,10 +506,7 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8, self->stream.close = nullptr; } - self->x = new FT2Font(hinting_factor, fallback_fonts, ft_glyph_warn, - warn_if_used); - self->x->set_kerning_factor(*kerning_factor); - self->x->open(open_args); + self->open(open_args); return self; } @@ -520,12 +524,6 @@ PyFT2Font_fname(PyFT2Font *self) const char *PyFT2Font_clear__doc__ = "Clear all the glyphs, reset for a new call to `.set_text`."; -static void -PyFT2Font_clear(PyFT2Font *self) -{ - self->x->clear(); -} - const char *PyFT2Font_set_size__doc__ = R"""( Set the size of the text. @@ -537,12 +535,6 @@ const char *PyFT2Font_set_size__doc__ = R"""( The DPI used for rendering the text. )"""; -static void -PyFT2Font_set_size(PyFT2Font *self, double ptsize, double dpi) -{ - self->x->set_size(ptsize, dpi); -} - const char *PyFT2Font_set_charmap__doc__ = R"""( Make the i-th charmap current. @@ -561,12 +553,6 @@ const char *PyFT2Font_set_charmap__doc__ = R"""( .get_charmap )"""; -static void -PyFT2Font_set_charmap(PyFT2Font *self, int i) -{ - self->x->set_charmap(i); -} - const char *PyFT2Font_select_charmap__doc__ = R"""( Select a charmap by its FT_Encoding number. @@ -585,12 +571,6 @@ const char *PyFT2Font_select_charmap__doc__ = R"""( .get_charmap )"""; -static void -PyFT2Font_select_charmap(PyFT2Font *self, unsigned long i) -{ - self->x->select_charmap(i); -} - const char *PyFT2Font_get_kerning__doc__ = R"""( Get the kerning between two glyphs. @@ -637,7 +617,7 @@ PyFT2Font_get_kerning(PyFT2Font *self, FT_UInt left, FT_UInt right, throw py::type_error("mode must be Kerning or int"); } - return self->x->get_kerning(left, right, mode); + return self->get_kerning(left, right, mode); } const char *PyFT2Font_get_fontmap__doc__ = R"""( @@ -671,7 +651,7 @@ PyFT2Font_get_fontmap(PyFT2Font *self, std::u32string text) py::object target_font; int index; - if (self->x->get_char_fallback_index(code, index)) { + if (self->get_char_fallback_index(code, index)) { if (index >= 0) { target_font = self->fallbacks[index]; } else { @@ -733,7 +713,7 @@ PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0 throw py::type_error("flags must be LoadFlags or int"); } - self->x->set_text(text, angle, static_cast(flags), xys); + self->set_text(text, angle, static_cast(flags), xys); py::ssize_t dims[] = { static_cast(xys.size()) / 2, 2 }; py::array_t result(dims); @@ -745,12 +725,6 @@ PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0 const char *PyFT2Font_get_num_glyphs__doc__ = "Return the number of loaded glyphs."; -static size_t -PyFT2Font_get_num_glyphs(PyFT2Font *self) -{ - return self->x->get_num_glyphs(); -} - const char *PyFT2Font_load_char__doc__ = R"""( Load character in current fontfile and set glyph. @@ -800,7 +774,7 @@ PyFT2Font_load_char(PyFT2Font *self, long charcode, throw py::type_error("flags must be LoadFlags or int"); } - self->x->load_char(charcode, static_cast(flags), ft_object, fallback); + self->load_char(charcode, static_cast(flags), ft_object, fallback); return PyGlyph_from_FT2Font(ft_object); } @@ -852,9 +826,9 @@ PyFT2Font_load_glyph(PyFT2Font *self, FT_UInt glyph_index, throw py::type_error("flags must be LoadFlags or int"); } - self->x->load_glyph(glyph_index, static_cast(flags)); + self->load_glyph(glyph_index, static_cast(flags)); - return PyGlyph_from_FT2Font(self->x); + return PyGlyph_from_FT2Font(self); } const char *PyFT2Font_get_width_height__doc__ = R"""( @@ -874,16 +848,6 @@ const char *PyFT2Font_get_width_height__doc__ = R"""( .get_descent )"""; -static py::tuple -PyFT2Font_get_width_height(PyFT2Font *self) -{ - long width, height; - - self->x->get_width_height(&width, &height); - - return py::make_tuple(width, height); -} - const char *PyFT2Font_get_bitmap_offset__doc__ = R"""( Get the (x, y) offset for the bitmap if ink hangs left or below (0, 0). @@ -901,16 +865,6 @@ const char *PyFT2Font_get_bitmap_offset__doc__ = R"""( .get_descent )"""; -static py::tuple -PyFT2Font_get_bitmap_offset(PyFT2Font *self) -{ - long x, y; - - self->x->get_bitmap_offset(&x, &y); - - return py::make_tuple(x, y); -} - const char *PyFT2Font_get_descent__doc__ = R"""( Get the descent of the current string set by `.set_text`. @@ -928,12 +882,6 @@ const char *PyFT2Font_get_descent__doc__ = R"""( .get_width_height )"""; -static long -PyFT2Font_get_descent(PyFT2Font *self) -{ - return self->x->get_descent(); -} - const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = R"""( Draw the glyphs that were loaded by `.set_text` to the bitmap. @@ -949,12 +897,6 @@ const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = R"""( .draw_glyph_to_bitmap )"""; -static void -PyFT2Font_draw_glyphs_to_bitmap(PyFT2Font *self, bool antialiased = true) -{ - self->x->draw_glyphs_to_bitmap(antialiased); -} - const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = R"""( Draw a single glyph to the bitmap at pixel locations x, y. @@ -989,7 +931,7 @@ PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, py::buffer &image, auto xd = _double_to_("x", vxd); auto yd = _double_to_("y", vyd); - self->x->draw_glyph_to_bitmap( + self->draw_glyph_to_bitmap( py::array_t{image}, xd, yd, glyph->glyphInd, antialiased); } @@ -1017,16 +959,6 @@ const char *PyFT2Font_get_glyph_name__doc__ = R"""( .get_name_index )"""; -static py::str -PyFT2Font_get_glyph_name(PyFT2Font *self, unsigned int glyph_number) -{ - std::string buffer; - - buffer.resize(128); - self->x->get_glyph_name(glyph_number, buffer); - return buffer; -} - const char *PyFT2Font_get_charmap__doc__ = R"""( Return a mapping of character codes to glyph indices in the font. @@ -1045,10 +977,10 @@ PyFT2Font_get_charmap(PyFT2Font *self) { py::dict charmap; FT_UInt index; - FT_ULong code = FT_Get_First_Char(self->x->get_face(), &index); + FT_ULong code = FT_Get_First_Char(self->get_face(), &index); while (index != 0) { charmap[py::cast(code)] = py::cast(index); - code = FT_Get_Next_Char(self->x->get_face(), code, &index); + code = FT_Get_Next_Char(self->get_face(), code, &index); } return charmap; } @@ -1060,6 +992,8 @@ const char *PyFT2Font_get_char_index__doc__ = R"""( ---------- codepoint : int A character code point in the current charmap (which defaults to Unicode.) + _fallback : bool + Whether to enable fallback fonts while searching for a character. Returns ------- @@ -1074,14 +1008,6 @@ const char *PyFT2Font_get_char_index__doc__ = R"""( .get_name_index )"""; -static FT_UInt -PyFT2Font_get_char_index(PyFT2Font *self, FT_ULong ccode) -{ - bool fallback = true; - - return self->x->get_char_index(ccode, fallback); -} - const char *PyFT2Font_get_sfnt__doc__ = R"""( Load the entire SFNT names table. @@ -1098,17 +1024,17 @@ const char *PyFT2Font_get_sfnt__doc__ = R"""( static py::dict PyFT2Font_get_sfnt(PyFT2Font *self) { - if (!(self->x->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { + if (!(self->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { throw py::value_error("No SFNT name table"); } - size_t count = FT_Get_Sfnt_Name_Count(self->x->get_face()); + size_t count = FT_Get_Sfnt_Name_Count(self->get_face()); py::dict names; for (FT_UInt j = 0; j < count; ++j) { FT_SfntName sfnt; - FT_Error error = FT_Get_Sfnt_Name(self->x->get_face(), j, &sfnt); + FT_Error error = FT_Get_Sfnt_Name(self->get_face(), j, &sfnt); if (error) { throw py::value_error("Could not get SFNT name"); @@ -1143,12 +1069,6 @@ const char *PyFT2Font_get_name_index__doc__ = R"""( .get_glyph_name )"""; -static long -PyFT2Font_get_name_index(PyFT2Font *self, char *glyphname) -{ - return self->x->get_name_index(glyphname); -} - const char *PyFT2Font_get_ps_font_info__doc__ = R"""( Return the information in the PS Font Info structure. @@ -1173,7 +1093,7 @@ PyFT2Font_get_ps_font_info(PyFT2Font *self) { PS_FontInfoRec fontinfo; - FT_Error error = FT_Get_PS_Font_Info(self->x->get_face(), &fontinfo); + FT_Error error = FT_Get_PS_Font_Info(self->get_face(), &fontinfo); if (error) { throw py::value_error("Could not get PS font info"); } @@ -1225,7 +1145,7 @@ PyFT2Font_get_sfnt_table(PyFT2Font *self, std::string tagname) return std::nullopt; } - void *table = FT_Get_Sfnt_Table(self->x->get_face(), tag); + void *table = FT_Get_Sfnt_Table(self->get_face(), tag); if (!table) { return std::nullopt; } @@ -1408,7 +1328,7 @@ PyFT2Font_get_path(PyFT2Font *self) std::vector vertices; std::vector codes; - self->x->get_path(vertices, codes); + self->get_path(vertices, codes); py::ssize_t length = codes.size(); py::ssize_t vertices_dims[2] = { length, 2 }; @@ -1437,12 +1357,6 @@ const char *PyFT2Font_get_image__doc__ = R"""( .get_path )"""; -static py::array -PyFT2Font_get_image(PyFT2Font *self) -{ - return self->x->get_image(); -} - const char *PyFT2Font__get_type1_encoding_vector__doc__ = R"""( Return a list mapping CharString indices of a Type 1 font to FreeType glyph indices. @@ -1454,7 +1368,7 @@ const char *PyFT2Font__get_type1_encoding_vector__doc__ = R"""( static std::array PyFT2Font__get_type1_encoding_vector(PyFT2Font *self) { - auto face = self->x->get_face(); + auto face = self->get_face(); auto indices = std::array{}; for (auto i = 0u; i < indices.size(); ++i) { auto len = FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, nullptr, 0); @@ -1610,12 +1524,12 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) "_fallback_list"_a=py::none(), "_kerning_factor"_a=py::none(), "_warn_if_used"_a=false, PyFT2Font_init__doc__) - .def("clear", &PyFT2Font_clear, PyFT2Font_clear__doc__) - .def("set_size", &PyFT2Font_set_size, "ptsize"_a, "dpi"_a, + .def("clear", &PyFT2Font::clear, PyFT2Font_clear__doc__) + .def("set_size", &PyFT2Font::set_size, "ptsize"_a, "dpi"_a, PyFT2Font_set_size__doc__) - .def("set_charmap", &PyFT2Font_set_charmap, "i"_a, + .def("set_charmap", &PyFT2Font::set_charmap, "i"_a, PyFT2Font_set_charmap__doc__) - .def("select_charmap", &PyFT2Font_select_charmap, "i"_a, + .def("select_charmap", &PyFT2Font::select_charmap, "i"_a, PyFT2Font_select_charmap__doc__) .def("get_kerning", &PyFT2Font_get_kerning, "left"_a, "right"_a, "mode"_a, PyFT2Font_get_kerning__doc__) @@ -1624,19 +1538,20 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) PyFT2Font_set_text__doc__) .def("_get_fontmap", &PyFT2Font_get_fontmap, "string"_a, PyFT2Font_get_fontmap__doc__) - .def("get_num_glyphs", &PyFT2Font_get_num_glyphs, PyFT2Font_get_num_glyphs__doc__) + .def("get_num_glyphs", &PyFT2Font::get_num_glyphs, + PyFT2Font_get_num_glyphs__doc__) .def("load_char", &PyFT2Font_load_char, "charcode"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT, PyFT2Font_load_char__doc__) .def("load_glyph", &PyFT2Font_load_glyph, "glyph_index"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT, PyFT2Font_load_glyph__doc__) - .def("get_width_height", &PyFT2Font_get_width_height, + .def("get_width_height", &PyFT2Font::get_width_height, PyFT2Font_get_width_height__doc__) - .def("get_bitmap_offset", &PyFT2Font_get_bitmap_offset, + .def("get_bitmap_offset", &PyFT2Font::get_bitmap_offset, PyFT2Font_get_bitmap_offset__doc__) - .def("get_descent", &PyFT2Font_get_descent, PyFT2Font_get_descent__doc__) - .def("draw_glyphs_to_bitmap", &PyFT2Font_draw_glyphs_to_bitmap, + .def("get_descent", &PyFT2Font::get_descent, PyFT2Font_get_descent__doc__) + .def("draw_glyphs_to_bitmap", &PyFT2Font::draw_glyphs_to_bitmap, py::kw_only(), "antialiased"_a=true, PyFT2Font_draw_glyphs_to_bitmap__doc__); // The generated docstring uses an unqualified "Buffer" as type hint, @@ -1652,26 +1567,27 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) PyFT2Font_draw_glyph_to_bitmap__doc__); } cls - .def("get_glyph_name", &PyFT2Font_get_glyph_name, "index"_a, + .def("get_glyph_name", &PyFT2Font::get_glyph_name, "index"_a, PyFT2Font_get_glyph_name__doc__) .def("get_charmap", &PyFT2Font_get_charmap, PyFT2Font_get_charmap__doc__) - .def("get_char_index", &PyFT2Font_get_char_index, "codepoint"_a, + .def("get_char_index", &PyFT2Font::get_char_index, + "codepoint"_a, py::kw_only(), "_fallback"_a=true, PyFT2Font_get_char_index__doc__) .def("get_sfnt", &PyFT2Font_get_sfnt, PyFT2Font_get_sfnt__doc__) - .def("get_name_index", &PyFT2Font_get_name_index, "name"_a, + .def("get_name_index", &PyFT2Font::get_name_index, "name"_a, PyFT2Font_get_name_index__doc__) .def("get_ps_font_info", &PyFT2Font_get_ps_font_info, PyFT2Font_get_ps_font_info__doc__) .def("get_sfnt_table", &PyFT2Font_get_sfnt_table, "name"_a, PyFT2Font_get_sfnt_table__doc__) .def("get_path", &PyFT2Font_get_path, PyFT2Font_get_path__doc__) - .def("get_image", &PyFT2Font_get_image, PyFT2Font_get_image__doc__) + .def("get_image", &PyFT2Font::get_image, PyFT2Font_get_image__doc__) .def("_get_type1_encoding_vector", &PyFT2Font__get_type1_encoding_vector, PyFT2Font__get_type1_encoding_vector__doc__) .def_property_readonly( "postscript_name", [](PyFT2Font *self) { - if (const char *name = FT_Get_Postscript_Name(self->x->get_face())) { + if (const char *name = FT_Get_Postscript_Name(self->get_face())) { return name; } else { return "UNAVAILABLE"; @@ -1679,11 +1595,11 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) }, "PostScript name of the font.") .def_property_readonly( "num_faces", [](PyFT2Font *self) { - return self->x->get_face()->num_faces; + return self->get_face()->num_faces; }, "Number of faces in file.") .def_property_readonly( "family_name", [](PyFT2Font *self) { - if (const char *name = self->x->get_face()->family_name) { + if (const char *name = self->get_face()->family_name) { return name; } else { return "UNAVAILABLE"; @@ -1691,7 +1607,7 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) }, "Face family name.") .def_property_readonly( "style_name", [](PyFT2Font *self) { - if (const char *name = self->x->get_face()->style_name) { + if (const char *name = self->get_face()->style_name) { return name; } else { return "UNAVAILABLE"; @@ -1699,77 +1615,77 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) }, "Style name.") .def_property_readonly( "face_flags", [](PyFT2Font *self) { - return static_cast(self->x->get_face()->face_flags); + return static_cast(self->get_face()->face_flags); }, "Face flags; see `.FaceFlags`.") .def_property_readonly( "style_flags", [](PyFT2Font *self) { - return static_cast(self->x->get_face()->style_flags & 0xffff); + return static_cast(self->get_face()->style_flags & 0xffff); }, "Style flags; see `.StyleFlags`.") .def_property_readonly( "num_named_instances", [](PyFT2Font *self) { - return (self->x->get_face()->style_flags & 0x7fff0000) >> 16; + return (self->get_face()->style_flags & 0x7fff0000) >> 16; }, "Number of named instances in the face.") .def_property_readonly( "num_glyphs", [](PyFT2Font *self) { - return self->x->get_face()->num_glyphs; + return self->get_face()->num_glyphs; }, "Number of glyphs in the face.") .def_property_readonly( "num_fixed_sizes", [](PyFT2Font *self) { - return self->x->get_face()->num_fixed_sizes; + return self->get_face()->num_fixed_sizes; }, "Number of bitmap in the face.") .def_property_readonly( "num_charmaps", [](PyFT2Font *self) { - return self->x->get_face()->num_charmaps; + return self->get_face()->num_charmaps; }, "Number of charmaps in the face.") .def_property_readonly( "scalable", [](PyFT2Font *self) { - return bool(FT_IS_SCALABLE(self->x->get_face())); + return bool(FT_IS_SCALABLE(self->get_face())); }, "Whether face is scalable; attributes after this one " "are only defined for scalable faces.") .def_property_readonly( "units_per_EM", [](PyFT2Font *self) { - return self->x->get_face()->units_per_EM; + return self->get_face()->units_per_EM; }, "Number of font units covered by the EM.") .def_property_readonly( "bbox", [](PyFT2Font *self) { - FT_BBox bbox = self->x->get_face()->bbox; + FT_BBox bbox = self->get_face()->bbox; return py::make_tuple(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax); }, "Face global bounding box (xmin, ymin, xmax, ymax).") .def_property_readonly( "ascender", [](PyFT2Font *self) { - return self->x->get_face()->ascender; + return self->get_face()->ascender; }, "Ascender in 26.6 units.") .def_property_readonly( "descender", [](PyFT2Font *self) { - return self->x->get_face()->descender; + return self->get_face()->descender; }, "Descender in 26.6 units.") .def_property_readonly( "height", [](PyFT2Font *self) { - return self->x->get_face()->height; + return self->get_face()->height; }, "Height in 26.6 units; used to compute a default line spacing " "(baseline-to-baseline distance).") .def_property_readonly( "max_advance_width", [](PyFT2Font *self) { - return self->x->get_face()->max_advance_width; + return self->get_face()->max_advance_width; }, "Maximum horizontal cursor advance for all glyphs.") .def_property_readonly( "max_advance_height", [](PyFT2Font *self) { - return self->x->get_face()->max_advance_height; + return self->get_face()->max_advance_height; }, "Maximum vertical cursor advance for all glyphs.") .def_property_readonly( "underline_position", [](PyFT2Font *self) { - return self->x->get_face()->underline_position; + return self->get_face()->underline_position; }, "Vertical position of the underline bar.") .def_property_readonly( "underline_thickness", [](PyFT2Font *self) { - return self->x->get_face()->underline_thickness; + return self->get_face()->underline_thickness; }, "Thickness of the underline bar.") .def_property_readonly( "fname", &PyFT2Font_fname, "The original filename for this object.") .def_buffer([](PyFT2Font &self) -> py::buffer_info { - return self.x->get_image().request(); + return self.get_image().request(); }); m.attr("__freetype_version__") = version_string; From ad32f0dd9aa8d7c3974ff6d25e2dbabb4f986cc6 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 21 Aug 2025 17:18:13 -0400 Subject: [PATCH 20/20] DOC: Fix missing references on text-overhaul branch I thought this was because this branch was missing d2d969ef9d01297728c15c0fdfa957852201834b, but it was actually a change introduced in #30324 as a separate (but similar-looking) issue. --- doc/missing-references.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/missing-references.json b/doc/missing-references.json index 1a3693c990e5..e89439b90483 100644 --- a/doc/missing-references.json +++ b/doc/missing-references.json @@ -126,10 +126,12 @@ "doc/docstring of matplotlib.ft2font.PyCapsule.set_text:1" ], "numpy.typing.NDArray": [ + "doc/docstring of matplotlib.ft2font.pybind11_detail_function_record_v1_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1.get_image:1", "doc/docstring of matplotlib.ft2font.pybind11_detail_function_record_v1_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1.set_text:1" ], "numpy.uint8": [ - ":1" + ":1", + "doc/docstring of matplotlib.ft2font.pybind11_detail_function_record_v1_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1.get_image:1" ] }, "py:obj": {