tiny.filigree.font.description
Defined in font.
API (5)
Actions
Public operations.
Types and contracts
Public types and contracts.
Values and defaults
Public values and defaults.
extended_emoji_bitextended_emoji_word: Coverage beyond the four OS/2 Unicode-range words.
Source
Source: lib/filigree/src/font/description.zig
zig
const std = @import("std");const binary = @import("binary.zig");const directory = @import("directory.zig");const model = @import("model.zig");const names = @import("names.zig");const cmap_owner = @import("cmap.zig");/// Coverage beyond the four OS/2 Unicode-range words.pub const extended_emoji_word: usize = 4;pub const extended_emoji_bit: u5 = 0;pub const Description = struct { family: []u8, weight_class: u16, width_class: u16, selection: u16, units_per_em: u16, ascent: f32, descent: f32, line_gap: f32, coverage: [8]u32, fixed_pitch: bool, color: bool, face_index: u32, pub fn deinit(self: *Description, allocator: std.mem.Allocator) void { allocator.free(self.family); self.* = undefined; }};pub fn describeAlloc(allocator: std.mem.Allocator, data: []const u8) (model.FontError || error{OutOfMemory})!Description { return describeAtAlloc(allocator, data, 0);}pub fn describeAtAlloc(allocator: std.mem.Allocator, data: []const u8, face_index: u32) (model.FontError || error{OutOfMemory})!Description { const offset = try directory.fontDirectoryOffset(data, face_index); const os2 = try directory.tableAt(data, offset, "OS/2"); if (os2.len < 64) return error.InvalidFont; const head = try directory.tableAt(data, offset, "head"); const hhea = try directory.tableAt(data, offset, "hhea"); if (head.len < 54 or hhea.len < 10) return error.InvalidFont; const units_per_em = try binary.readU16(data, head.offset + 18); if (units_per_em == 0) return error.InvalidFont; const selection = try binary.readU16(data, os2.offset + 62); const metric_source = if (selection & 0x80 != 0) os2 else hhea; if (selection & 0x80 != 0 and os2.len < 78) return error.InvalidFont; const metric_offset: usize = if (selection & 0x80 != 0) 68 else 4; const scale = @as(f32, @floatFromInt(units_per_em)); const cmap_table = try directory.tableAt(data, offset, "cmap"); const cmap = try cmap_owner.Cmap.init(data, cmap_table); var coverage: [8]u32 = @splat(0); inline for (0..4) |word| { coverage[word] = try binary.readU32(data, os2.offset + 42 + word * 4); } if (try hasEmoji(data, cmap)) coverage[extended_emoji_word] |= @as(u32, 1) << extended_emoji_bit; const post = directory.tableAt(data, offset, "post") catch |err| switch (err) { error.MissingTable => null, else => return err, }; const name_table = try directory.tableAt(data, offset, "name"); const record = (try names.bestNameRecord(data, name_table, 16)) orelse (try names.bestNameRecord(data, name_table, 1)) orelse return error.InvalidFont; const family = names.utf16BeToUtf8Alloc(allocator, record.bytes) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return error.InvalidFont, }; errdefer allocator.free(family); return .{ .family = family, .weight_class = try binary.readU16(data, os2.offset + 4), .width_class = try binary.readU16(data, os2.offset + 6), .selection = selection, .units_per_em = units_per_em, .ascent = @as(f32, @floatFromInt(try binary.readI16(data, metric_source.offset + metric_offset))) / scale, .descent = @as(f32, @floatFromInt(try binary.readI16(data, metric_source.offset + metric_offset + 2))) / scale, .line_gap = @as(f32, @floatFromInt(try binary.readI16(data, metric_source.offset + metric_offset + 4))) / scale, .coverage = coverage, .fixed_pitch = if (post) |table| try readFixedPitch(data, table) else false, .color = directory.optionalAt(data, offset, "COLR") != null or directory.optionalAt(data, offset, "CBDT") != null or directory.optionalAt(data, offset, "sbix") != null or directory.optionalAt(data, offset, "SVG ") != null, .face_index = face_index, };}fn hasEmoji(data: []const u8, cmap: cmap_owner.Cmap) model.FontError!bool { for (0x1f300..0x1fb00) |codepoint| { if (try cmap.map(data, @intCast(codepoint)) != 0) return true; } return false;}fn readFixedPitch(data: []const u8, table: binary.Table) model.FontError!bool { if (table.len < 16) return error.InvalidFont; return (try binary.readU32(data, table.offset + 12)) != 0;}test "face description reads family and OS/2 fields" { const fixture = @import("../fixture/root.zig"); const allocator = std.testing.allocator; const bytes = try fixture.createWithMetricVariations(allocator); defer allocator.free(bytes); const os2 = try directory.table(bytes, "OS/2"); std.mem.writeInt(u16, bytes[os2.offset + 4 ..][0..2], 700, .big); std.mem.writeInt(u16, bytes[os2.offset + 6 ..][0..2], 3, .big); std.mem.writeInt(u32, bytes[os2.offset + 42 ..][0..4], 0x201, .big); std.mem.writeInt(u16, bytes[os2.offset + 62 ..][0..2], 0x21, .big); var result = try describeAlloc(allocator, bytes); defer result.deinit(allocator); try std.testing.expectEqualStrings("Tiny Fixture", result.family); try std.testing.expectEqual(@as(u16, 700), result.weight_class); try std.testing.expectEqual(@as(u16, 3), result.width_class); try std.testing.expectEqual(@as(u16, 0x21), result.selection); try std.testing.expectEqual(@as(u32, 0x201), result.coverage[0]); try std.testing.expectEqual(@as(u16, 1000), result.units_per_em); try std.testing.expectEqual(@as(f32, 0.8), result.ascent); try std.testing.expectEqual(@as(f32, -0.2), result.descent); try std.testing.expectEqual(@as(f32, 0), result.line_gap); try std.testing.expect(!result.fixed_pitch); try std.testing.expect(!result.color); try std.testing.expectEqual(@as(u32, 0), result.face_index);}test "face description selects OS/2 typo metrics when requested" { const fixture = @import("../fixture/root.zig"); const allocator = std.testing.allocator; const bytes = try fixture.createWithMetricVariations(allocator); defer allocator.free(bytes); var result = try describeAlloc(allocator, bytes); defer result.deinit(allocator); try std.testing.expectEqual(@as(f32, 0.8), result.ascent); try std.testing.expectEqual(@as(f32, -0.2), result.descent); try std.testing.expectEqual(@as(f32, 0.04), result.line_gap);}test "face description derives extended emoji coverage from cmap" { var bytes: [40]u8 = @splat(0); std.mem.writeInt(u16, bytes[2..4], 1, .big); std.mem.writeInt(u16, bytes[4..6], 3, .big); std.mem.writeInt(u16, bytes[6..8], 10, .big); std.mem.writeInt(u32, bytes[8..12], 12, .big); std.mem.writeInt(u16, bytes[12..14], 12, .big); std.mem.writeInt(u32, bytes[16..20], 28, .big); std.mem.writeInt(u32, bytes[24..28], 1, .big); std.mem.writeInt(u32, bytes[28..32], 0x1f600, .big); std.mem.writeInt(u32, bytes[32..36], 0x1f600, .big); std.mem.writeInt(u32, bytes[36..40], 1, .big); const cmap = try cmap_owner.Cmap.init(&bytes, .{ .offset = 0, .len = bytes.len }); try std.testing.expect(try hasEmoji(&bytes, cmap));}test "face description reads post fixed pitch and rejects a short table" { var bytes: [16]u8 = @splat(0); std.mem.writeInt(u32, bytes[12..16], 1, .big); try std.testing.expect(try readFixedPitch(&bytes, .{ .offset = 0, .len = bytes.len })); try std.testing.expectError(error.InvalidFont, readFixedPitch(&bytes, .{ .offset = 0, .len = 15 }));}test "face description rejects missing and truncated OS/2" { const fixture = @import("../fixture/root.zig"); const allocator = std.testing.allocator; const missing = try fixture.create(allocator); defer allocator.free(missing); try std.testing.expectError(error.MissingTable, describeAlloc(allocator, missing)); const bytes = try fixture.createWithMetricVariations(allocator); defer allocator.free(bytes); const os2 = try directory.table(bytes, "OS/2"); const records = try binary.readU16(bytes, 4); for (0..records) |index| { const row = 12 + index * 16; if (try binary.readU32(bytes, row) != model.tag("OS/2")) continue; std.mem.writeInt(u32, bytes[row + 12 ..][0..4], 63, .big); break; } _ = os2; try std.testing.expectError(error.InvalidFont, describeAlloc(allocator, bytes));}Source: lib/filigree/src/font/root.zig:5
zig
pub const description = @import("description.zig");Audit
| Definitions | 3 |
|---|---|
| Public names | 3 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |