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.
3.4 kB · 91 lines
Zig
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! An OpenType text shaper: given text and a font decoded by zig-font, it//! picks the glyphs and works out where each goes, applying the font's//! `GSUB` and `GPOS` features as HarfBuzz does.//!//! ```zig//! var face: shaper.Face = .init(gpa, &font, &.{});//! var buffer: shaper.Buffer = .init(gpa);//! defer buffer.deinit();//! try buffer.addUtf8("Hello", 0, null);//! buffer.guessSegmentProperties();//! try shaper.shapeBuffer(gpa, &face, &buffer, &.{});//! for (buffer.info.items, buffer.pos.items) |info, pos| {//! // info.codepoint is the glyph ID, info.cluster the text it came//! // from, and pos its advance and offset in font units.//! }//! ```//!//! The pipeline is a port of HarfBuzz's OpenType shaper, kept close enough//! to the original that it gives the same glyphs and positions, which the//! test suite checks against HarfBuzz's own tests. It has HarfBuzz's default//! shaper and its Arabic one; scripts that HarfBuzz gives a shaper of their//! own (Indic, Khmer, Myanmar, Hangul, Thai, Hebrew and the Universal Shaping//! Engine scripts) are shaped with the default one for now.
const std = @import("std");
pub const Buffer = @import("Buffer.zig");pub const Face = @import("Face.zig");pub const Feature = @import("Feature.zig");pub const Map = @import("Map.zig");pub const shape = @import("shape.zig");pub const unicode = @import("unicode.zig");pub const script = @import("script.zig");pub const language = @import("language.zig");pub const variations = @import("variations.zig");
pub const Plan = shape.Plan;pub const Direction = script.Direction;pub const Script = script.Script;pub const Language = language.Language;pub const GlyphInfo = Buffer.GlyphInfo;pub const GlyphPosition = Buffer.GlyphPosition;
/// Shapes `buffer` with a plan made for it on the spot. The buffer's/// direction must be set; `Buffer.guessSegmentProperties` sets it, and the/// script, from the text. To shape many runs alike, make a `Plan` once and/// call `shape.shape` with it instead.pub fn shapeBuffer(gpa: std.mem.Allocator, face: *const Face, buffer: *Buffer, features: []const Feature) std.mem.Allocator.Error!void { var plan: Plan = try .init(gpa, face, .{ .direction = buffer.direction orelse .ltr, .script = buffer.script, .language = buffer.language, .features = features, }); defer plan.deinit(); try shape.shape(&plan, buffer);}
test { _ = Buffer; _ = Face; _ = Feature; _ = Map; _ = shape; _ = unicode; _ = script; _ = language; _ = variations; _ = @import("cmap.zig"); _ = @import("round.zig"); _ = @import("apply.zig"); _ = @import("gsub.zig"); _ = @import("gpos.zig"); _ = @import("normalize.zig"); _ = @import("fallback.zig"); _ = @import("kern.zig"); _ = @import("props.zig"); _ = @import("shapers/arabic.zig"); _ = @import("shapers/indic.zig"); _ = @import("shapers/khmer.zig"); _ = @import("shapers/myanmar.zig"); _ = @import("shapers/use.zig"); _ = @import("shapers/hangul.zig"); _ = @import("shapers/hebrew.zig"); _ = @import("shapers/thai.zig"); _ = @import("shapers/ragel.zig"); _ = @import("shapers/syllabic.zig");}