fonttools / fontbakery

🧁 A font quality assurance tool for everyone
https://fontbakery.readthedocs.io
Apache License 2.0
534 stars 99 forks source link

CFF checks produce errors in Python 3.12 with multithreading #4722

Open dscorbett opened 1 month ago

dscorbett commented 1 month ago

Observed behavior

Font Bakery with multithreading enabled in Python 3.12 sometimes produces errors for fonts with 'CFF ' tables when running checks that parse the glyphs’ charstrings. The errors happen frequently but not every time. I don’t know why it only seems to happen in Python 3.12.

The problem is that SimpleT2Decompiler.execute in fontTools is not thread-safe. The decompiler keeps track of the current index in the charstring. A charstring has two representations, bytecode and decompiled, which are not the same length. A SimpleT2Decompiler might use a bytecode-oriented index to index into the decompiled charstring if another thread happens to decompile it concurrently, thereby effectively skipping ahead in the charstring by an unpredictable amount.

Expected behavior

Font Bakery’s checks should not cause errors in other checks when multithreading is enabled.

A solution could be to decompile all the charstrings eagerly before anything starts indexing into them, similar to #4683. However, I don’t know if the proper fix belongs in fontTools or Font Bakery.

Resources and steps needed to reproduce

curl -LO https://github.com/notofonts/latin-greek-cyrillic/releases/download/NotoSans-v2.013/NotoSans-v2.013.zip
unzip NotoSans-v2.013.zip
python3.12 -m venv venv
. venv/bin/activate
pip install 'fontbakery[notofonts]==0.12.6'
fontbakery check-notofonts -j \
    -c com.adobe.fonts/check/cff2_call_depth \
    -c com.adobe.fonts/check/cff_call_depth \
    -c com.adobe.fonts/check/cff_deprecated_operators \
    -c com.google.fonts/check/unitsperem \
    -c com.google.fonts/check/maxadvancewidth \
    -c com.google.fonts/check/points_out_of_bounds \
    -c com.google.fonts/check/superfamily/vertical_metrics \
    -c com.google.fonts/check/contour_count \
    -c com.google.fonts/check/interpolation_issues \
    -c com.google.fonts/check/outline_direction \
    -c com.google.fonts/check/outline_colinear_vectors \
    -c com.google.fonts/check/outline_short_segments \
    -c com.google.fonts/check/outline_semi_vertical \
    -c com.google.fonts/check/outline_jaggy_segments \
    -c com.google.fonts/check/outline_alignment_miss \
    NotoSans/unhinted/otf/NotoSans-Regular.otf