Skip to content
View Syamjith-NK's full-sized avatar

Block or report Syamjith-NK

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
Syamjith-NK/README.md

Syamjith NK

Cinematographer and AI creative technologist in Abu Dhabi. I work where film craft meets applied AI, and I publish open evaluation of the Arabic tooling that production depends on.

Checkable in ten seconds, without taking my word for anything:

🟣 Merged into matplotlib #32263, 4 Sep 2026. A documentation change, thirty lines: the upgrade guide now tells 3.11 upgraders to remove the Arabic workaround. The research behind it is the substance, not the diff.
Accepted answer, 15,226 views Matplotlib: Writing right-to-left text — the canonical question, asked thirteen years ago. Every prior answer predates 3.11 and now reverses your text.
📦 pip install arabic-lint PyPI — finds Arabic corrupted before it was stored, and now the source code that will corrupt it at render time. Zero dependencies, CI-ready.
📊 Four open datasets huggingface.co/syamjithnk — CC BY 4.0, test sets, scorers and raw results included.
🔬 Corpus audit: 341 Arabic datasets arabic-corpus-audit — 276 readable, 119,517 text fields. One is 100% corrupted, and it is an OCR ground-truth set.

Full write-up with the measurements: syamjithnk.com/evidence

Arabic breaks silently — three benchmarks and an audit

Each measures a failure that looks correct to anyone who does not read Arabic, which is exactly why it ships. All CC BY 4.0, with the test set, the scorer, the raw per-item results, and a plain statement of what the measurement does not establish.

finding
arnum-tts The same Arabic sentence with Arabic-Indic digits (٢٠٢٦) instead of Western (2026) drops from 73% to 7% intelligible on one engine. Another handles both at 80% — a missing normalisation step, not a hard problem.
arshape The arabic_reshaper + python-bidi recipe recommended in nearly every tutorial corrupts Arabic on any renderer that already shapes: 15/15 correct without it, 0/15 with it.
arpdf Reversed, ligature-mangled and space-collapsed are three distinct failure modes that a single pass/fail test collapses into one.

Datasets: huggingface.co/syamjithnk

The fix, not just the finding

The numeral failure above is now a package anyone can install:

pip install arabic-tts-frontend

arabic-tts-frontend · PyPI — numerals, dates, currency, times and percentages converted to spoken Arabic before the text reaches a TTS engine. MIT, zero dependencies. Re-measured against the released build on the same 45 sentences, same engine and scorer: Arabic-Indic digits 0/15 → 11/15. The scorer ships with it, so the figures can be re-derived rather than trusted, and the release notes state the run-to-run noise (±3) instead of quoting the overall number precisely.

Writing

The Arabic fix everyone recommends is now the bug — the get_display(reshape(...)) recipe is in 1,180 indexed files on GitHub and now renders Arabic backwards on matplotlib 3.11, silently. Why the shaping half is detectable and the reordering half is not, and what happened when I filed it upstream.

More at syamjith-nk.github.io.

Upstream

Filing the finding where the bug lives has reached far more people than publishing it ever did.

matplotlib — matplotlib 3.11 shapes Arabic itself, so the reshape+bidi recipe now runs twice and renders the label reversed. Nothing raises; it looks like Arabic to anyone who cannot read it. Measured as mean absolute pixel difference against a reference render: 7.75 for the raw logical string (antialiasing only), 76.28 pre-shaped. Three maintainers replied within two days, including the project lead — and one of them went and edited a StackOverflow answer to say the workaround is only for older versions (q47057509, 30 August 2026). To be exact: that happened on one of the two big questions, not both. The canonical one still carried the pre-3.11 advice, so I answered it there myself, and the asker accepted it. That is the widest-reaching outcome of any of this and it cost nothing but filing accurately. Docs PR #32263 was merged on 4 September 2026.

The project lead proposed a fix I had not thought of — wrapping pre-processed text in Unicode LRO/PDF — and said he was not confident it was right for Arabic. I could check, so I did: across three fonts and nine strings it reads correctly in all of them, but does not always render identically, because the wrapped string draws the font's static presentation forms instead of the font's own shaping. That exchange is the argument for filing upstream rather than blogging first. He had the better fix; I had the only way to verify it.

Pillow — the same class of failure, different mechanism: Pillow's behaviour depends on whether it was built with Raqm, so the identical script produces correct text on one machine and broken text on another. On a Raqm-enabled build, 0 of 15 strings survive the recipe unchanged. The maintainer pushed back on my first draft for overclaiming a security angle. He was right, I dropped it, and the warning that remains is narrower and true.

python-arabic-reshaperissue #102 and PR #103, adding the PIL.features.check("raqm") test to the README so readers can tell which case they are in. Still unanswered.

The linter

arabic-lintpip install arabic-lint. Finds Arabic that was corrupted before it was stored, in JSON, localisation files, database exports and source. Zero dependencies, exit code 1 on a finding, so it drops into CI unchanged.

The current release adds a source check: the recipe appears in 3,168 indexed Python files, and it reports only the ones where a renderer that already shapes is actually being drawn to. ReportLab, non-Raqm Pillow, terminal output and dead helpers stay silent, because a checker that flagged all 3,168 would be switched off in a day. I read six real projects to build it: three were broken and I filed those (1, 2, 3, 4); three were correct and are documented in the write-up.

Since then it has gained --doctor, which reports whether your matplotlib and Pillow shape text so you know which case you are in; --fix, which rewrites the source findings that are mechanically safe and refuses the ones that are not; and severity, because across 276 audited datasets 361 of 363 findings were a single pasted glyph rather than a destroyed corpus, and those need different alarms.

Detection is asymmetric, which is why this survived years of being copied: the shaping half leaves presentation-form codepoints that correctly authored Arabic never contains, so it is detectable — but the reordering half produces identical codepoints in a different order, so no general detector exists for it. And the damage is not cleanly reversible: lam-alef is one codepoint that decomposes to two, in logical order, while the text around it is in visual order. الإمارات goes 8 codepoints to 7, and the obvious NFKC repair turns السلام into السالم — a real but different word. The tool refuses to auto-fix those rather than guess.

How I work

In two of the three benchmarks the measurement contradicted the assumption I started with, and the write-ups say so. In arshape a pass/fail scorer reported a broken configuration as working; adding a middle band is what surfaced the real result. Designing a test that can prove you wrong is most of the work.

When I found four faults in my own scorer I published a documented correction with old-to-new figures rather than editing the numbers quietly.

Also

Brand and product films, VFX compositing, and AI-assisted image pipelines where the craft still governs the tool — a generated frame is judged on light, framing and rhythm like a photographed one.

syamjithnk.com · Abu Dhabi, GMT+4

Popular repositories Loading

  1. python-arabic-reshaper python-arabic-reshaper Public

    Forked from mpcabd/python-arabic-reshaper

    Reconstruct Arabic sentences to be used in applications that don't support Arabic

    Python

  2. arnum-tts arnum-tts Public

    Do numbers survive Arabic text-to-speech? An open, reproducible benchmark.

    Python

  3. arshape arshape Public

    Does Arabic survive text rendering? Shaping measured across fonts and rendering paths.

    Python

  4. arpdf arpdf Public

    Does Arabic survive a PDF round trip? Generation and extraction measured end to end.

    Python

  5. Syamjith-NK Syamjith-NK Public

    Profile README

  6. arabic-tts-frontend arabic-tts-frontend Public

    A text front-end for Arabic TTS: numerals, dates, currency and times converted to spoken Arabic before synthesis. Measured, zero dependencies.

    Python