Skip to main content

universal-render

universal-render

Universal complex-script text rendering for Matplotlib and Seaborn, powered by Qt/HarfBuzz shaping.

PyPI Python License Scripts Tests

Open in Colab Open in Kaggle


Matplotlib's text engine cannot shape complex scripts — Bengali conjuncts break apart, Arabic letters don't join, Tamil and Thai vowels land in the wrong place, and often all you get is tofu boxes (▯▯▯). universal_render routes text through Qt's shaping engine (HarfBuzz) and embeds the correctly shaped result into your figures, with automatic script detection and per-script font fallback — no font configuration needed.

Before / After

Left: Matplotlib's own text engine. Right: the same text through universal_render — 22 scripts, same machine, zero configuration.

Matplotlib default vs universal_render across 22 scripts

And it isn't just tofu: even with the correct font installed, Matplotlib (and, on Windows, mplcairo and Pillow — their official wheels ship without Raqm/HarfBuzz) silently renders complex scripts unshaped: Arabic unjoined, Bengali conjuncts split, with zero warnings. See evaluation/ for the measured comparison.

✨ Features

  • Automatic Unicode script detection (detect_script, segment_runs)
  • Per-script font fallback with glyph-coverage validation (auto_font_fallback)
  • Correct shaping for Indic (Bengali, Hindi, Tamil, Telugu, …), RTL (Arabic, Hebrew), Southeast Asian (Thai, Khmer, Lao, Myanmar), CJK, and more — 22 scripts verified by the built-in self_test()
  • Mixed-script text: Bengali + English + numbers in one string
  • Native numerals for 16 scripts (to_native_numerals(2026, "Bengali")২০২৬)
  • Drop-in replacements for titles, axis labels, tick labels, legends, annotations, and heatmap cell text
  • Matplotlib-style text options: weight="bold", italic=True, alpha=0.5, rotation=45 (any angle, including rotated tick labels), multiline "\n" strings, title loc="left"/"center"/"right", and a proper figure suptitle
  • DPI-safe layout: titles, labels, and ticks stay correctly placed at any savefig(dpi=..., bbox_inches="tight")
  • Works headless (Colab/Kaggle/CI) via Qt offscreen mode
  • Drop-in bangla_render compatibility: import universal_render.compat as br

🚀 Quick Example

import matplotlib.pyplot as plt
import universal_render as ur

fig, ax = plt.subplots()
ax.plot([1, 2, 3], [3, 1, 4])

ur.set_multilingual_title(ax, "বাংলা শিরোনাম")        # Bengali — font auto-selected
ur.set_multilingual_xlabel(ax, "أشهر السنة")           # Arabic — shaped + RTL
ur.set_multilingual_ylabel(ax, "மாத விற்பனை")          # Tamil

ur.set_multilingual_xticks(ax, [1, 2, 3], ["एक", "दो", "तीन"])  # Hindi
ur.apply_multilingual_layout(fig, auto=True)

plt.savefig("plot.png", dpi=300)

Line plot with Bengali, Cyrillic, Tamil, Arabic, Hindi, Thai and English labels

One-call APIs

# Label a whole plot at once — each string picks its own script's font
ur.localize_axes(
    ax,
    title="বিক্রয় প্রতিবেদন ২০২৬",
    xlabel="महीना", ylabel="மதிப்பு",
    xticklabels=["জানু", "फ़र", "மார்", "April"],
    legend_labels=["পূর্বাভাস"],
)

# Annotated heatmap with native Bengali digits and auto text contrast
ur.multilingual_heatmap(
    ax, data, value_format=".2f", value_script="Bengali",
    row_labels=["ঢাকা", "চট্টগ্রাম", "খুলনা"],
    col_labels=["جودة", "คุณภาพ", "Quality"],
    title="গুণমান ম্যাট্রিক্স",
)

# Verify this machine renders all 22 supported scripts
ur.self_test()

Heatmap with per-cell text in five scripts Bold suptitle, left-aligned mixed-script title, rotated Bengali ticks

📦 API Overview

Area Functions
High-level (one call) localize_axes, multilingual_heatmap, multilingual_bar_labels, language_to_script, set_language_font, font_for_language, localized_numerals, supported_languages
Script detection detect_script, segment_runs, is_rtl_text, is_mixed_script, describe_text, to_native_numerals, script_direction
Fonts auto_font_fallback, find_font_for_script, set_script_font, register_font, validate_font, font_covers_text, coverage_report
Rendering render_text, render_text_array, render_mixed_text, render_paragraph, measure_text, set_render_defaults, render cache controls
Matplotlib set_multilingual_title/xlabel/ylabel/suptitle, set_multilingual_xticks/yticks/numeric_ticks, set_multilingual_legend, multilingual_text, annotate_multilingual, add_multilingual_cell_text, multilingual_paragraph, apply_multilingual_layout
Diagnostics / evaluation self_test (per-script render check), benchmark_render (cold/warm latency), save_comparison_figure (before/after figure)
bangla-render compat import universal_render.compat as br — every set_bangla_* name works unchanged

🌍 Supported Script Families

Family Scripts (verified by self_test()) Direction
Indic / Brahmic Bengali, Devanagari (Hindi/Marathi/Nepali), Tamil, Telugu, Kannada, Malayalam, Gujarati, Gurmukhi (Punjabi), Odia, Sinhala LTR
Right-to-left Arabic (Arabic/Urdu/Persian), Hebrew RTL
Southeast Asian Thai, Lao, Khmer, Myanmar LTR
East Asian Han (Chinese), Hangul (Korean), Hiragana (Japanese) LTR
European Latin, Greek, Cyrillic LTR

Script detection additionally covers Katakana, Tibetan, Georgian, Armenian, and Ethiopic. One script serves many languages — language_to_script() maps ~60 language names.

📊 Evaluation

evaluation/compare_backends.py renders the same text with the same font file through every backend, so the shaping engine is the only variable. Findings on Windows (Python 3.11):

Backend Bengali Arabic Notes
universal_render ✅ correct ✅ correct reference (Qt/HarfBuzz)
Matplotlib, default font ▯▯▯ tofu (104 warnings) unjoined at least it warns
Matplotlib, correct font broken conjuncts, 0 warnings unjoined, 0 warnings silent failure
mplcairo 0.6.1 (pip wheel) broken conjuncts, 0 warnings unjoined, 0 warnings wheel ships without Raqm
Pillow (pip wheel) PIL.features.check("raqm")False

🛠 Installation

pip install universal-render

Or try it without installing anything — the demo notebook runs on Colab or Kaggle with one click (on Kaggle, enable Internet in the notebook settings).

Fonts: on Windows, everything works out of the box (Nirmala UI, Segoe UI, Leelawadee UI…). On Linux/Colab, install Noto fonts (fonts-noto / fonts-noto-cjk) — the fallback tables pick them up automatically. Verify any environment with:

import universal_render as ur
ur.self_test()   # prints a per-script pass/fail table

🧪 Tests

py tests/test_universal_render.py     # or: pytest tests/

Limitations

  • Text is embedded as high-resolution raster images; SVG/PDF exports contain images rather than selectable vector glyphs.
  • Arbitrary-angle rotated tick labels are center-anchored under their tick (Matplotlib anchors the label end).
  • Scripts requiring vertical layout (e.g. traditional Mongolian) are not supported.

Citation

If you use universal-render in academic work, please cite the bangla-render paper (SoftwareX) for now — a dedicated paper for the universal framework is in preparation.

License

MIT © 2026 Mrinal Basak Shuvo

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

universal_render-0.1.2.tar.gz (55.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

universal_render-0.1.2-py3-none-any.whl (52.0 kB view details)

Uploaded Python 3

File details

Details for the file universal_render-0.1.2.tar.gz.

File metadata

  • Download URL: universal_render-0.1.2.tar.gz
  • Upload date:
  • Size: 55.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for universal_render-0.1.2.tar.gz
Algorithm Hash digest
SHA256 57de140bfefa607d2a25c4123e65deefe9469920a57b3b6a250c750e1d139b2a
MD5 c0df209baa72d06c5e45c3b9f7a4fb0f
BLAKE2b-256 ee70befda78af94ebd8cbdc7268405df9d67bca3e43bce01b7c279c5d9b1589b

See more details on using hashes here.

File details

Details for the file universal_render-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for universal_render-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 25ad162aae23b11902f5b548af4c5bd576240ce1434744d3ffd579a6ed384318
MD5 d8c4815620a78e2ab6c14848d90576e6
BLAKE2b-256 1ad0d8344dfcb77538795ca0cdb50c22f3c25f61943f780c54a5324f64d24656

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.3

2 files

This release

0.1.2 This release

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page