An OpenType text shaper for Zig, ported from HarfBuzz and checked against its test suite, for fonts decoded by zig-font.
readme.zig
// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>
// SPDX-License-Identifier: MIT

//! The examples in README.md, so that they keep compiling and working.

const std = @import("std");
const font = @import("font");
const shaper = @import("font_shaper");

const latin = @embedFile("hb/in-house/fonts/49c9f7485c1392fa09a1b801bc2ffea79275f22e.ttf");
const variable = @embedFile("hb/in-house/fonts/HBTest-VF.ttf");

test "quick start" {
    const gpa = std.testing.allocator;
    var f = try font.Font.parse(gpa, latin, .{});
    defer f.deinit();

    var face: shaper.Face = .init(gpa, &f, &.{});

    var buffer: shaper.Buffer = .init(gpa);
    defer buffer.deinit();
    try buffer.addUtf8("VABE", 0, null);
    buffer.guessSegmentProperties();

    try shaper.shapeBuffer(gpa, &face, &buffer, &.{.init("liga", 1)});

    // As hb-shape gives it (simple.tests): V kerned against A.
    const want = [_][2]i32{ .{ 4, 1142 }, .{ 1, 1295 }, .{ 2, 1295 }, .{ 3, 1123 } };
    try std.testing.expectEqual(want.len, buffer.len());
    for (buffer.info.items, buffer.pos.items, want) |info, pos, w| {
        try std.testing.expectEqual(@as(u32, @intCast(w[0])), info.codepoint);
        try std.testing.expectEqual(w[1], pos.x_advance);
    }
}

test "a plan made once" {
    const gpa = std.testing.allocator;
    var f = try font.Font.parse(gpa, latin, .{});
    defer f.deinit();
    var face: shaper.Face = .init(gpa, &f, &.{});
    var plan: shaper.Plan = try .init(gpa, &face, .{ .direction = .ltr, .script = .latin });
    defer plan.deinit();
    for ([_][]const u8{ "VA", "BE" }) |text| {
        var buffer: shaper.Buffer = .init(gpa);
        defer buffer.deinit();
        try buffer.addUtf8(text, 0, null);
        buffer.direction = .ltr;
        buffer.script = .latin;
        try shaper.shape.shape(&plan, &buffer);
        try std.testing.expectEqual(@as(usize, 2), buffer.len());
    }
}

test "variable fonts" {
    const gpa = std.testing.allocator;
    var f = try font.Font.parse(gpa, variable, .{});
    defer f.deinit();
    var coords: [8]font.F2Dot14 = @splat(.{ .raw = 0 });
    const axes = if (f.fvar) |fvar| fvar.axes.len else 0;
    shaper.variations.normalize(&f, &.{.{ .tag = std.mem.readInt(u32, "TEST", .big), .value = 491 }}, null, coords[0..axes]);
    var face: shaper.Face = .init(gpa, &f, coords[0..axes]);

    var buffer: shaper.Buffer = .init(gpa);
    defer buffer.deinit();
    try buffer.addUtf8("A", 0, null);
    buffer.guessSegmentProperties();
    try shaper.shapeBuffer(gpa, &face, &buffer, &.{});
    // As hb-shape gives it (variations.tests).
    try std.testing.expectEqual(@as(i32, 496), buffer.pos.items[0].x_advance);
}