Recursive AI

Arabic and Hindi text in a PDF

All onepdf recipes

Write Arabic (right to left, letters joined) and Hindi (conjuncts and vowel signs in place) into a PDF in C# with OnePdf.Layout, and read it back.

Terminal
dotnet add package RecursiveAI.OnePdf.Layout --version 1.11.0

The fonts are Noto Sans Arabic and Noto Sans Devanagari, free under the SIL Open Font License from Google Fonts. Any TrueType or OpenType font that covers the script works.

C#
using OnePdf;
using OnePdf.Layout;
 
LayoutDocument.Compose(doc =>
{
    doc.Title("Greetings").Language("en");
    doc.Fonts.Add(File.ReadAllBytes("NotoSansArabic-Regular.ttf"), "Noto Sans Arabic");
    doc.Fonts.Add(File.ReadAllBytes("NotoSansDevanagari-Regular.ttf"), "Noto Sans Devanagari");
    doc.Fonts.FallbackFamilies.Add("Noto Sans Arabic");          // used for characters the main font lacks
    doc.Fonts.FallbackFamilies.Add("Noto Sans Devanagari");
    doc.Page(page =>
    {
        page.Margin(54);
        page.Content().Column(col =>
        {
            col.Spacing(12);
            col.Item().Text("Arabic, right to left:").FontSize(10);
            col.Item().Language("ar").RightToLeft()
                .Text("مرحبا بالعالم. هذا نص عربي في ملف PDF.").FontSize(20);
            col.Item().Text("Hindi:").FontSize(10);
            col.Item().Language("hi").Text("नमस्ते दुनिया। यह पीडीएफ़ में हिंदी पाठ है।").FontSize(20);
        });
    });
}).Save("greetings.pdf");
 
// Extraction returns the text in logical order, as it was written.
using PdfDocument pdf = PdfDocument.Open("greetings.pdf");
Console.WriteLine(pdf.Pages[0].ExtractText().Text);
Text
Arabic, right to left:
 
مرحبا بالعالم. هذا نص عربي في ملف PDF.
 
Hindi:
 
नमस्ते दुनिया। यह पीडीएफ़ में हिंदी पाठ है।

On the page the Arabic line runs from the right margin, its letters in their joined forms, and the Latin “PDF” inside it keeps left-to-right order. In the Hindi line the vowel sign ि of “दुनिया” and “हिंदी” is drawn before its consonant, and “स्त” in “नमस्ते” is one conjunct, as the script requires.

How it works

  • OnePdf.Layout shapes text with the OneFont engine: Arabic joining, Indic reordering and conjuncts, kerning and ligatures. The fonts are embedded as subsets.
  • Bidirectional text follows the Unicode bidirectional algorithm. RightToLeft() sets the base direction of an item, and doc.RightToLeft() mirrors a whole document: rows, table columns, alignment and list markers.
  • Fallback families are tried, cluster by cluster, for anything the main font lacks. Here the default Liberation Sans has neither script, so the Arabic and Devanagari text comes from the fallbacks.
  • Language marks the language of the text in the tagged output, for screen readers, and tells Hyphenators.Auto() which patterns to use where there are any.
  • Extraction returns logical order, because each run of glyphs is written with the text it stands for.

Good to know

  • Use OnePdf.Layout for these scripts. Text drawn with the core content builder (DrawText) isn’t shaped, so Arabic comes out unjoined and Hindi with its vowel signs out of place.
  • A character no registered font has is drawn as the font’s missing-glyph box and reported in the layout’s warnings.
  • Hyphenation patterns (RecursiveAI.OnePdf.Hyphenation) cover 26 language variants, none of them Arabic or Hindi.

Fonts, fallback, bidirectional text and shaping are in Layout text.

A longer walk-through on shaping, right-to-left layout and font fallback: Arabic and Hindi text in a C# PDF.