HarfBuzz is a text-shaping engine, not a complete paragraph compositor. It transforms Unicode code points in a buffer into positioned glyphs using a font and run properties. Correct integration depends on upstream bidi and font selection, and on downstream line layout, paint and caret mapping.
What hb_shape actually consumes and returns
The central operation hb_shape(font, buffer, features, feature_count) accepts an hb_font_t, a mutable hb_buffer_t containing Unicode text and run properties, plus optional feature settings. The buffer is replaced with glyph information and corresponding position records. Client code consumes glyph IDs, clusters, advances and offsets, not freshly encoded Unicode characters.
HarfBuzz is intentionally focused: it does not own all application concerns such as full-document selection, font fallback policy, Unicode normalization strategy, paragraph wrapping or pixel rasterization. Misunderstanding these boundaries commonly produces errors blamed on the shaper even though the input runs are malformed.
Prepare the buffer with segment properties
Buffer direction, script and language are shaping inputs. hb_buffer_guess_segment_properties can provide convenient defaults, but guessing from a mixed-script paragraph is not a substitute for proper text itemization. Supply known properties when embedding the shaper into an editor, multilingual interface or paragraph engine.
A right-to-left run should not be passed as an indiscriminate left-to-right span. Similarly, setting an inappropriate script tag can disable expected contextual substitutions. Keep language tags consistent with user content and font support; two language systems may select different lookups in the same font.
Choose fonts and configure scaling explicitly
An hb_face_t represents font face data and an hb_font_t holds scaled font behavior and callbacks. Font coordinates and scale must be interpreted consistently with the target graphics system. If output advances look unexpectedly large or tiny, verify units before assuming corruption in shaping.
HarfBuzz can use its OpenType font functions and integrate with font/graphics libraries. Different font instances or variable-font axis coordinates can produce different glyph positions. Record the exact font binary and variation settings for reproducible results; family names alone do not identify identical font data.
Understand glyph clusters without treating them as grapheme IDs
Output glyph info contains a cluster value associated with source text indices. Cluster values aid mapping from glyph output back to input but are not guaranteed to represent a single Unicode scalar or exactly one UAX #29 extended grapheme cluster. Certain lookups merge clusters; scripts may reorder glyphs.
HarfBuzz provides configurable cluster levels and documents their semantics. A client must choose a level appropriate to editing, hit testing and reconstruction requirements. Do not sort the final glyph array by cluster values: that can destroy the shaped order and break right-to-left and Indic text.
Distinguish advances from offsets
Glyph advances move the current pen position. X/Y offsets move the placement of a glyph relative to that pen without generally changing the advance for following glyphs. Mark positioning often relies on offsets; kerning commonly changes advances or offsets. Confusing these fields can displace accents even when the shaper returned correct results.
Integrators should draw the glyph at pen plus offset, then advance by the returned advance, respecting coordinate system conventions and text direction. A missing mark may be a paint clipping or font selection issue rather than a shaping failure; compare the recorded glyph ID and offset before judging.
Features and variation settings must be controlled
Feature settings allow enabling, disabling or adjusting supported OpenType features over text ranges. Passing no explicit features does not mean all features are off: default and required features still depend on shaper and script logic. Variation axes may change glyph outlines, advances or even substitution behavior.
When measuring a difference, log the feature tag, value and range, together with the font variation coordinates. Testing liga=0 in a Latin string is useful for one narrow behavior, not a safe universal prescription for all scripts.
Shape a run, then perform line layout
A typical integration segments text by bidi levels, script and font, shapes each item, and collects advances to place glyphs and determine line breaks. Wrapping may require reshaping after selecting breakpoints, depending on shaping context and script. Rendering code then draws the positioned glyphs using its graphics engine.
Passing a whole multiline paragraph into one shape call without considering direction, font fallback and break context is not a complete layout algorithm. HarfBuzz buffers have boundary flags for special contexts, but the application remains responsible for using correct run and paragraph boundaries.
Interpret failures with upstream and downstream evidence
If Arabic forms remain isolated, inspect script and direction, font GSUB and supported shapers. If accents float, inspect GPOS, glyph offsets, scaling and paint transforms. If a glyph is missing, check whether fallback selected a font with the relevant mapping; shaping cannot create outlines absent from the face.
For triage, capture the original scalars, font file hash, shaping flags, feature list, buffer properties and returned glyph arrays. Also capture the exact rasterizer version when the difference is purely pixel-level. The HarfBuzz command-line utilities provide useful isolation when available.
Reproducible experiment: preserve the input while comparing the output
The following specimen is deliberately small enough to audit manually. For any test involving rendering, also record the exact font files, shaping configuration, user locale and platform versions. The code prints or illustrates source-level facts; it does not claim that JavaScript can identify a platform's selected font or glyph IDs without the relevant layout instrumentation.
// Conceptual C API sequence (font and buffer ownership omitted):
hb_buffer_t *buf = hb_buffer_create();
hb_buffer_add_utf8(buf, text, -1, 0, -1);
hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
hb_buffer_set_language(buf, hb_language_from_string("en", -1));
hb_shape(font, buf, NULL, 0);
unsigned int n = 0;
const hb_glyph_info_t *ids = hb_buffer_get_glyph_infos(buf, &n);
const hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buf, &n);
// Read ids[i].codepoint, ids[i].cluster, pos[i].x_advance and offsets.A useful report includes both positive and negative evidence. Record what stayed identical as well as what changed: identical scalar values with different glyphs indicate a presentation-layer divergence, whereas differing scalars reveal a transformation, selection or interchange problem. Preserve the original code points before applying normalization or conversion.
Evidence matrix for HarfBuzz shaping API
| Layer or specimen | Controlled observation | Correct interpretation |
|---|---|---|
| hb_buffer | Unicode + run properties | same object holds output glyphs after shaping |
| hb_font | font face and scale | different instances yield different metrics |
| glyph info | codepoint and cluster | codepoint now a glyph ID, not Unicode scalar |
| glyph position | advances and offsets | must be consumed in correct coordinate system |
| features | tag/value/range | not all default features are disabled by omission |
This matrix is a diagnostic aid, not a substitute for conformance testing. Some differences are allowed by Unicode or CSS; others indicate a bug in an implementation, a font, a font-subsetting pipeline or an application that edits text. Be explicit about the relevant standard and the end-user expectation.
Validation protocol for applications and browser integrations
- hb_buffer: Reproduce the observed behavior using Unicode + run properties as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is same object holds output glyphs after shaping; do not extrapolate from a screenshot alone.
- hb_font: Reproduce the observed behavior using font face and scale as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is different instances yield different metrics; do not extrapolate from a screenshot alone.
- glyph info: Reproduce the observed behavior using codepoint and cluster as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is codepoint now a glyph ID, not Unicode scalar; do not extrapolate from a screenshot alone.
- glyph position: Reproduce the observed behavior using advances and offsets as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is must be consumed in correct coordinate system; do not extrapolate from a screenshot alone.
- features: Reproduce the observed behavior using tag/value/range as a controlled input. Preserve exact source data, capture an expected outcome, and explain whether the measured difference is located in font selection, shaping, layout or copying. The useful observation is not all default features are disabled by omission; do not extrapolate from a screenshot alone.
Keep regression fixtures in UTF-8 with explicit scalar annotations when invisible characters, combining sequences or non-BMP characters are present. Test a stable baseline and at least one deliberately adversarial input. Store observed environment data alongside results so later font or browser updates can be interpreted rather than mistaken for content edits.
Failure patterns, false positives and interpretation
- Overinterpreting hb_buffer. A result associated with Unicode + run properties does not, by itself, establish the integrity of the whole text pipeline. same object holds output glyphs after shaping. Compare the input and an independently observed intermediate state before assigning the defect to a component.
- Overinterpreting hb_font. A result associated with font face and scale does not, by itself, establish the integrity of the whole text pipeline. different instances yield different metrics. Compare the input and an independently observed intermediate state before assigning the defect to a component.
- Overinterpreting glyph info. A result associated with codepoint and cluster does not, by itself, establish the integrity of the whole text pipeline. codepoint now a glyph ID, not Unicode scalar. Compare the input and an independently observed intermediate state before assigning the defect to a component.
- Overinterpreting glyph position. A result associated with advances and offsets does not, by itself, establish the integrity of the whole text pipeline. must be consumed in correct coordinate system. Compare the input and an independently observed intermediate state before assigning the defect to a component.
- Overinterpreting features. A result associated with tag/value/range does not, by itself, establish the integrity of the whole text pipeline. not all default features are disabled by omission. Compare the input and an independently observed intermediate state before assigning the defect to a component.
Do not correct a visual artifact by silently rewriting the user's Unicode string, stripping default-ignorable characters without context, replacing unknown scalars with question marks, or flattening rich text without consent. Data integrity, rendering aesthetics, selection behavior and search policy have different constraints. A fix at one layer can create regressions in another.
Standards and primary implementation references
Primary references are linked below. A standard describes normative requirements or terminology, while an implementation manual describes a particular engine. Where behavior is unspecified or depends on fonts and applications, treat observations as environment-specific rather than universal guarantees.
- HarfBuzz Getting Started — use this normative reference or implementation manual to verify the applicable definitions and requirements for HarfBuzz shaping API. Distinguish what the source specifies from what a particular font or platform happens to implement.
- HarfBuzz Shaping & Shape Plans — use this normative reference or implementation manual to verify the applicable definitions and requirements for HarfBuzz shaping API. Distinguish what the source specifies from what a particular font or platform happens to implement.
- HarfBuzz Clusters — use this normative reference or implementation manual to verify the applicable definitions and requirements for HarfBuzz shaping API. Distinguish what the source specifies from what a particular font or platform happens to implement.
- HarfBuzz API index — use this normative reference or implementation manual to verify the applicable definitions and requirements for HarfBuzz shaping API. Distinguish what the source specifies from what a particular font or platform happens to implement.
- OpenType Layout specification — use this normative reference or implementation manual to verify the applicable definitions and requirements for HarfBuzz shaping API. Distinguish what the source specifies from what a particular font or platform happens to implement.
Related research and boundaries
This article belongs to the Fonts, Shaping & Text Rendering virtual silo. Related published technical notes explain adjacent layers so the original problem can be isolated without conflating scalar identity, shaping output, font support and editor behavior.
When reproducing a finding, cite the relevant standard, the exact Unicode sequence and the environment in which it occurred. This preserves the value of a technical observation even after browsers, fonts and rendering libraries change.