8000 [Bug]: Style flag errors trying to save figures as PDF with font Inter · Issue #29396 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

[Bug]: Style flag errors trying to save figures as PDF with font Inter #29396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ohshitgorillas opened this issue Jan 3, 2025 · 3 comments · Fixed by #29431
Closed

[Bug]: Style flag errors trying to save figures as PDF with font Inter #29396

ohshitgorillas opened this issue Jan 3, 2025 · 3 comments · Fixed by #29431
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. status: confirmed bug topic: text/fonts
Milestone

Comments

@ohshitgorillas
Copy link
ohshitgorillas commented Jan 3, 2025

Bug summary

I have installed the font Inter with brew install font-inter and successfully imported it into matplotlib such that the figure from the plot below displays correctly with the Inter font as specified; however, when it comes to saving, I get the error below in "actual outcome".

Code for reproduction

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

matplotlib.use("QtAgg")
# Ensure 'Inter' is available
available_fonts = [f.name for f in fm.fontManager.ttflist]
if 'Inter' in available_fonts:
    plt.rcParams['font.family'] = 'Inter'
else:
    print("Inter font is not available. Please ensure it is installed.")

# generate a test plot and save it
fig, ax = plt.subplots()
ax.plot([0, 1], [0, 1])
ax.set_title("Example Plot with Inter Font")
plt.show()
fig.savefig("example_plot.pdf", format='pdf')

Actual outcome

Traceback (most recent call last):
  File "/Users/atom/hemanpro/HeMan/misc_tools/addfont.py", line 18, in <module>
    fig.savefig("example_plot.pdf", format='pdf')
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/figure.py", line 3490, in savefig
    self.canvas.print_figure(fname, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/backends/backend_qtagg.py", line 75, in print_figure
    super().print_figure(*args, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/backend_bases.py", line 2184, in print_figure
    result = print_method(
        filename,
    ...<3 lines>...
        bbox_inches_restore=_bbox_inches_restore,
        **kwargs)
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/backend_bases.py", line 2040, in <lambda>
    print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
                                                                 ~~~~^
        *args, **{k: v for k, v in kwargs.items() if k not in skip}))
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/backends/backend_pdf.py", line 2789, in print_pdf
    file.finalize()
    ~~~~~~~~~~~~~^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/backends/backend_pdf.py", line 827, in finalize
    self.writeFonts()
    ~~~~~~~~~~~~~~~^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/backends/backend_pdf.py", line 973, in writeFonts
    fonts[Fx] = self.embedTTF(filename, chars)
                ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/backends/backend_pdf.py", line 1416, in embedTTF
    sf = font.style_flags
         ^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.13/3.13.1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/enum.py", line 726, in __call__
    return cls.__new__(cls, value)
           ~~~~~~~~~~~^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.13/3.13.1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/enum.py", line 1207, in __new__
    raise exc
  File "/opt/homebrew/Cellar/python@3.13/3.13.1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/enum.py", line 1184, in __new__
    result = cls._missing_(value)
  File "/opt/homebrew/Cellar/python@3.13/3.13.1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/enum.py", line 1480, in _missing_
    raise ValueError(
    ...<2 lines>...
                ))
ValueError: <flag 'StyleFlags'> invalid value 589824
    given 0b0 10010000000000000000
  allowed 0b0 00000000000000000011
.venvFAIL

Expected outcome

The figure is saved as a PDF with the Inter font.

Additional information

This only occurs on my macOS installation of Python 3.13.1 with matplotlib 3.10.0.

Operating system

macOS Sequoia 15.2

Matplotlib Version

3.10.0

Matplotlib Backend

QtAgg, cairo, macosx

Python version

3.13.1

Jupyter version

No response

Installation

pip

@anntzer
Copy link
Contributor
anntzer commented Jan 4, 2025

Note for repro: to trigger the bug it may be necessary to use the explicit path to the "bad" (actually, variable) font, i.e. something like

from pylab import *; from pathlib import Path
figtext(.5, .5, "foo", font=Path("/path/to/InterVariable.ttf"))  # or font="Inter Variable" also seems to "work" here
savefig("/tmp/test.pdf")

but the bug is valid.

It basically arises because of the conversion of FT_STYLE_FLAG_XXX to enum.Flag (regression in #28842), which means that any unknown value in style_flags raises the error seen above. Note that such values are explicitly allowed by the FreeType docs (https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_facerec): "[Since 2.6.1] Bits 16-30 hold the number of named instances available for the current face if we have a GX or OpenType variation (sub)font. Bit 31 is always zero (that is, style_flags is always a positive value). Note that a variation font has always at least one named instance, namely the default instance."

There's a few fixes possible there:

  • switch the use of enum.Flag to enum.IntFlag, which allows "unregistered" high bits; probably the easiest fix by far.
  • or, on Py3.11+ only, use boundary=KEEP when declaring the enum (the easiest way to do so with the current pybind11 enum machinery is probably p11x::enums["StyleFlags"].attr("_boundary_") = py::module::import("enum").attr("KEEP"); at the same place as where the enum docs are set, but one can probably also adjust the P11X_DECLARE_ENUM macro to avoid touching at semi-internal APIs if desired). However I don't think there's an equivalent available on Py3.10.
  • or, split out bits 16-30 of style_flags (per the documentation) and store them in a separate attribute, as they are semantically separate.

Note that face_flags likely suffers from the same issue; at least, the commented out FaceFlag values in ft2font_wrapper.cpp's P11X_DECLARE_ENUM("FaceFlags", ...) should probably be ifdef'd as done in DECLARE_FLAG just above.

@anntzer anntzer added the Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. label Jan 4, 2025
@anntzer anntzer added this to the v3.10.1 milestone Jan 4, 2025
@QuLogic
Copy link
Member
QuLogic commented Jan 8, 2025

Note that face_flags likely suffers from the same issue; at least, the commented out FaceFlag values in ft2font_wrapper.cpp's P11X_DECLARE_ENUM("FaceFlags", ...) should probably be ifdef'd as done in DECLARE_FLAG just above.

These can't be #ifdef because P11X_DECLARE_ENUM is a macro invocation. If we could rewrite that as some kind of template, then it might be possible, but not as a macro.

@anntzer
Copy link
Contributor
anntzer commented Jan 8, 2025

Good catch. The following appears to work, though:

#ifdef FT_FACE_FLAG_VARIATION
    #define FACE_FLAG_VARIATION {"VARIATION", FaceFlags::VARIATION},
#else
    #define FACE_FLAG_VARIATION
#endif
P11X_DECLARE_ENUM(
    "FaceFlags", "Flag",
    {"SCALABLE", FaceFlags::SCALABLE},
    {"FIXED_SIZES", FaceFlags::FIXED_SIZES},
    {"FIXED_WIDTH", FaceFlags::FIXED_WIDTH},
    {"SFNT", FaceFlags::SFNT},
    {"HORIZONTAL", FaceFlags::HORIZONTAL},
    {"VERTICAL", FaceFlags::VERTICAL},
    {"KERNING", FaceFlags::KERNING},
    {"FAST_GLYPHS", FaceFlags::FAST_GLYPHS},
    {"MULTIPLE_MASTERS", FaceFlags::MULTIPLE_MASTERS},
    {"GLYPH_NAMES", FaceFlags::GLYPH_NAMES},
    {"EXTERNAL_STREAM", FaceFlags::EXTERNAL_STREAM},
    {"HINTER", FaceFlags::HINTER},
    {"CID_KEYED", FaceFlags::CID_KEYED},
    {"TRICKY", FaceFlags::TRICKY},
    {"COLOR", FaceFlags::COLOR},
    FACE_FLAG_VARIATION  // backcompat: ft 2.9.0.
    // ditto for the others
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. status: confirmed bug topic: text/fonts
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants
0