tiny.filigree.font.math
Defined in font.
API (21)
Actions
Public operations.
GlyphInfo.initGlyphInfo.topAccentAttachmentMath.horizontalAssemblyAllocMath.horizontalVariantMath.initMath.topAccentAttachmentMath.verticalAssemblyAllocMath.verticalVariantVariants.horizontalAssemblyAllocVariants.horizontalVariantVariants.initVariants.verticalAssemblyAllocVariants.verticalVariant
Types and contracts
Public types and contracts.
Source
Source: lib/filigree/src/font/math.zig
zig
const std = @import("std");const binary = @import("binary.zig");const layout = @import("../layout/root.zig");const model = @import("model.zig");const FontError = model.FontError;const Table = binary.Table;const readI16 = binary.readI16;const readU16 = binary.readU16;const math_header_size = 10;const math_constants_size = 214;const math_glyph_info_header_size = 8;const math_top_accent_attachment_header_size = 4;const math_variants_header_size = 10;const math_glyph_construction_header_size = 4;const math_glyph_variant_size = 4;const math_glyph_assembly_header_size = 6;const math_glyph_part_size = 10;const math_value_record_size = 4;const glyph_part_extender_flag = 0x0001;const max_assembly_parts = 4096;pub const ValueRecord = struct { value: i16, device_offset: u16,};pub const Constants = struct { script_percent_scale_down: i16, script_script_percent_scale_down: i16, delimited_sub_formula_min_height: u16, display_operator_min_height: u16, math_leading: ValueRecord, axis_height: ValueRecord, accent_base_height: ValueRecord, flattened_accent_base_height: ValueRecord, subscript_shift_down: ValueRecord, subscript_top_max: ValueRecord, subscript_baseline_drop_min: ValueRecord, superscript_shift_up: ValueRecord, superscript_shift_up_cramped: ValueRecord, superscript_bottom_min: ValueRecord, superscript_baseline_drop_max: ValueRecord, sub_superscript_gap_min: ValueRecord, superscript_bottom_max_with_subscript: ValueRecord, space_after_script: ValueRecord, upper_limit_gap_min: ValueRecord, upper_limit_baseline_rise_min: ValueRecord, lower_limit_gap_min: ValueRecord, lower_limit_baseline_drop_min: ValueRecord, stack_top_shift_up: ValueRecord, stack_top_display_style_shift_up: ValueRecord, stack_bottom_shift_down: ValueRecord, stack_bottom_display_style_shift_down: ValueRecord, stack_gap_min: ValueRecord, stack_display_style_gap_min: ValueRecord, stretch_stack_top_shift_up: ValueRecord, stretch_stack_bottom_shift_down: ValueRecord, stretch_stack_gap_above_min: ValueRecord, stretch_stack_gap_below_min: ValueRecord, fraction_numerator_shift_up: ValueRecord, fraction_numerator_display_style_shift_up: ValueRecord, fraction_denominator_shift_down: ValueRecord, fraction_denominator_display_style_shift_down: ValueRecord, fraction_numerator_gap_min: ValueRecord, fraction_num_display_style_gap_min: ValueRecord, fraction_rule_thickness: ValueRecord, fraction_denominator_gap_min: ValueRecord, fraction_denom_display_style_gap_min: ValueRecord, skewed_fraction_horizontal_gap: ValueRecord, skewed_fraction_vertical_gap: ValueRecord, overbar_vertical_gap: ValueRecord, overbar_rule_thickness: ValueRecord, overbar_extra_ascender: ValueRecord, underbar_vertical_gap: ValueRecord, underbar_rule_thickness: ValueRecord, underbar_extra_descender: ValueRecord, radical_vertical_gap: ValueRecord, radical_display_style_vertical_gap: ValueRecord, radical_rule_thickness: ValueRecord, radical_extra_ascender: ValueRecord, radical_kern_before_degree: ValueRecord, radical_kern_after_degree: ValueRecord, radical_degree_bottom_raise_percent: i16,};pub const GlyphVariant = struct { glyph_id: u16, advance_measurement: u16,};pub const GlyphAssemblyPart = struct { glyph_id: u16, advance_offset: u16, full_advance: u16,};pub const GlyphAssembly = struct { parts: []GlyphAssemblyPart, advance_measurement: u16, italics_correction: ValueRecord, pub fn deinit(self: GlyphAssembly, allocator: std.mem.Allocator) void { allocator.free(self.parts); }};pub const GlyphInfo = struct { top_accent_attachment: ?TopAccentAttachment, pub fn init(data: []const u8, math_table: Table, relative_offset: u16) FontError!GlyphInfo { const glyph_info_offset = try mathChildOffset(math_table, relative_offset, math_glyph_info_header_size); const top_accent_relative = try readU16(data, glyph_info_offset + 2); return .{ .top_accent_attachment = if (top_accent_relative == 0) null else try TopAccentAttachment.init(data, math_table, glyph_info_offset, top_accent_relative), }; } pub fn topAccentAttachment(self: GlyphInfo, data: []const u8, glyph_id: u32) FontError!?ValueRecord { const attachment = self.top_accent_attachment orelse return null; return try attachment.value(data, glyph_id); }};const TopAccentAttachment = struct { coverage: layout.Coverage, value_offset: usize, count: u16, fn init(data: []const u8, table: Table, glyph_info_offset: usize, relative_offset: u16) FontError!TopAccentAttachment { const offset = try mathChildOffsetFrom(table, glyph_info_offset, relative_offset, math_top_accent_attachment_header_size); const coverage_relative = try readU16(data, offset); const count = try readU16(data, offset + 2); const value_offset = offset + math_top_accent_attachment_header_size; const values_len = @as(usize, count) * math_value_record_size; const offset_relative = offset - table.offset; if (offset_relative > table.len or math_top_accent_attachment_header_size > table.len - offset_relative or values_len > table.len - offset_relative - math_top_accent_attachment_header_size) return error.InvalidFont; const coverage_offset = try mathChildOffsetFrom(table, offset, coverage_relative, 2); const coverage = layout.Coverage.init(data, coverage_offset) catch return error.InvalidFont; return .{ .coverage = coverage, .value_offset = value_offset, .count = count, }; } fn value(self: TopAccentAttachment, data: []const u8, glyph_id: u32) FontError!?ValueRecord { const index = (self.coverage.index(data, glyph_id) catch return error.InvalidFont) orelse return null; if (index >= self.count) return error.InvalidFont; const offset = self.value_offset + @as(usize, index) * math_value_record_size; return .{ .value = try readI16(data, offset), .device_offset = try readU16(data, offset + 2), }; }};pub const Variants = struct { table: Table, variants_offset: usize, min_connector_overlap: u16, vertical_coverage: ?layout.Coverage, horizontal_coverage: ?layout.Coverage, vertical_glyph_count: u16, horizontal_glyph_count: u16, vertical_construction_offsets: usize, horizontal_construction_offsets: usize, pub fn init(data: []const u8, math_table: Table, relative_offset: u16) FontError!Variants { const variants_offset = try mathChildOffset(math_table, relative_offset, math_variants_header_size); const min_connector_overlap = try readU16(data, variants_offset); const vertical_coverage_relative = try readU16(data, variants_offset + 2); const horizontal_coverage_relative = try readU16(data, variants_offset + 4); const vertical_glyph_count = try readU16(data, variants_offset + 6); const horizontal_glyph_count = try readU16(data, variants_offset + 8); const vertical_construction_offsets = variants_offset + math_variants_header_size; const horizontal_construction_offsets = vertical_construction_offsets + @as(usize, vertical_glyph_count) * 2; const construction_arrays_len = @as(usize, vertical_glyph_count) * 2 + @as(usize, horizontal_glyph_count) * 2; _ = try mathVariantsChildOffset(math_table, variants_offset, math_variants_header_size, construction_arrays_len); const vertical_coverage = try optionalCoverage(data, math_table, variants_offset, vertical_coverage_relative, vertical_glyph_count); const horizontal_coverage = try optionalCoverage(data, math_table, variants_offset, horizontal_coverage_relative, horizontal_glyph_count); var variants = Variants{ .table = math_table, .variants_offset = variants_offset, .min_connector_overlap = min_connector_overlap, .vertical_coverage = vertical_coverage, .horizontal_coverage = horizontal_coverage, .vertical_glyph_count = vertical_glyph_count, .horizontal_glyph_count = horizontal_glyph_count, .vertical_construction_offsets = vertical_construction_offsets, .horizontal_construction_offsets = horizontal_construction_offsets, }; try variants.validateConstructions(data, .vertical); try variants.validateConstructions(data, .horizontal); return variants; } pub fn verticalVariant(self: Variants, data: []const u8, glyph_id: u32, target_measurement: u16) FontError!?GlyphVariant { return self.variant(data, glyph_id, target_measurement, .vertical); } pub fn horizontalVariant(self: Variants, data: []const u8, glyph_id: u32, target_measurement: u16) FontError!?GlyphVariant { return self.variant(data, glyph_id, target_measurement, .horizontal); } pub fn verticalAssemblyAlloc(self: Variants, allocator: std.mem.Allocator, data: []const u8, glyph_id: u32, target_measurement: u16) AssemblyError!?GlyphAssembly { return self.assemblyAlloc(allocator, data, glyph_id, target_measurement, .vertical); } pub fn horizontalAssemblyAlloc(self: Variants, allocator: std.mem.Allocator, data: []const u8, glyph_id: u32, target_measurement: u16) AssemblyError!?GlyphAssembly { return self.assemblyAlloc(allocator, data, glyph_id, target_measurement, .horizontal); } fn variant(self: Variants, data: []const u8, glyph_id: u32, target_measurement: u16, direction: VariantDirection) FontError!?GlyphVariant { const construction_offset = (try self.constructionOffset(data, glyph_id, direction)) orelse return null; return try constructionVariant(data, self.table, construction_offset, target_measurement); } fn assemblyAlloc(self: Variants, allocator: std.mem.Allocator, data: []const u8, glyph_id: u32, target_measurement: u16, direction: VariantDirection) AssemblyError!?GlyphAssembly { const construction_offset = (try self.constructionOffset(data, glyph_id, direction)) orelse return null; return try constructionAssemblyAlloc(allocator, data, self.table, construction_offset, self.min_connector_overlap, target_measurement); } fn constructionOffset(self: Variants, data: []const u8, glyph_id: u32, direction: VariantDirection) FontError!?usize { const coverage = switch (direction) { .vertical => self.vertical_coverage orelse return null, .horizontal => self.horizontal_coverage orelse return null, }; const index = (coverage.index(data, glyph_id) catch return error.InvalidFont) orelse return null; const count = switch (direction) { .vertical => self.vertical_glyph_count, .horizontal => self.horizontal_glyph_count, }; if (index >= count) return error.InvalidFont; const offset_array = switch (direction) { .vertical => self.vertical_construction_offsets, .horizontal => self.horizontal_construction_offsets, }; const relative = try readU16(data, offset_array + @as(usize, index) * 2); return try mathVariantsChildOffset(self.table, self.variants_offset, relative, math_glyph_construction_header_size); } fn validateConstructions(self: Variants, data: []const u8, direction: VariantDirection) FontError!void { const count = switch (direction) { .vertical => self.vertical_glyph_count, .horizontal => self.horizontal_glyph_count, }; const offset_array = switch (direction) { .vertical => self.vertical_construction_offsets, .horizontal => self.horizontal_construction_offsets, }; for (0..count) |index| { const relative = try readU16(data, offset_array + index * 2); const construction_offset = try mathVariantsChildOffset(self.table, self.variants_offset, relative, math_glyph_construction_header_size); try validateConstruction(data, self.table, construction_offset); } }};pub const Math = struct { table: Table, constants_offset: usize, constants: Constants, glyph_info: ?GlyphInfo, variants: ?Variants, pub fn init(data: []const u8, math_table: Table) FontError!Math { if (math_table.len < math_header_size) return error.InvalidFont; const major = try readU16(data, math_table.offset); const minor = try readU16(data, math_table.offset + 2); if (major != 1 or minor != 0) return error.InvalidFont; const constants_relative = try readU16(data, math_table.offset + 4); const glyph_info_relative = try readU16(data, math_table.offset + 6); const variants_relative = try readU16(data, math_table.offset + 8); if (glyph_info_relative > math_table.len or variants_relative > math_table.len) return error.InvalidFont; const constants_offset = try mathChildOffset(math_table, constants_relative, math_constants_size); const glyph_info = if (glyph_info_relative == 0) null else try GlyphInfo.init(data, math_table, glyph_info_relative); const variants = if (variants_relative == 0) null else try Variants.init(data, math_table, variants_relative); return .{ .table = math_table, .constants_offset = constants_offset, .constants = try readConstants(data, constants_offset), .glyph_info = glyph_info, .variants = variants, }; } pub fn topAccentAttachment(self: Math, data: []const u8, glyph_id: u32) FontError!?ValueRecord { const glyph_info = self.glyph_info orelse return null; return try glyph_info.topAccentAttachment(data, glyph_id); } pub fn verticalVariant(self: Math, data: []const u8, glyph_id: u32, target_measurement: u16) FontError!?GlyphVariant { const variants = self.variants orelse return null; return variants.verticalVariant(data, glyph_id, target_measurement); } pub fn horizontalVariant(self: Math, data: []const u8, glyph_id: u32, target_measurement: u16) FontError!?GlyphVariant { const variants = self.variants orelse return null; return variants.horizontalVariant(data, glyph_id, target_measurement); } pub fn verticalAssemblyAlloc(self: Math, allocator: std.mem.Allocator, data: []const u8, glyph_id: u32, target_measurement: u16) AssemblyError!?GlyphAssembly { const variants = self.variants orelse return null; return variants.verticalAssemblyAlloc(allocator, data, glyph_id, target_measurement); } pub fn horizontalAssemblyAlloc(self: Math, allocator: std.mem.Allocator, data: []const u8, glyph_id: u32, target_measurement: u16) AssemblyError!?GlyphAssembly { const variants = self.variants orelse return null; return variants.horizontalAssemblyAlloc(allocator, data, glyph_id, target_measurement); }};const VariantDirection = enum { vertical, horizontal,};const AssemblyError = FontError || std.mem.Allocator.Error;const GlyphPartRecord = struct { glyph_id: u16, start_connector_length: u16, end_connector_length: u16, full_advance: u16, flags: u16, fn extender(self: GlyphPartRecord) bool { return self.flags & glyph_part_extender_flag != 0; }};const AssemblyRange = struct { min_size: i64, max_size: i64,};const AssemblyConnection = struct { max_overlap: u16, min_overlap: u16, extra: u16 = 0, fn capacity(self: AssemblyConnection) u16 { return self.max_overlap - self.min_overlap; }};fn readConstants(data: []const u8, offset: usize) FontError!Constants { var cursor = offset; const script_percent_scale_down = try readI16(data, cursor); cursor += 2; const script_script_percent_scale_down = try readI16(data, cursor); cursor += 2; const delimited_sub_formula_min_height = try readU16(data, cursor); cursor += 2; const display_operator_min_height = try readU16(data, cursor); cursor += 2; return .{ .script_percent_scale_down = script_percent_scale_down, .script_script_percent_scale_down = script_script_percent_scale_down, .delimited_sub_formula_min_height = delimited_sub_formula_min_height, .display_operator_min_height = display_operator_min_height, .math_leading = try readValue(data, &cursor), .axis_height = try readValue(data, &cursor), .accent_base_height = try readValue(data, &cursor), .flattened_accent_base_height = try readValue(data, &cursor), .subscript_shift_down = try readValue(data, &cursor), .subscript_top_max = try readValue(data, &cursor), .subscript_baseline_drop_min = try readValue(data, &cursor), .superscript_shift_up = try readValue(data, &cursor), .superscript_shift_up_cramped = try readValue(data, &cursor), .superscript_bottom_min = try readValue(data, &cursor), .superscript_baseline_drop_max = try readValue(data, &cursor), .sub_superscript_gap_min = try readValue(data, &cursor), .superscript_bottom_max_with_subscript = try readValue(data, &cursor), .space_after_script = try readValue(data, &cursor), .upper_limit_gap_min = try readValue(data, &cursor), .upper_limit_baseline_rise_min = try readValue(data, &cursor), .lower_limit_gap_min = try readValue(data, &cursor), .lower_limit_baseline_drop_min = try readValue(data, &cursor), .stack_top_shift_up = try readValue(data, &cursor), .stack_top_display_style_shift_up = try readValue(data, &cursor), .stack_bottom_shift_down = try readValue(data, &cursor), .stack_bottom_display_style_shift_down = try readValue(data, &cursor), .stack_gap_min = try readValue(data, &cursor), .stack_display_style_gap_min = try readValue(data, &cursor), .stretch_stack_top_shift_up = try readValue(data, &cursor), .stretch_stack_bottom_shift_down = try readValue(data, &cursor), .stretch_stack_gap_above_min = try readValue(data, &cursor), .stretch_stack_gap_below_min = try readValue(data, &cursor), .fraction_numerator_shift_up = try readValue(data, &cursor), .fraction_numerator_display_style_shift_up = try readValue(data, &cursor), .fraction_denominator_shift_down = try readValue(data, &cursor), .fraction_denominator_display_style_shift_down = try readValue(data, &cursor), .fraction_numerator_gap_min = try readValue(data, &cursor), .fraction_num_display_style_gap_min = try readValue(data, &cursor), .fraction_rule_thickness = try readValue(data, &cursor), .fraction_denominator_gap_min = try readValue(data, &cursor), .fraction_denom_display_style_gap_min = try readValue(data, &cursor), .skewed_fraction_horizontal_gap = try readValue(data, &cursor), .skewed_fraction_vertical_gap = try readValue(data, &cursor), .overbar_vertical_gap = try readValue(data, &cursor), .overbar_rule_thickness = try readValue(data, &cursor), .overbar_extra_ascender = try readValue(data, &cursor), .underbar_vertical_gap = try readValue(data, &cursor), .underbar_rule_thickness = try readValue(data, &cursor), .underbar_extra_descender = try readValue(data, &cursor), .radical_vertical_gap = try readValue(data, &cursor), .radical_display_style_vertical_gap = try readValue(data, &cursor), .radical_rule_thickness = try readValue(data, &cursor), .radical_extra_ascender = try readValue(data, &cursor), .radical_kern_before_degree = try readValue(data, &cursor), .radical_kern_after_degree = try readValue(data, &cursor), .radical_degree_bottom_raise_percent = try readI16(data, cursor), };}fn readValue(data: []const u8, cursor: *usize) FontError!ValueRecord { const result = ValueRecord{ .value = try readI16(data, cursor.*), .device_offset = try readU16(data, cursor.* + 2), }; cursor.* += 4; return result;}fn mathChildOffset(table: Table, relative_offset: u16, min_len: usize) FontError!usize { if (relative_offset == 0) return error.InvalidFont; const offset: usize = relative_offset; if (offset > table.len or min_len > table.len - offset) return error.InvalidFont; return table.offset + offset;}fn mathChildOffsetFrom(table: Table, parent_offset: usize, relative_offset: usize, min_len: usize) FontError!usize { if (relative_offset == 0) return error.InvalidFont; if (parent_offset < table.offset) return error.InvalidFont; const parent_relative = parent_offset - table.offset; if (parent_relative > table.len or relative_offset > table.len - parent_relative) return error.InvalidFont; const child_relative = parent_relative + relative_offset; if (min_len > table.len - child_relative) return error.InvalidFont; return table.offset + child_relative;}fn mathVariantsChildOffset(table: Table, variants_offset: usize, relative_offset: usize, min_len: usize) FontError!usize { return mathChildOffsetFrom(table, variants_offset, relative_offset, min_len);}fn mathConstructionChildOffset(table: Table, construction_offset: usize, relative_offset: usize, min_len: usize) FontError!usize { return mathChildOffsetFrom(table, construction_offset, relative_offset, min_len);}fn optionalCoverage(data: []const u8, table: Table, variants_offset: usize, relative_offset: u16, count: u16) FontError!?layout.Coverage { if (count == 0) return null; const coverage_offset = try mathVariantsChildOffset(table, variants_offset, relative_offset, 2); return layout.Coverage.init(data, coverage_offset) catch error.InvalidFont;}fn validateConstruction(data: []const u8, table: Table, construction_offset: usize) FontError!void { const construction_relative = construction_offset - table.offset; if (construction_relative > table.len or math_glyph_construction_header_size > table.len - construction_relative) return error.InvalidFont; const assembly_relative = try readU16(data, construction_offset); if (assembly_relative != 0) { const assembly_offset = try mathConstructionChildOffset(table, construction_offset, assembly_relative, math_glyph_assembly_header_size); try validateGlyphAssembly(data, table, assembly_offset); } _ = try constructionVariant(data, table, construction_offset, std.math.maxInt(u16));}fn constructionVariant(data: []const u8, table: Table, construction_offset: usize, target_measurement: u16) FontError!?GlyphVariant { const construction_relative = construction_offset - table.offset; if (construction_relative > table.len or math_glyph_construction_header_size > table.len - construction_relative) return error.InvalidFont; const variant_count = try readU16(data, construction_offset + 2); const records_len = @as(usize, variant_count) * math_glyph_variant_size; if (records_len > table.len - construction_relative - math_glyph_construction_header_size) return error.InvalidFont; var fallback: ?GlyphVariant = null; var best: ?GlyphVariant = null; for (0..variant_count) |index| { const record_offset = construction_offset + math_glyph_construction_header_size + index * math_glyph_variant_size; const variant = GlyphVariant{ .glyph_id = try readU16(data, record_offset), .advance_measurement = try readU16(data, record_offset + 2), }; if (fallback == null or variant.advance_measurement > fallback.?.advance_measurement) fallback = variant; if (variant.advance_measurement >= target_measurement and (best == null or variant.advance_measurement < best.?.advance_measurement)) { best = variant; } } return best orelse fallback;}fn constructionAssemblyAlloc(allocator: std.mem.Allocator, data: []const u8, table: Table, construction_offset: usize, min_connector_overlap: u16, target_measurement: u16) AssemblyError!?GlyphAssembly { const assembly_relative = try readU16(data, construction_offset); if (assembly_relative == 0) return null; const assembly_offset = try mathConstructionChildOffset(table, construction_offset, assembly_relative, math_glyph_assembly_header_size); return try glyphAssemblyAlloc(allocator, data, table, assembly_offset, min_connector_overlap, target_measurement);}fn validateGlyphAssembly(data: []const u8, table: Table, assembly_offset: usize) FontError!void { const assembly_relative = assembly_offset - table.offset; if (assembly_relative > table.len or math_glyph_assembly_header_size > table.len - assembly_relative) return error.InvalidFont; const part_count = try readU16(data, assembly_offset + 4); const records_len = @as(usize, part_count) * math_glyph_part_size; if (records_len > table.len - assembly_relative - math_glyph_assembly_header_size) return error.InvalidFont; for (0..part_count) |index| { _ = try readGlyphPart(data, assembly_offset + math_glyph_assembly_header_size + index * math_glyph_part_size); }}fn glyphAssemblyAlloc(allocator: std.mem.Allocator, data: []const u8, table: Table, assembly_offset: usize, min_connector_overlap: u16, target_measurement: u16) AssemblyError!?GlyphAssembly { try validateGlyphAssembly(data, table, assembly_offset); const italics_correction = ValueRecord{ .value = try readI16(data, assembly_offset), .device_offset = try readU16(data, assembly_offset + 2), }; const part_count = try readU16(data, assembly_offset + 4); if (part_count == 0) return null; const records = try allocator.alloc(GlyphPartRecord, part_count); defer allocator.free(records); var has_extender = false; for (records, 0..) |*record, index| { record.* = try readGlyphPart(data, assembly_offset + math_glyph_assembly_header_size + index * math_glyph_part_size); has_extender = has_extender or record.extender(); } var repeat_count: usize = 0; while (true) : (repeat_count += 1) { const sequence = try assemblySequenceAlloc(allocator, records, repeat_count); defer allocator.free(sequence); if (sequence.len == 0) { if (!has_extender) return null; continue; } const range = try assemblyRange(sequence, min_connector_overlap); if (@as(i64, target_measurement) <= range.max_size) { return try buildAssembly(allocator, sequence, min_connector_overlap, target_measurement, italics_correction); } if (!has_extender) return null; if (assemblySequenceLen(records, repeat_count + 1) > max_assembly_parts) return error.InvalidFont; }}fn readGlyphPart(data: []const u8, offset: usize) FontError!GlyphPartRecord { const part = GlyphPartRecord{ .glyph_id = try readU16(data, offset), .start_connector_length = try readU16(data, offset + 2), .end_connector_length = try readU16(data, offset + 4), .full_advance = try readU16(data, offset + 6), .flags = try readU16(data, offset + 8), }; if (part.full_advance == 0) return error.InvalidFont; if (part.start_connector_length > part.full_advance or part.end_connector_length > part.full_advance) return error.InvalidFont; if (part.flags & 0xfffe != 0) return error.InvalidFont; return part;}fn assemblySequenceLen(records: []const GlyphPartRecord, repeat_count: usize) usize { var len: usize = 0; for (records) |record| { len += if (record.extender()) repeat_count else 1; } return len;}fn assemblySequenceAlloc(allocator: std.mem.Allocator, records: []const GlyphPartRecord, repeat_count: usize) AssemblyError![]GlyphPartRecord { const len = assemblySequenceLen(records, repeat_count); if (len > max_assembly_parts) return error.InvalidFont; const sequence = try allocator.alloc(GlyphPartRecord, len); var index: usize = 0; for (records) |record| { const count = if (record.extender()) repeat_count else 1; for (0..count) |_| { sequence[index] = record; index += 1; } } return sequence;}fn assemblyRange(parts: []const GlyphPartRecord, min_connector_overlap: u16) FontError!AssemblyRange { if (parts.len == 0) return error.InvalidFont; var min_size: i64 = 0; var max_size: i64 = 0; for (parts) |part| { min_size += part.full_advance; max_size += part.full_advance; } for (0..parts.len - 1) |index| { const connection = assemblyConnection(parts[index], parts[index + 1], min_connector_overlap); min_size -= connection.max_overlap; max_size -= connection.min_overlap; } if (min_size <= 0 or max_size < min_size) return error.InvalidFont; return .{ .min_size = min_size, .max_size = max_size };}fn buildAssembly(allocator: std.mem.Allocator, parts: []const GlyphPartRecord, min_connector_overlap: u16, target_measurement: u16, italics_correction: ValueRecord) AssemblyError!GlyphAssembly { const range = try assemblyRange(parts, min_connector_overlap); const connection_count = parts.len - 1; const connections = try allocator.alloc(AssemblyConnection, connection_count); defer allocator.free(connections); for (connections, 0..) |*connection, index| { connection.* = assemblyConnection(parts[index], parts[index + 1], min_connector_overlap); } var requested_extra: i64 = 0; if (@as(i64, target_measurement) > range.min_size) requested_extra = @as(i64, target_measurement) - range.min_size; requested_extra = @min(requested_extra, range.max_size - range.min_size); distributeConnectionExtra(connections, requested_extra); const out = try allocator.alloc(GlyphAssemblyPart, parts.len); errdefer allocator.free(out); var advance_offset: i64 = 0; for (parts, 0..) |part, index| { if (advance_offset < 0 or advance_offset > std.math.maxInt(u16)) return error.InvalidFont; out[index] = .{ .glyph_id = part.glyph_id, .advance_offset = @intCast(advance_offset), .full_advance = part.full_advance, }; if (index < connections.len) { const overlap = @as(i64, connections[index].max_overlap) - connections[index].extra; advance_offset += @as(i64, part.full_advance) - overlap; } } const advance_measurement = advance_offset + parts[parts.len - 1].full_advance; if (advance_measurement <= 0 or advance_measurement > std.math.maxInt(u16)) return error.InvalidFont; return .{ .parts = out, .advance_measurement = @intCast(advance_measurement), .italics_correction = italics_correction, };}fn assemblyConnection(left: GlyphPartRecord, right: GlyphPartRecord, min_connector_overlap: u16) AssemblyConnection { const max_overlap = @min(left.end_connector_length, right.start_connector_length); return .{ .max_overlap = max_overlap, .min_overlap = @min(min_connector_overlap, max_overlap), };}fn distributeConnectionExtra(connections: []AssemblyConnection, requested_extra: i64) void { if (connections.len == 0 or requested_extra <= 0) return; var remaining = requested_extra; while (remaining > 0) { var eligible: usize = 0; for (connections) |connection| { if (connection.extra < connection.capacity()) eligible += 1; } if (eligible == 0) return; var step = @divTrunc(remaining, @as(i64, @intCast(eligible))); if (step <= 0) step = 1; for (connections) |*connection| { if (remaining <= 0) return; const capacity = connection.capacity(); if (connection.extra >= capacity) continue; const available = capacity - connection.extra; const add: u16 = @intCast(@min(@as(i64, available), step)); connection.extra += add; remaining -= add; } }}fn writeU16(bytes: []u8, offset: usize, value: u16) void { std.mem.writeInt(u16, bytes[offset..][0..2], value, .big);}fn writeI16(bytes: []u8, offset: usize, value: i16) void { std.mem.writeInt(i16, bytes[offset..][0..2], value, .big);}fn writeValue(bytes: []u8, constants_offset: usize, field_index: usize, value: i16, device_offset: u16) void { const offset = constants_offset + 8 + (field_index - 1) * 4; writeI16(bytes, offset, value); writeU16(bytes, offset + 2, device_offset);}fn writeVariant(bytes: []u8, offset: usize, glyph_id: u16, advance_measurement: u16) void { writeU16(bytes, offset, glyph_id); writeU16(bytes, offset + 2, advance_measurement);}fn writePart(bytes: []u8, offset: usize, glyph_id: u16, start_connector_length: u16, end_connector_length: u16, full_advance: u16, flags: u16) void { writeU16(bytes, offset, glyph_id); writeU16(bytes, offset + 2, start_connector_length); writeU16(bytes, offset + 4, end_connector_length); writeU16(bytes, offset + 6, full_advance); writeU16(bytes, offset + 8, flags);}test "MATH constants parse script, fraction, and radical metrics" { var bytes = @as([(math_header_size + math_constants_size)]u8, @splat(0)); writeU16(&bytes, 0, 1); writeU16(&bytes, 2, 0); writeU16(&bytes, 4, math_header_size); const constants_offset = math_header_size; writeI16(&bytes, constants_offset, 80); writeI16(&bytes, constants_offset + 2, 60); writeU16(&bytes, constants_offset + 4, 1500); writeU16(&bytes, constants_offset + 6, 2000); writeValue(&bytes, constants_offset, 6, 12, 0); writeValue(&bytes, constants_offset, 7, 34, 0); writeValue(&bytes, constants_offset, 11, 9, 0); writeValue(&bytes, constants_offset, 13, 18, 0); writeValue(&bytes, constants_offset, 20, 61, 0); writeValue(&bytes, constants_offset, 22, 62, 0); writeValue(&bytes, constants_offset, 24, 63, 0); writeValue(&bytes, constants_offset, 35, 55, 7); writeValue(&bytes, constants_offset, 41, 44, 0); writeValue(&bytes, constants_offset, 46, 120, 0); writeValue(&bytes, constants_offset, 48, 50, 0); writeValue(&bytes, constants_offset, 51, -90, 0); writeI16(&bytes, constants_offset + math_constants_size - 2, 65); const parsed = try Math.init(&bytes, .{ .offset = 0, .len = bytes.len }); try std.testing.expectEqual(@as(i16, 80), parsed.constants.script_percent_scale_down); try std.testing.expectEqual(@as(i16, 60), parsed.constants.script_script_percent_scale_down); try std.testing.expectEqual(@as(u16, 1500), parsed.constants.delimited_sub_formula_min_height); try std.testing.expectEqual(@as(u16, 2000), parsed.constants.display_operator_min_height); try std.testing.expectEqual(@as(i16, 12), parsed.constants.subscript_top_max.value); try std.testing.expectEqual(@as(i16, 34), parsed.constants.subscript_baseline_drop_min.value); try std.testing.expectEqual(@as(i16, 9), parsed.constants.superscript_baseline_drop_max.value); try std.testing.expectEqual(@as(i16, 18), parsed.constants.superscript_bottom_max_with_subscript.value); try std.testing.expectEqual(@as(i16, 61), parsed.constants.stack_top_display_style_shift_up.value); try std.testing.expectEqual(@as(i16, 62), parsed.constants.stack_bottom_display_style_shift_down.value); try std.testing.expectEqual(@as(i16, 63), parsed.constants.stack_display_style_gap_min.value); try std.testing.expectEqual(@as(i16, 55), parsed.constants.fraction_rule_thickness.value); try std.testing.expectEqual(@as(u16, 7), parsed.constants.fraction_rule_thickness.device_offset); try std.testing.expectEqual(@as(i16, 44), parsed.constants.overbar_rule_thickness.value); try std.testing.expectEqual(@as(i16, 120), parsed.constants.radical_vertical_gap.value); try std.testing.expectEqual(@as(i16, 50), parsed.constants.radical_rule_thickness.value); try std.testing.expectEqual(@as(i16, -90), parsed.constants.radical_kern_after_degree.value); try std.testing.expectEqual(@as(i16, 65), parsed.constants.radical_degree_bottom_raise_percent);}test "MATH constants reject truncated table" { var bytes = @as([math_header_size]u8, @splat(0)); writeU16(&bytes, 0, 1); writeU16(&bytes, 2, 0); writeU16(&bytes, 4, math_header_size); try std.testing.expectError(error.InvalidFont, Math.init(&bytes, .{ .offset = 0, .len = bytes.len }));}test "MATH glyph info exposes top accent attachments" { var bytes = @as([(math_header_size + math_constants_size + math_glyph_info_header_size + 32)]u8, @splat(0)); writeU16(&bytes, 0, 1); writeU16(&bytes, 2, 0); writeU16(&bytes, 4, math_header_size); const glyph_info_offset = math_header_size + math_constants_size; writeU16(&bytes, 6, glyph_info_offset); writeU16(&bytes, glyph_info_offset + 2, math_glyph_info_header_size); const top_offset = glyph_info_offset + math_glyph_info_header_size; writeU16(&bytes, top_offset, 12); writeU16(&bytes, top_offset + 2, 2); writeI16(&bytes, top_offset + 4, 275); writeU16(&bytes, top_offset + 6, 0); writeI16(&bytes, top_offset + 8, 125); writeU16(&bytes, top_offset + 10, 0); writeU16(&bytes, top_offset + 12, 1); writeU16(&bytes, top_offset + 14, 2); writeU16(&bytes, top_offset + 16, 40); writeU16(&bytes, top_offset + 18, 60); const parsed = try Math.init(&bytes, .{ .offset = 0, .len = bytes.len }); const first = (try parsed.topAccentAttachment(&bytes, 40)).?; const second = (try parsed.topAccentAttachment(&bytes, 60)).?; try std.testing.expectEqual(@as(i16, 275), first.value); try std.testing.expectEqual(@as(i16, 125), second.value); try std.testing.expectEqual(@as(?ValueRecord, null), try parsed.topAccentAttachment(&bytes, 41));}test "MATH variants choose ready-made vertical glyph by target measurement" { var bytes = @as([(math_header_size + math_constants_size + 64)]u8, @splat(0)); writeU16(&bytes, 0, 1); writeU16(&bytes, 2, 0); writeU16(&bytes, 4, math_header_size); const variants_offset = math_header_size + math_constants_size; writeU16(&bytes, 8, variants_offset); writeU16(&bytes, variants_offset, 5); writeU16(&bytes, variants_offset + 2, 12); writeU16(&bytes, variants_offset + 4, 0); writeU16(&bytes, variants_offset + 6, 1); writeU16(&bytes, variants_offset + 8, 0); writeU16(&bytes, variants_offset + 10, 18); writeU16(&bytes, variants_offset + 12, 1); writeU16(&bytes, variants_offset + 14, 1); writeU16(&bytes, variants_offset + 16, 40); writeU16(&bytes, variants_offset + 18, 0); writeU16(&bytes, variants_offset + 20, 3); writeVariant(&bytes, variants_offset + 22, 41, 700); writeVariant(&bytes, variants_offset + 26, 42, 1000); writeVariant(&bytes, variants_offset + 30, 43, 1300); const parsed = try Math.init(&bytes, .{ .offset = 0, .len = bytes.len }); const exact = (try parsed.verticalVariant(&bytes, 40, 1000)).?; const middle = (try parsed.verticalVariant(&bytes, 40, 800)).?; const too_large = (try parsed.verticalVariant(&bytes, 40, 2000)).?; try std.testing.expectEqual(@as(u16, 42), exact.glyph_id); try std.testing.expectEqual(@as(u16, 1000), exact.advance_measurement); try std.testing.expectEqual(@as(u16, 42), middle.glyph_id); try std.testing.expectEqual(@as(u16, 43), too_large.glyph_id); try std.testing.expectEqual(@as(?GlyphVariant, null), try parsed.verticalVariant(&bytes, 39, 800));}test "MATH variants choose ready-made horizontal glyph by target measurement" { var bytes = @as([(math_header_size + math_constants_size + 64)]u8, @splat(0)); writeU16(&bytes, 0, 1); writeU16(&bytes, 2, 0); writeU16(&bytes, 4, math_header_size); const variants_offset = math_header_size + math_constants_size; writeU16(&bytes, 8, variants_offset); writeU16(&bytes, variants_offset, 5); writeU16(&bytes, variants_offset + 2, 0); writeU16(&bytes, variants_offset + 4, 12); writeU16(&bytes, variants_offset + 6, 0); writeU16(&bytes, variants_offset + 8, 1); writeU16(&bytes, variants_offset + 10, 18); writeU16(&bytes, variants_offset + 12, 1); writeU16(&bytes, variants_offset + 14, 1); writeU16(&bytes, variants_offset + 16, 40); writeU16(&bytes, variants_offset + 18, 0); writeU16(&bytes, variants_offset + 20, 3); writeVariant(&bytes, variants_offset + 22, 41, 700); writeVariant(&bytes, variants_offset + 26, 42, 1000); writeVariant(&bytes, variants_offset + 30, 43, 1300); const parsed = try Math.init(&bytes, .{ .offset = 0, .len = bytes.len }); const exact = (try parsed.horizontalVariant(&bytes, 40, 1000)).?; const middle = (try parsed.horizontalVariant(&bytes, 40, 800)).?; const too_large = (try parsed.horizontalVariant(&bytes, 40, 2000)).?; try std.testing.expectEqual(@as(u16, 42), exact.glyph_id); try std.testing.expectEqual(@as(u16, 1000), exact.advance_measurement); try std.testing.expectEqual(@as(u16, 42), middle.glyph_id); try std.testing.expectEqual(@as(u16, 43), too_large.glyph_id); try std.testing.expectEqual(@as(?GlyphVariant, null), try parsed.horizontalVariant(&bytes, 39, 800));}test "MATH assembly repeats extenders and distributes connector overlap" { var bytes = @as([(math_header_size + math_constants_size + 128)]u8, @splat(0)); writeU16(&bytes, 0, 1); writeU16(&bytes, 2, 0); writeU16(&bytes, 4, math_header_size); const variants_offset = math_header_size + math_constants_size; writeU16(&bytes, 8, variants_offset); writeU16(&bytes, variants_offset, 50); writeU16(&bytes, variants_offset + 2, 12); writeU16(&bytes, variants_offset + 4, 0); writeU16(&bytes, variants_offset + 6, 1); writeU16(&bytes, variants_offset + 8, 0); writeU16(&bytes, variants_offset + 10, 18); writeU16(&bytes, variants_offset + 12, 1); writeU16(&bytes, variants_offset + 14, 1); writeU16(&bytes, variants_offset + 16, 40); writeU16(&bytes, variants_offset + 18, 4); writeU16(&bytes, variants_offset + 20, 0); writeI16(&bytes, variants_offset + 22, 0); writeU16(&bytes, variants_offset + 24, 0); writeU16(&bytes, variants_offset + 26, 3); writePart(&bytes, variants_offset + 28, 50, 0, 200, 500, 0); writePart(&bytes, variants_offset + 38, 51, 200, 200, 300, glyph_part_extender_flag); writePart(&bytes, variants_offset + 48, 52, 200, 0, 500, 0); const parsed = try Math.init(&bytes, .{ .offset = 0, .len = bytes.len }); var assembly = (try parsed.verticalAssemblyAlloc(std.testing.allocator, &bytes, 40, 1300)).?; defer assembly.deinit(std.testing.allocator); try std.testing.expectEqual(@as(u16, 1300), assembly.advance_measurement); try std.testing.expectEqual(@as(usize, 4), assembly.parts.len); try std.testing.expectEqual(@as(u16, 50), assembly.parts[0].glyph_id); try std.testing.expectEqual(@as(u16, 0), assembly.parts[0].advance_offset); try std.testing.expectEqual(@as(u16, 51), assembly.parts[1].glyph_id); try std.testing.expectEqual(@as(u16, 400), assembly.parts[1].advance_offset); try std.testing.expectEqual(@as(u16, 51), assembly.parts[2].glyph_id); try std.testing.expectEqual(@as(u16, 600), assembly.parts[2].advance_offset); try std.testing.expectEqual(@as(u16, 52), assembly.parts[3].glyph_id); try std.testing.expectEqual(@as(u16, 800), assembly.parts[3].advance_offset);}test "MATH horizontal assembly repeats extenders and distributes connector overlap" { var bytes = @as([(math_header_size + math_constants_size + 128)]u8, @splat(0)); writeU16(&bytes, 0, 1); writeU16(&bytes, 2, 0); writeU16(&bytes, 4, math_header_size); const variants_offset = math_header_size + math_constants_size; writeU16(&bytes, 8, variants_offset); writeU16(&bytes, variants_offset, 50); writeU16(&bytes, variants_offset + 2, 0); writeU16(&bytes, variants_offset + 4, 12); writeU16(&bytes, variants_offset + 6, 0); writeU16(&bytes, variants_offset + 8, 1); writeU16(&bytes, variants_offset + 10, 18); writeU16(&bytes, variants_offset + 12, 1); writeU16(&bytes, variants_offset + 14, 1); writeU16(&bytes, variants_offset + 16, 40); writeU16(&bytes, variants_offset + 18, 4); writeU16(&bytes, variants_offset + 20, 0); writeI16(&bytes, variants_offset + 22, 0); writeU16(&bytes, variants_offset + 24, 0); writeU16(&bytes, variants_offset + 26, 3); writePart(&bytes, variants_offset + 28, 50, 0, 200, 500, 0); writePart(&bytes, variants_offset + 38, 51, 200, 200, 300, glyph_part_extender_flag); writePart(&bytes, variants_offset + 48, 52, 200, 0, 500, 0); const parsed = try Math.init(&bytes, .{ .offset = 0, .len = bytes.len }); var assembly = (try parsed.horizontalAssemblyAlloc(std.testing.allocator, &bytes, 40, 1300)).?; defer assembly.deinit(std.testing.allocator); try std.testing.expectEqual(@as(u16, 1300), assembly.advance_measurement); try std.testing.expectEqual(@as(usize, 4), assembly.parts.len); try std.testing.expectEqual(@as(u16, 50), assembly.parts[0].glyph_id); try std.testing.expectEqual(@as(u16, 0), assembly.parts[0].advance_offset); try std.testing.expectEqual(@as(u16, 51), assembly.parts[1].glyph_id); try std.testing.expectEqual(@as(u16, 400), assembly.parts[1].advance_offset); try std.testing.expectEqual(@as(u16, 51), assembly.parts[2].glyph_id); try std.testing.expectEqual(@as(u16, 600), assembly.parts[2].advance_offset); try std.testing.expectEqual(@as(u16, 52), assembly.parts[3].glyph_id); try std.testing.expectEqual(@as(u16, 800), assembly.parts[3].advance_offset);}Source: lib/filigree/src/font/root.zig:12
zig
pub const math = @import("math.zig");Complete caller list for font.math.Math.init
8 direct callers.
lib.filigree.src.font.face.initDirectoryImpl[function] — private source atlib/filigree/src/font/face.zig:217in nearest public ownertiny.filigree.font.facelib.filigree.src.font.math.test_MATH_assembly_repeats_extenders_and_distributes_connector_overlap[function] — test source atlib/filigree/src/font/math.zig:847in nearest public ownertiny.filigree.font.mathlib.filigree.src.font.math.test_MATH_constants_parse_script,_fraction,_and_radical_metrics[function] — test source atlib/filigree/src/font/math.zig:695in nearest public ownertiny.filigree.font.mathlib.filigree.src.font.math.test_MATH_constants_reject_truncated_table[function] — test source atlib/filigree/src/font/math.zig:741in nearest public ownertiny.filigree.font.mathlib.filigree.src.font.math.test_MATH_glyph_info_exposes_top_accent_attachments[function] — test source atlib/filigree/src/font/math.zig:750in nearest public ownertiny.filigree.font.mathlib.filigree.src.font.math.test_MATH_horizontal_assembly_repeats_extenders_and_distributes_connector_overlap[function] — test source atlib/filigree/src/font/math.zig:888in nearest public ownertiny.filigree.font.mathlib.filigree.src.font.math.test_MATH_variants_choose_ready-made_horizontal_glyph_by_target_measurement[function] — test source atlib/filigree/src/font/math.zig:813in nearest public ownertiny.filigree.font.mathlib.filigree.src.font.math.test_MATH_variants_choose_ready-made_vertical_glyph_by_target_measurement[function] — test source atlib/filigree/src/font/math.zig:779in nearest public ownertiny.filigree.font.math
Audit
| Definitions | 17 |
|---|---|
| Public names | 20 |
| Members | 15 |
| Version | 26.7.0 |
| Revision | daab053ee433 |