Something went wrong. Try again.
An OpenType text shaper for Zig, ported from HarfBuzz and checked against its test suite, for fonts decoded by zig-font.
Something went wrong. Try again.
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);
}