Since 3.11, text is shaped with libraqm, but the paragraph direction is always auto-detected from
the first strong character. An Urdu (or Arabic, Hebrew) sentence that starts with a Latin word, a
product name or a version number is therefore laid out left-to-right, so its words and punctuation
come out in the wrong order. There is currently no way to set the direction.
Code for reproduction
importmatplotlib.pyplotasplttext="Python اور matplotlib میں اردو؟"# Urdu sentence that starts with an English wordfig=plt.figure(figsize=(6, 1.6))
fig.text(0.95, 0.70, text, ha="right", fontsize=18, language="ur")
fig.text(0.95, 0.25, ""+text, ha="right", fontsize=18, language="ur") # RLM prefixfig.savefig("repro_direction.png", dpi=100)
Actual outcome
The top line is laid out left-to-right: "Python" is on the left and the Urdu question mark ؟ ends up
in the middle of the line instead of at the end of the sentence.
Expected outcome
The bottom line, where the only change is a U+200F RIGHT-TO-LEFT MARK prefix: the sentence reads
right-to-left, "Python" is on the right where the sentence starts, and ؟ is at the end.
Additional information
FT2Font::layout in src/ft2font.cpp sets the text, face, features and language on the raqm
object but never calls raqm_set_par_direction, so libraqm uses RAQM_DIRECTION_DEFAULT. The
libraqm documentation for that function notes that this default "has problems when a mainly
right-to-left paragraph starts with a left-to-right character and vice versa as the detected
paragraph direction will be the wrong one". The same is true on main.
Passing language="ur" does not change the direction.
The reverse also happens: a left-to-right label whose first letter is Urdu is laid out
right-to-left.
Workaround: prefix the string with U+200F (RLM) or U+061C (ALM), or wrap it in U+2067 ... U+2069
(RLI ... PDI).
This is common in Urdu text: technical terms, names and numbers are often written in Latin script
at the start of a sentence.
A second image shows the same sentence in Noto Nastaliq Urdu, as is, with each direction mark,
and an Urdu-first sentence for comparison.
Possible fix: a direction text property ("auto" by default, "rtl", "ltr"), passed down to
raqm_set_par_direction the same way language is passed to raqm_set_language. I would be
happy to work on a PR if that approach sounds acceptable.
There're 2 orders of magnitude more possible language codes, and they also support subtags extending it even more, so it didn't make sense to use an Enum there. There are only 4 options for the direction setting, so an Enum works better.
In the past, we have used StrEnum mostly for compatibility with previous code where we used a plain string. For completely new ones, it's been a bit of a mixed bag, but I think it depended on the underlying use case (BlendMode was string originally, and remained so, while ft2font Enums are translations of FreeType API and are ints). At the lower level, it definitely doesn't need to be a string, but I'm not sure how we wish to expose it in. e.g., text.
Thanks both! Here's a concrete proposal, mirroring how language is plumbed:
Low level: a new ft2font.Direction Enum (DEFAULT, LTR, RTL, TTB) mapping directly to raqm_direction_t, and a direction parameter on FT2Font.set_text next to language.
High level: Text.set_direction / get_direction, a direction= keyword wherever language= is accepted, and a text.direction rcParam defaulting to auto. Text would accept the Enum, and also "auto" / "ltr" / "rtl" strings for consistency with language, converted at the boundary.
Activity
QuLogic commented on Oct 21, 2026
That seems reasonable, but I think we would use an Enum, not arbitrary strings.
iccir commented on Oct 21, 2026
Wouldn't we want a string to match existing APIs such as matplotlib.text or the text.language rcparam? Maybe a StrEnum?
I'd imagine that each API which accepts an optional language parameter would need a direction parameter as well. A search for BCP47 shows several.
QuLogic commented on Oct 21, 2026
There're 2 orders of magnitude more possible language codes, and they also support subtags extending it even more, so it didn't make sense to use an Enum there. There are only 4 options for the direction setting, so an Enum works better.
In the past, we have used StrEnum mostly for compatibility with previous code where we used a plain string. For completely new ones, it's been a bit of a mixed bag, but I think it depended on the underlying use case (BlendMode was string originally, and remained so, while ft2font Enums are translations of FreeType API and are ints). At the lower level, it definitely doesn't need to be a string, but I'm not sure how we wish to expose it in. e.g., text.
msarmadsohail commented on Oct 22, 2026
Thanks both! Here's a concrete proposal, mirroring how language is plumbed:
Does that sound right? If so, I'll start on a PR.