A pure-Zig text shaper. Ports of HarfBuzz's shaping engine (src/) and of a
TTF/OTF/AAT parser (src/ttf/), with Unicode tables from uucode. See
NOTICE.md for provenance and licensing.
Status: early, but now measured. Shaping output is validated against HarfBuzz's own
corpus, vendored at test/shape/data/: 4838 of 5536 run cases pass, with 588
skipped and 9 files that still abort. Every failure is enumerated in
test/shape/EXPECTED-FAIL, every abort in test/shape/CRASHES, and every skip is
attributed by cause in the runner's own output. The fallback positioning path in
src/ot/shape_fallback.zig is still unimplemented.
PORTING-DEBT.md tracks the known holes and the places the port
may be wrong.
Zig 0.17.0-dev.1818+7051f8e73 or newer (see minimum_zig_version in
build.zig.zon). uucode is vendored, so no network fetch is needed.
zig build # compile, install zig-out/bin/api-smoke
zig build run # run the API smoke check
zig build test # both suites: the shaper and the vendored parser
zig build test -Dfilter=cmap # filter tests by name
zig build test-shape # HarfBuzz's shaping corpus (aots by default)
zig build test-shape -Dcorpus=all # every collection
zig build test-shape-all # ignore the expected-failure listSee test/shape/README.md for how the corpus suite works.
Build options: -Dgvar_max_stack_tuples_len=N (default 32). The feature toggles
-Dvariable_fonts, -Dopentype_layout and -Dapple_layout exist but currently only
work at their default of true — see PORTING-DEBT.md.
Most Unicode tables are generated into the build cache on every build. One
generated file is checked in — src/tag_table.zig, built from materials/ by
scripts/gen-tag-table.py. Regenerate it only when materials/ changes:
rm -rf .zig-cache && zig build generateThe .zig-cache removal is required; the step does not pick up changes otherwise.
const hb = @import("harfbuzz_zig");
const font = try hb.tatfi.Face.parse(font_bytes, 0);
var face = try hb.Face.from_face(gpa, &font);
defer face.deinit(gpa);
var buffer = hb.Buffer.init(gpa);
defer buffer.deinit();
try buffer.push_str("hello");
_ = try hb.shape(gpa, &face, &.{}, &buffer);