tiny.filigree.font.cmap
Defined in font.
API (4)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/filigree/src/font/cmap.zig
zig
const std = @import("std");const binary = @import("binary.zig");const model = @import("model.zig");const FontError = model.FontError;const Table = binary.Table;const readU16 = binary.readU16;const readU24 = binary.readU24;const readU32 = binary.readU32;const CmapFormat = enum { format_4, format_12,};pub const Cmap = struct { offset: usize, len: usize, format: CmapFormat, variation: ?Table = null, pub fn init(data: []const u8, cmap_table: Table) FontError!Cmap { if (cmap_table.len < 4) return error.InvalidCmap; const count = try readU16(data, cmap_table.offset + 2); if (4 + @as(usize, count) * 8 > cmap_table.len) return error.InvalidCmap; var best_score: u16 = 0; var best: ?Cmap = null; var variation: ?Table = null; for (0..count) |i| { const record = cmap_table.offset + 4 + i * 8; const platform = try readU16(data, record); const encoding = try readU16(data, record + 2); const sub_offset = try readU32(data, record + 4); if (sub_offset >= cmap_table.len) continue; const absolute = cmap_table.offset + @as(usize, @intCast(sub_offset)); if (absolute + 2 > data.len) continue; const format = try readU16(data, absolute); if (format == 14 and platform == 0 and encoding == 5) { variation = validateFormat14(data, absolute) catch variation; continue; } const candidate = switch (format) { 4 => validateFormat4(data, absolute) catch continue, 12 => validateFormat12(data, absolute) catch continue, else => continue, }; const score = scoreSubtable(platform, encoding, candidate.format); if (score > best_score) { best_score = score; best = candidate; } } var selected = best orelse return error.UnsupportedCmap; selected.variation = variation; return selected; } pub fn map(self: Cmap, data: []const u8, codepoint: u32) FontError!u32 { return switch (self.format) { .format_4 => mapFormat4(self, data, codepoint), .format_12 => mapFormat12(self, data, codepoint), }; } pub fn mapVariation(self: Cmap, data: []const u8, codepoint: u32, variation_selector: u32) FontError!?u32 { const variation = self.variation orelse return null; return try mapFormat14(self, data, variation, codepoint, variation_selector); }};fn validateFormat4(data: []const u8, offset: usize) FontError!Cmap { if (offset > data.len or data.len - offset < 16) return error.InvalidCmap; const len: usize = try readU16(data, offset + 2); if (len < 16 or len > data.len - offset) return error.InvalidCmap; const seg_count_x2 = try readU16(data, offset + 6); if (seg_count_x2 == 0 or seg_count_x2 % 2 != 0) return error.InvalidCmap; const seg_count = @as(usize, seg_count_x2 / 2); const arrays_end = 14 + seg_count * 8; if (arrays_end > len) return error.InvalidCmap; return .{ .offset = offset, .len = len, .format = .format_4 };}fn validateFormat12(data: []const u8, offset: usize) FontError!Cmap { if (offset > data.len or data.len - offset < 16) return error.InvalidCmap; const len_u32 = try readU32(data, offset + 4); if (@as(u64, len_u32) > @as(u64, std.math.maxInt(usize))) return error.InvalidCmap; const len: usize = @intCast(len_u32); if (len < 16 or len > data.len - offset) return error.InvalidCmap; const groups = try readU32(data, offset + 12); const records_len = @as(u64, groups) * 12; if (records_len > std.math.maxInt(usize)) return error.InvalidCmap; if (records_len > @as(u64, len - 16)) return error.InvalidCmap; return .{ .offset = offset, .len = len, .format = .format_12 };}fn validateFormat14(data: []const u8, offset: usize) FontError!Table { if (offset > data.len or data.len - offset < 10) return error.InvalidCmap; const len_u32 = try readU32(data, offset + 2); if (@as(u64, len_u32) > @as(u64, std.math.maxInt(usize))) return error.InvalidCmap; const len: usize = @intCast(len_u32); if (len < 10 or len > data.len - offset) return error.InvalidCmap; const selector_count = try readU32(data, offset + 6); if (@as(u64, selector_count) * 11 > @as(u64, len - 10)) return error.InvalidCmap; const subtable = Table{ .offset = offset, .len = len }; for (0..selector_count) |i| { const record = offset + 10 + i * 11; _ = try readU24(data, record); const default_offset = try readU32(data, record + 3); const non_default_offset = try readU32(data, record + 7); if (default_offset != 0) { const default_table = try format14ChildOffset(subtable, default_offset, 4); const range_count = try readU32(data, default_table); if (@as(u64, range_count) * 4 > @as(u64, offset + len - default_table - 4)) return error.InvalidCmap; } if (non_default_offset != 0) { const non_default_table = try format14ChildOffset(subtable, non_default_offset, 4); const mapping_count = try readU32(data, non_default_table); if (@as(u64, mapping_count) * 5 > @as(u64, offset + len - non_default_table - 4)) return error.InvalidCmap; } } return subtable;}fn mapFormat4(cmap: Cmap, data: []const u8, codepoint: u32) FontError!u32 { if (codepoint > 0xffff) return 0; const cp: u16 = @intCast(codepoint); const seg_count = @as(usize, (try readU16(data, cmap.offset + 6)) / 2); const end_codes = cmap.offset + 14; const start_codes = end_codes + seg_count * 2 + 2; const id_deltas = start_codes + seg_count * 2; const id_range_offsets = id_deltas + seg_count * 2; for (0..seg_count) |i| { const end = try readU16(data, end_codes + i * 2); if (cp > end) continue; const start = try readU16(data, start_codes + i * 2); if (cp < start) return 0; const delta = try readU16(data, id_deltas + i * 2); const range_offset_pos = id_range_offsets + i * 2; const range_offset = try readU16(data, range_offset_pos); if (range_offset == 0) { return @as(u16, @truncate(@as(u32, cp) + @as(u32, delta))); } const glyph_offset = range_offset_pos + @as(usize, range_offset) + (@as(usize, cp - start) * 2); if (glyph_offset + 2 > cmap.offset + cmap.len) return error.InvalidCmap; const glyph = try readU16(data, glyph_offset); if (glyph == 0) return 0; return @as(u16, @truncate(@as(u32, glyph) + @as(u32, delta))); } return 0;}fn mapFormat12(cmap: Cmap, data: []const u8, codepoint: u32) FontError!u32 { const groups = try readU32(data, cmap.offset + 12); var left: usize = 0; var right: usize = @intCast(groups); while (left < right) { const mid = left + (right - left) / 2; const record = cmap.offset + 16 + mid * 12; const start = try readU32(data, record); const end = try readU32(data, record + 4); if (codepoint < start) { right = mid; } else if (codepoint > end) { left = mid + 1; } else { const glyph_start = try readU32(data, record + 8); const delta = codepoint - start; if (glyph_start > std.math.maxInt(u32) - delta) return error.InvalidCmap; return glyph_start + delta; } } return 0;}fn mapFormat14(cmap: Cmap, data: []const u8, variation: Table, codepoint: u32, variation_selector: u32) FontError!?u32 { const selector_count = try readU32(data, variation.offset + 6); var left: usize = 0; var right: usize = @intCast(selector_count); while (left < right) { const mid = left + (right - left) / 2; const record = variation.offset + 10 + mid * 11; const selector = try readU24(data, record); if (variation_selector < selector) { right = mid; } else if (variation_selector > selector) { left = mid + 1; } else { const default_offset = try readU32(data, record + 3); const non_default_offset = try readU32(data, record + 7); if (non_default_offset != 0) { if (try mapFormat14NonDefault(data, variation, non_default_offset, codepoint)) |glyph| return glyph; } if (default_offset != 0 and try mapFormat14Default(data, variation, default_offset, codepoint)) { return try cmap.map(data, codepoint); } return null; } } return null;}fn mapFormat14Default(data: []const u8, variation: Table, relative_offset: u32, codepoint: u32) FontError!bool { const table_offset = try format14ChildOffset(variation, relative_offset, 4); const range_count = try readU32(data, table_offset); var left: usize = 0; var right: usize = @intCast(range_count); while (left < right) { const mid = left + (right - left) / 2; const record = table_offset + 4 + mid * 4; const start = try readU24(data, record); const additional_count = data[record + 3]; const end = start + additional_count; if (codepoint < start) { right = mid; } else if (codepoint > end) { left = mid + 1; } else { return true; } } return false;}fn mapFormat14NonDefault(data: []const u8, variation: Table, relative_offset: u32, codepoint: u32) FontError!?u32 { const table_offset = try format14ChildOffset(variation, relative_offset, 4); const mapping_count = try readU32(data, table_offset); var left: usize = 0; var right: usize = @intCast(mapping_count); while (left < right) { const mid = left + (right - left) / 2; const record = table_offset + 4 + mid * 5; const value = try readU24(data, record); if (codepoint < value) { right = mid; } else if (codepoint > value) { left = mid + 1; } else { return try readU16(data, record + 3); } } return null;}fn format14ChildOffset(subtable: Table, relative_offset: u32, min_len: usize) FontError!usize { if (relative_offset == 0) return error.InvalidCmap; if (relative_offset > std.math.maxInt(usize)) return error.InvalidCmap; const relative: usize = @intCast(relative_offset); if (relative > subtable.len or min_len > subtable.len - relative) return error.InvalidCmap; return subtable.offset + relative;}fn scoreSubtable(platform: u16, encoding: u16, format: CmapFormat) u16 { return switch (format) { .format_12 => if (platform == 3 and encoding == 10) 600 else if (platform == 0) 500 else 100, .format_4 => if (platform == 3 and encoding == 1) 400 else if (platform == 3 and encoding == 0) 350 else if (platform == 0) 300 else 50, };}test "Cmap maps supplementary codepoints through format 12" { var bytes = @as([40]u8, @splat(0)); std.mem.writeInt(u16, bytes[0..2], 0, .big); 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(u16, bytes[14..16], 0, .big); std.mem.writeInt(u32, bytes[16..20], 28, .big); std.mem.writeInt(u32, bytes[20..24], 0, .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], 77, .big); const mapped = try Cmap.init(&bytes, .{ .offset = 0, .len = bytes.len }); try std.testing.expectEqual(@as(u32, 77), try mapped.map(&bytes, 0x1f600)); try std.testing.expectEqual(@as(u32, 0), try mapped.map(&bytes, 'A'));}Source: lib/filigree/src/font/root.zig:3
zig
pub const cmap = @import("cmap.zig");Audit
| Definitions | 5 |
|---|---|
| Public names | 5 |
| Members | 4 |
| Version | 26.7.0 |
| Revision | daab053ee433 |