← Syamjith NK

7 September 2026 · Syamjith NK

Your Pillow version does not tell you whether Arabic will work

Here is a bug report I have now written a version of three times, and read versions of many more:

"Arabic renders correctly on my machine and comes out broken in CI. Same code. Same Pillow version. Same font file."

The instinct is to suspect the font, then the encoding, then something exotic about the container. It is none of those. It is that the thing which decides whether Pillow shapes Arabic is not in the Python package at all.

What actually decides it

Pillow can lay out complex scripts — Arabic, Hebrew, Indic — only when it was compiled against libraqm. Raqm is a C library that wraps HarfBuzz and FriBidi; it does the joining and the bidirectional reordering that Arabic requires.

That linkage is decided when the wheel is built, on the machine that built it, according to which system libraries were present. It is not a Pillow feature flag, it is not a version, and it is not something pip resolves for you.

So pillow==11.0.0 is not one behaviour. It is two, and the version number cannot distinguish them.

environmentsame wheel, same code
Raqm presentArabic joins and reorders correctly
Raqm absentletters drawn in isolated forms, logical order

Both produce an image. Neither raises. If you do not read Arabic, both look like Arabic.

The one line that answers it

from PIL import features
features.check("raqm")   # True or False, in THIS environment

The important word is this. Not the environment you developed in, not the one in your lockfile, not the one the docs were written against. Run it in the process that actually draws the image, and print it in your logs. A Docker base image change, a switch from a manylinux wheel to a source build, a different CI runner, a colleague on a Mac — any of those can flip it, and none of them touch a line of your code.

Why this compounds into a real bug

On its own, a missing Raqm gives you visibly disconnected Arabic. Someone who reads the language spots it in a second. The trouble starts when it meets the other Arabic trap: the arabic_reshaper + python-bidi pre-processing recipe that every tutorial still recommends.

Those two facts point in opposite directions:

So the correct code depends on a property of the machine, and the failure modes are mirror images. That is why "works on my machine" is not lazy shorthand here — it is a precise and complete description of the bug.

What I filed

Pillow #9925 adds a run-time warning when pre-shaped text is handed to a Raqm-enabled build, and documents that the layout engine is a build-time decision rather than a version.

My first draft was worse than what is there now. I framed part of it as a security concern, and the maintainer pushed back on that — correctly. I dropped it. What remains is narrower and true, which is the only useful kind of bug report.

If you draw Arabic in Python