Something went wrong. Try again.
Draws glyphs from zig-font onto z2d surfaces from positioned glyph runs, with fontconfig-based font loading and fallback.
Something went wrong. Try again.
2.6 kB · 72 lines
Zig
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Text to glyphs across a fallback list, without shaping: a stand-in until//! there is a shaper.//!//! The text is split wherever the face that draws it changes, as an//! itemizer would split it, and each piece is laid out by//! `font_renderer.simple_layout` after the one before. The same caveats//! apply: no `GSUB`, no `GPOS`, no right-to-left.
const std = @import("std");const Allocator = std.mem.Allocator;const renderer = @import("font_renderer");const Face = renderer.Face;const FontList = @import("FontList.zig");const Utf8Iterator = renderer.simple_layout.Utf8Iterator;
pub const Layout = struct { /// One per stretch of text drawn from one face, in order. Every run's /// glyphs are placed relative to the same origin, so all of them are /// drawn at the one position. runs: []renderer.Run, /// The pen's position after the last glyph. end_x: f32,
pub fn deinit(l: *Layout, gpa: Allocator) void { for (l.runs) |r| gpa.free(r.glyphs); gpa.free(l.runs); l.* = undefined; }};
pub const Error = Allocator.Error || error{ /// No font in the list can be loaded. NoFont,};
/// Lay `text` out at `size` pixels per em. Each character is drawn from/// the first face in `list` that has it, or from the primary face, as/// `.notdef`, if none does.pub fn layout(gpa: Allocator, list: *FontList, size: f32, text: []const u8) Error!Layout { const fallback = try list.primary() orelse return error.NoFont;
var runs: std.ArrayList(renderer.Run) = .empty; errdefer { for (runs.items) |r| gpa.free(r.glyphs); runs.deinit(gpa); } var pen: f32 = 0; var start: usize = 0; var current: ?*Face = null; var it: Utf8Iterator = .{ .bytes = text }; while (true) { const at = it.i; const cp = it.next(); const f: ?*Face = if (cp) |c| (try list.faceFor(c)) orelse fallback else null; if (current) |cur| if (f != cur) { var placed = try renderer.simple_layout.layout(gpa, cur, size, text[start..at]); for (placed.glyphs) |*g| g.x += pen; pen += placed.end_x; runs.append(gpa, .{ .face = cur, .size = size, .glyphs = placed.glyphs }) catch |err| { placed.deinit(gpa); return err; }; start = at; }; current = f; if (cp == null) break; } return .{ .runs = try runs.toOwnedSlice(gpa), .end_x = pen };}