Bengali text rendering for Matplotlib & Seaborn using Qt/HarfBuzz
Project description
🇧🇩 bangla-render
Bengali Text Rendering for Matplotlib & Seaborn (with full OpenType shaping)
bangla-render is the first open-source Python library that enables fully correct Bengali text rendering inside Matplotlib and Seaborn.
Matplotlib cannot shape Bengali text — it does not use HarfBuzz and therefore fails with:
- Matra (ি, ী, ু, ূ, ৃ)
- Reph (র্)
- Juktakkhor (জ্ঞ, ক্ষ, ন্দ, ত্ম, ন্ত …)
- GSUB/GPOS OpenType shaping
So Bengali titles, axis labels, annotations, and heatmap text become broken, disjoint, or scrambled.
💡 bangla-render solves this completely.
It uses Qt's HarfBuzz engine to shape Bengali correctly, renders it into an RGBA image, and overlays it into Matplotlib using OffsetImage, bypassing Matplotlib's broken text renderer entirely.
✨ What's New in v0.2
| Area | Change |
|---|---|
| Architecture | Single file split into 5 dedicated modules |
| Font handling | Auto-discovery, validation & fallback chain |
| Performance | LRU render cache (256 entries, ~4× speedup on repeated labels) |
| Layout engine | Full BanglaLayoutManager — event-driven, multi-subplot aware |
| Tick labels | New set_bangla_xticks() / set_bangla_yticks() |
| Indic scripts | Hindi (Devanagari) and Tamil verified out-of-the-box |
| Environment | Headless / Colab / Kaggle detection in backend.py |
| Test suite | Benchmark, debug JSON reports, 4-subplot and multi-subplot tests |
✨ Features
✔ Full Bengali OpenType shaping
- Correct matra placement
- Proper conjunct formation
- Reph, rafar, vowel signs
- Multi-line paragraph shaping
- True Unicode (no ANSI/Bijoy hacks)
✔ High-level Matplotlib API
br.set_bangla_title(ax, "বাংলা শিরোনাম")
br.set_bangla_xlabel(ax, "এক্স অক্ষ")
br.set_bangla_ylabel(ax, "ওয়াই অক্ষ")
br.set_bangla_xticks(ax, positions, ["একটি", "দুটি", "তিনটি"])
br.set_bangla_yticks(ax, positions, ["রাগ", "আনন্দ", "ভয়"])
br.text(ax, 0.5, 0.5, "মাঝখানে", coord="axes")
✔ Heatmap and confusion-matrix support
br.add_bangla_in_cell(ax, row, col, "খুশি", rows, cols)
✔ Automatic layout engine
apply_bangla_layout(fig, auto=True) measures every placed label using the
Matplotlib renderer and adjusts margins so titles, tick labels, and axis labels
never overlap — correctly for any number of subplots.
✔ Works everywhere
- Matplotlib and Seaborn
- Jupyter / JupyterLab / VS Code
- Windows 10/11, macOS, Linux
- Any Matplotlib backend (Agg, TkAgg, QtAgg, …)
🔍 Before & After Comparison
Line Plot
| Default Matplotlib | With bangla-render |
|---|---|
Heatmap
| Before | After |
|---|---|
Confusion Matrix
| Before | After |
|---|---|
🔥 Why This Library Exists
Matplotlib cannot shape Indic scripts. Even with Bangla fonts installed, it produces:
- Disjoint characters
- Wrong glyph order
- Broken juktakkhor
- Incorrect matra positioning
Existing "solutions" only work for very simple words like ভয়, রাগ — but fail completely for:
- খুশি
- দৃষ্টিভঙ্গি
- শ্রদ্ধা
- ব্যবস্থাপনা
- হাস্যোজ্জ্বল
- পর্যালোচনায়
- And any real paragraph
Before bangla-render, there was:
- No PyPI library
- No correct Bengali shaping
- No Seaborn heatmap support
- No way to set Bengali xlabel/ylabel/title
- No Unicode-safe method
bangla-render fills this gap for the first time.
📦 Installation
pip install bangla-render
Dependencies (installed automatically):
| Package | Purpose |
|---|---|
PySide6 |
Qt / HarfBuzz shaping engine |
NumPy |
RGBA array conversion |
Matplotlib |
Plot integration |
Font note: On Windows, Nirmala UI (built-in) is used automatically.
On Linux / macOS, install Noto Sans Bengali:
sudo apt install fonts-notoorbrew install font-noto-sans
🚀 Quick Start
Line plot
import matplotlib.pyplot as plt
import bangla_render as br
br.init_renderer() # initialise Qt once
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot([1, 2, 3, 4, 5], [2, 4, 3, 5, 4])
br.set_bangla_title(ax, "রেখাচিত্র")
br.set_bangla_xlabel(ax, "সময় (মাস)")
br.set_bangla_ylabel(ax, "মান")
br.set_bangla_xticks(ax, [1, 2, 3, 4, 5],
["জানু", "ফেব্রু", "মার্চ", "এপ্রিল", "মে"])
br.apply_bangla_layout(fig, auto=True)
plt.savefig("line_plot.png", dpi=150)
plt.show()
Heatmap
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import bangla_render as br
br.init_renderer()
data = np.random.rand(3, 3)
words = [["খুশি", "রাগ", "আশা"],
["ভয়", "বিস্ময়", "শান্তি"],
["ঘৃণা", "আনন্দ", "সুখ"]]
fig, ax = plt.subplots(figsize=(6, 6))
sns.heatmap(data, ax=ax, cbar=True,
xticklabels=False, yticklabels=False)
rows, cols = data.shape
for i in range(rows):
for j in range(cols):
br.add_bangla_in_cell(ax, i, j, words[i][j], rows, cols)
br.set_bangla_title(ax, "বাংলা হিটম্যাপ")
br.set_bangla_xlabel(ax, "পূর্বাভাস শ্রেণি")
br.set_bangla_ylabel(ax, "আসল শ্রেণি")
br.apply_bangla_layout(fig, auto=True)
plt.savefig("heatmap.png", dpi=150)
plt.show()
Multi-subplot figure
import matplotlib.pyplot as plt
import bangla_render as br
br.init_renderer()
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# Left subplot
axes[0].plot([1, 2, 3], [3, 1, 4])
br.set_bangla_title(axes[0], "বাম প্লট")
br.set_bangla_xlabel(axes[0], "সময়")
br.set_bangla_ylabel(axes[0], "মান")
# Right subplot — with colorbar (ylabel auto-skipped when blocked)
import numpy as np
im = axes[1].imshow(np.random.rand(3, 3), cmap="viridis")
fig.colorbar(im, ax=axes[1])
br.set_bangla_title(axes[1], "ডান হিটম্যাপ")
br.set_bangla_xlabel(axes[1], "কলাম")
br.apply_bangla_layout(fig, auto=True)
plt.savefig("multisubplot.png", dpi=150)
plt.show()
🌐 Other Indic Scripts
The rendering pipeline is language-agnostic — pass any Brahmic script Unicode string and a matching OpenType font:
# Hindi (Devanagari) — uses Nirmala UI on Windows
br.set_bangla_ylabel(ax, "वास्तविक वर्ग",
font_family="Nirmala UI")
# Tamil — same font on Windows
br.set_bangla_ylabel(ax, "உண்மை வகை",
font_family="Nirmala UI")
Verified scripts: Bengali, Hindi (Devanagari), Tamil.
Expected to work (font availability required):
Assamese, Odia, Gujarati, Gurmukhi, Sinhala.
🧩 API Reference
Initialisation
| Function | Description |
|---|---|
init_renderer() |
Initialise Qt application (call once at startup) |
check_environment() |
Report Qt status, headless mode, Colab/Kaggle detection |
get_renderer_status() |
Detailed Qt initialisation info |
Font utilities
| Function | Description |
|---|---|
find_best_bangla_font() |
Return the best available Bengali font name |
list_available_fonts() |
List all system fonts |
list_bangla_candidate_fonts() |
List Bengali candidate fonts found on system |
Plot labels
| Function | Description |
|---|---|
set_bangla_title(ax, text, **kw) |
Set per-axes title |
set_bangla_xlabel(ax, text, **kw) |
Set x-axis label |
set_bangla_ylabel(ax, text, **kw) |
Set y-axis label |
set_bangla_xticks(ax, positions, labels, **kw) |
Set x-axis tick labels |
set_bangla_yticks(ax, positions, labels, **kw) |
Set y-axis tick labels |
Annotations
| Function | Description |
|---|---|
bangla_text(ax, x, y, text, coord="axes", **kw) |
Place text at arbitrary coordinates |
add_bangla_in_cell(ax, row, col, text, rows, cols, **kw) |
Annotate heatmap / matrix cell |
Layout
| Function | Description |
|---|---|
apply_bangla_layout(fig, auto=False, **kw) |
Adjust margins; auto=True measures placed artists |
Cache
| Function | Description |
|---|---|
get_render_cache_info() |
Return cache hit/miss counts and occupancy |
clear_render_cache() |
Clear the LRU cache (useful before benchmarking) |
Low-level rendering
| Function | Description |
|---|---|
render_text(text, output_path, **kw) |
Render text to a PNG file |
render_text_qimage(text, **kw) |
Render text to a QImage (internal use) |
render_paragraph(text, output_path, **kw) |
Render multi-line paragraph to PNG |
🏗 Architecture
bangla-render v0.2 — five-module architecture
─────────────────────────────────────────────
backend.py Qt application lifecycle, headless / Colab / Kaggle detection
fonts.py Font discovery, validation (conjunct/matra test), fallback chain
renderer.py HarfBuzz shaping via Qt, QImage rasterisation, LRU cache
layout.py BanglaLayoutManager — event-driven, multi-subplot, colorbar-aware
mpl_support.py Public Matplotlib API — all set_bangla_* functions
⚡ Performance
Measured on Windows 10, Python 3.11.9, font: Nirmala UI, N = 50 calls, cold cache.
| Text category | Median (ms) | Cache hit (ms) |
|---|---|---|
| Simple word (3–4 chars) | 0.27 | 0.06 |
| Conjunct consonant | 0.32 | 0.07 |
| Complex multi-conjunct | 0.40 | 0.08 |
| Axis label (medium) | 0.57 | 0.10 |
| 6×6 heatmap (36 cells, batch) | 10.5 ms total | — |
The LRU cache delivers roughly a 4× speedup for repeated labels.
🧪 Running the Test Suite
git clone https://github.com/mbs57/bangla-render.git
cd bangla-render
pip install -e .
python tests/test_suite.py
Outputs saved to test_outputs/. Debug JSON reports saved to test_outputs/debug/.
Benchmark results saved to test_outputs/benchmark_results.txt and .json.
🗺 Roadmap
- v0.1 — Bengali rendering for title, xlabel, ylabel, heatmap cells
- v0.2 — Five-module architecture, font validation, LRU cache, tick labels, Indic scripts, layout engine
- v0.3 — Mixed Bengali + MathText (
$\alpha$) support - v0.4 — Vector output via SVG path extraction
- v0.5 — Extend verified Indic support: Odia, Gujarati, Malayalam, Telugu
- v1.0 — Production-ready stable release and full documentation site
📄 License
MIT License — free for personal, academic, and commercial use.
📖 Citation
If you use bangla-render in research, please cite:
@article{shuvo2025banglarender,
title = {bangla-render: Correct Bengali Text Rendering for
Matplotlib \& Seaborn Using Qt/HarfBuzz},
author = {Shuvo, Mrinal Basak},
journal = {SoftwareX},
year = {2025},
note = {Under review, Manuscript SOFTX-D-25-00884}
}
⭐ Acknowledgements
This project aims to make scientific and data visualisation accessible to millions of Bengali speakers — helping students, educators, analysts, and researchers present data in their native language.
Built on the shoulders of Qt, HarfBuzz, Matplotlib, NumPy, and PySide6.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bangla_render-0.2.1.tar.gz.
File metadata
- Download URL: bangla_render-0.2.1.tar.gz
- Upload date:
- Size: 403.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1e131d32eac65ccc701e0ae655f2412349c759fe9cfae67dcb6658bc5a07971
|
|
| MD5 |
a30360c712927739916cae1d1a576749
|
|
| BLAKE2b-256 |
8b1790412c268c100501e3c5c5849e887c288e4a89bed25f95cfbe62ad27430c
|
File details
Details for the file bangla_render-0.2.1-py3-none-any.whl.
File metadata
- Download URL: bangla_render-0.2.1-py3-none-any.whl
- Upload date:
- Size: 32.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2061ce42ae84988667f918185c84f520e83c44198de99d7e6652484e07414744
|
|
| MD5 |
e39a3c07992e88c540b147b699389e26
|
|
| BLAKE2b-256 |
2ce1bdcfc5e98e55a631d16dcf3a5d3fa3abac31e688c2fd6380715646e0a853
|