Checkpoint 2: The Quest for Perfect Quran Harakat Rendering in PDF

English > Checkpoint 2: The Quest for Perfect Quran Harakat Rendering in PDF
Facebook
Print
LinkedIn
Telegram
X
WhatsApp
0 0
Read Time:5 Minute, 20 Second

After successfully generating a bilingual Quran PDF from my dataset, I was excited to enhance the visual fidelity of the Arabic script. Specifically, I wanted to ensure that every verse—every fathah, kasrah, dhammah, and sukun—was rendered clearly and accurately. These diacritics (harakat) are not just decorative; they are essential to the integrity and meaning of Quranic recitation.

Why Harakat Matter

In Arabic, harakat determine how words are pronounced and interpreted. In the Quran, they preserve the divine cadence and precision of the Prophet’s ﷺ recitation. If I was going to build a meaningful Quran tool, harakat rendering couldn’t be optional—it had to be right.

The Challenge

I began by testing a specialized font: AmiriQuran.ttf. It’s crafted specifically for Quranic typesetting and includes extensive diacritic support. But when I ran the script using reportlab, I was met with a familiar frustration: blank boxes and rectangles instead of beautifully shaped Arabic script.

Rectangular shape when rendering Arabic font

Even after carefully reshaping the Arabic with arabic_reshaper and correcting its direction with python-bidi, the PDF output failed. I tried other system fonts like Traditional Arabic, Arabic Typesetting, and even Scheherazade New—all of which displayed harakat perfectly in Word, but still failed when rendered through reportlab.

The Realization

I learned that reportlab, while powerful for layout and basic multilingual text, has limited support for advanced OpenType features. It simply can’t handle Arabic ligatures or precise diacritic placement that fonts like AmiriQuran rely on. That explained why rectangles appeared even with valid fonts.

A Touch of Tradition: Basmala Placement

Despite this setback, one thing did work beautifully: dynamically adding “بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ” before each Surah. I wrote logic to include the Basmala automatically at the beginning of every chapter—except Surah At-Tawbah (Surah 9), which traditionally appears without it.

This wasn’t just a formatting detail. It was a symbolic and spiritual enhancement. It felt like a small but powerful reminder that code, when written with purpose, can carry tradition and reverence.

Here’s how the Basmala addition works under the hood:

if row["surah_no"] != 9:
    c.setFont("Amiri", 14)
    basmala = "بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ"
    reshaped_basmala = arabic_reshaper.reshape(basmala)
    bidi_basmala = get_display(reshaped_basmala)
    c.drawRightString(width - 50, y_position, bidi_basmala)
    y_position -= 25
  • We skip Surah 9 to honor traditional formatting.
  • The Basmala is reshaped and rendered right-aligned using drawRightString.
  • It visually separates the Surah heading from the ayah, reinforcing both structure and spirituality.

This logic not only preserves tradition but also enhances readability, especially for learners and educators who rely on these visual cues.

Adding Basmalah at the beginning of the Surah

Back to Basics

With AmiriQuran proving unstable, I reverted to the reliable Amiri-Regular.ttf. It may not be perfect, but it rendered the core Arabic text—and most of its harakat—consistently and clearly. I adjusted the font size, spacing, and alignment to improve readability, and was able to render key verses like the Basmala properly.

I even created a standalone test script that outputs a single line:

بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ

This “Basmala test” PDF became my controlled environment for trying out fonts and render strategies before returning to full Quran output.

Where I Stand at This Checkpoint

After several iterations, here’s what I’ve achieved:

  • ✅ I can render Arabic with basic harakat using Amiri-Regular.ttf
  • ✅ I dynamically add Basmala before every Surah except Surah At-Tawbah — following traditional Mushaf structure
  • ✅ I have a standalone test setup to validate font behavior and Arabic rendering
  • ⚠️ I now understand the limitations of reportlab for complex Arabic scripts
  • ❗ I’m preparing to explore alternatives like image rendering or HTML-to-PDF for full Quranic typography

Lessons Learned

  • The font is only half the story — the rendering engine matters just as much.
  • Test in isolation — a single line can reveal what 6,000 ayahs can’t.
  • Don’t chase perfection too early — use what works now, and iterate.

Checkpoint 2 wasn’t just a technical stage—it was a philosophical one. It reminded me that even in code, form and meaning must coexist, and that building something sacred requires both precision and patience.

Basmallah Addition Full Code

import pandas as pd
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
import arabic_reshaper
from bidi.algorithm import get_display

# Load dataset
df = pd.read_csv("The Quran Dataset.csv")
df = df[df["surah_no"] >= 110]
df = df.sort_values(by=["surah_no", "ayah_no_surah"])

# Setup PDF
c = canvas.Canvas("quran_last_5_surahs_bismillah.pdf", pagesize=A4)
width, height = A4
y_position = height - 50

# Register Arabic font (make sure .ttf is in same folder)
pdfmetrics.registerFont(TTFont('Amiri', 'Amiri-Regular.ttf'))

# Set default font
c.setFont("Amiri", 16)

current_surah = ""
for _, row in df.iterrows():
    surah_en = row["surah_name_en"]
    surah_ar = row["surah_name_ar"]
    ayah_no = row["ayah_no_surah"]
    ayah_ar = row["ayah_ar"]
    ayah_en = row["ayah_en"]

    # Add new surah title
    if surah_en != current_surah:
        current_surah = surah_en
        y_position -= 30
        if y_position < 100:
            c.showPage()
            y_position = height - 50
            c.setFont("Amiri", 16)

        # English + Arabic surah title
        c.setFont("Helvetica-Bold", 14)
        c.drawString(50, y_position, f"Surah {surah_en} / {surah_ar}")
        y_position -= 25

        # Add Basmala (skip Surah 9)
        if row["surah_no"] != 9:
            c.setFont("Amiri", 14)
            basmala = "بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ"
            reshaped_basmala = arabic_reshaper.reshape(basmala)
            bidi_basmala = get_display(reshaped_basmala)
            c.drawRightString(width - 50, y_position, bidi_basmala)
            y_position -= 25

        # Reset font for ayahs
        c.setFont("Amiri", 16)

    # Prepare Arabic (reshaped + RTL)
    reshaped_text = arabic_reshaper.reshape(f"{ayah_no}. {ayah_ar}")
    bidi_text = get_display(reshaped_text)

    # Add Arabic ayah
    c.drawRightString(width - 50, y_position, bidi_text)
    y_position -= 20

    # Add English ayah
    c.setFont("Helvetica", 12)
    c.drawString(50, y_position, ayah_en)
    y_position -= 30
    c.setFont("Amiri", 16)

    # New page if needed
    if y_position < 100:
        c.showPage()
        y_position = height - 50
        c.setFont("Amiri", 16)

# Save PDF
c.save()
Facebook
Twitter
LinkedIn
Pinterest
Pocket
WhatsApp

Jangan lewatkan artikel penting! Langganan newsletter dosensibuk.com sekarang.

Leave a Reply

Your email address will not be published. Required fields are marked *