How Chromium Handles Bidirectional Text Rendering

This article provides an overview of the technical pipeline the Chromium web browser uses to render bidirectional (BiDi) text, enabling the seamless integration of right-to-left (RTL) scripts like Hebrew and Arabic with left-to-right (LTR) English. It covers the entire process inside Chromium's Blink rendering engine, from initial character analysis using the Unicode Bidirectional Algorithm to text segmentation, font shaping via HarfBuzz, and final visual reordering on the screen.

Logical Order vs. Visual Order

Chromium’s rendering engine, Blink, processes text using its modern layout architecture, LayoutNG. To mix English with Arabic or Hebrew, Blink strictly separates the logical order of text from its visual order.

Logical order is the sequence in which characters are typed, read, and stored in the computer's memory. Visual order is how those characters are physically arranged from left to right on a screen. Chromium keeps all text in logical order inside the Document Object Model (DOM) and only converts it to visual order at the very end of the rendering pipeline.

The Unicode Bidirectional Algorithm and ICU

To translate logical order into visual order, Chromium relies on the International Components for Unicode (ICU) library, which implements the standard Unicode Bidirectional Algorithm (UBA). The algorithm categorizes every Unicode character by its directional property:

  • Strong characters: Latin letters are strictly LTR, whereas Arabic and Hebrew letters are strictly RTL.
  • Weak characters: Numbers and currency symbols have weak directionality. For example, numbers in Arabic and Hebrew are actually read left-to-right, even though the surrounding text is right-to-left.
  • Neutral characters: Spaces, punctuation, and symbols have no inherent direction and adopt the direction of the text surrounding them.

Bidi Resolution and Directional Runs

When Blink encounters a paragraph containing mixed scripts, it passes the text string to the ICU BiDi engine. The process follows a strict sequence:

  1. Base Direction Determination: Blink establishes the overall paragraph direction. It checks for explicit HTML attributes (like dir="rtl"), CSS properties, or defaults to the direction of the first strong character it finds.
  2. Embedding Levels: The ICU library scans the text and assigns a numerical "embedding level" to every character. Even numbers (0, 2, 4) represent LTR segments, while odd numbers (1, 3, 5) represent RTL segments.
  3. Segmentation into Runs: Contiguous characters that share the same embedding level are grouped into "directional runs." For example, an English sentence containing a two-word Hebrew phrase is split into three distinct runs: an LTR run (English), an RTL run (Hebrew), and another LTR run (the remaining English). Neutral characters like periods or brackets are resolved based on the runs next to them and the base direction of the paragraph.

Line Breaking in Logical Order

Before Chromium reverses any RTL text for display, it must calculate where lines wrap. LayoutNG measures the width of the text in its logical order to determine line breaks.

This step happens before visual reordering to ensure sentences break naturally. If a long Hebrew sentence is embedded in an English paragraph and needs to wrap across two lines, the chronologically first part of the Hebrew sentence must appear on the top line, and the continuation must appear on the bottom line.

Text Shaping with HarfBuzz

Once the text is broken into lines and segmented into unidirectional runs, Chromium passes each run to HarfBuzz, its text-shaping engine.

Shaping is critical for RTL languages. In Arabic, letters are cursive and change their visual form depending on whether they are at the beginning, middle, or end of a word. In Hebrew, vowel points (niqqud) and cantillation marks must be positioned precisely above or below specific consonants.

Blink feeds each isolated directional run into HarfBuzz along with its resolved direction (LTR or RTL), font data, and script type. HarfBuzz processes the run, selects the correct ligatures and glyphs from the font file, and calculates the exact spacing and coordinates for each character.

Visual Reordering and Skia Rendering

After HarfBuzz finishes shaping the runs for a specific line of text, Blink performs the final visual reordering based on the ICU embedding levels calculated earlier.

Blink reverses the order of the runs that have odd embedding levels (the RTL runs) so they read from right to left. Once the runs are in their final visual sequence from left to right for that specific line, Chromium hands the positioned glyphs over to Skia, its 2D graphics library, which paints the pixels onto the screen.

HTML and CSS Overrides

Web developers can manually influence Chromium's BiDi pipeline using HTML and CSS. When a developer uses the dir attribute, the <bdi> (BiDi Isolate) tag, or CSS properties like direction: rtl and unicode-bidi: isolate, Blink translates these rules into invisible Unicode BiDi control characters.

Blink injects these control characters directly into the logical text stream before handing it off to the ICU library. This forces the algorithm to treat specific segments of text as isolated directional islands, preventing neutral characters—such as punctuation at the end of an Arabic name inside an English sentence—from jumping to the wrong side of the text.