tiny.filigree.font.face
Defined in font.
API (1)
Types and contracts
Public types and contracts.
Source
Source: lib/filigree/src/font/face.zig
zig
const std = @import("std");const binary = @import("binary.zig");const cmap_owner = @import("cmap.zig");const directory = @import("directory.zig");const gdef_owner = @import("gdef.zig");const gsub_owner = @import("gsub.zig");const gpos_owner = @import("gpos.zig");const kern_owner = @import("kern.zig");const lookups = @import("lookups.zig");const math_owner = @import("math.zig");const metrics_table = @import("metrics.zig");const model = @import("model.zig");const names = @import("names.zig");const variations = @import("variations.zig");const test_font = @import("../fixture/root.zig");const fixture_binary = test_font.binary;const Cmap = cmap_owner.Cmap;const FontError = model.FontError;const Table = binary.Table;const Tag = model.Tag;const readI8 = binary.readI8;const readI16 = binary.readI16;const readI32 = binary.readI32;const readU16 = binary.readU16;const readU24 = binary.readU24;const readU32 = binary.readU32;const table = directory.table;const tableAt = directory.tableAt;const tableAtTag = directory.tableAtTag;const tableOptionalAt = directory.optionalAt;const tableOptionalAtTag = directory.optionalAtTag;const fontDirectoryOffset = directory.fontDirectoryOffset;const tag = model.tag;const tagLiteral = model.tag;const GlyphClass = model.GlyphClass;const LigatureCaret = model.LigatureCaret;const FeatureRange = model.FeatureRange;const VariationAxis = model.VariationAxis;const VariationSetting = model.VariationSetting;const NormalizedVariation = model.NormalizedVariation;const LayoutSelection = model.LayoutSelection;const defaultScriptTag = model.defaultScriptTag;const featureEnabled = model.featureEnabled;const fixed16ToFloat = model.fixed16ToFloat;const noGposVariationIndex = model.noGposVariationIndex;const GsubSubstitution = model.GsubSubstitution;const GposValueAdjustment = model.GposValueAdjustment;const GposPairAdjustment = model.GposPairAdjustment;const GposPairAdjustmentBefore = model.GposPairAdjustmentBefore;const GposContextualPairAdjustment = model.GposContextualPairAdjustment;const GposSingleAdjustment = model.GposSingleAdjustment;const GposCursiveAttachment = model.GposCursiveAttachment;const Gpos = gpos_owner.Gpos;const Gdef = gdef_owner.Gdef;const Gsub = gsub_owner.Gsub;const Kern = kern_owner.Kern;const Math = math_owner.Math;const MathConstants = math_owner.Constants;const MathGlyphAssembly = math_owner.GlyphAssembly;const MathGlyphVariant = math_owner.GlyphVariant;const MathValueRecord = math_owner.ValueRecord;const VerticalMetrics = metrics_table.VerticalMetrics;const Vorg = metrics_table.Vorg;const HorizontalMetrics = metrics_table.HorizontalMetrics;const verticalMetrics = metrics_table.verticalMetrics;const horizontalTypoMetrics = metrics_table.horizontalTypoMetrics;const bestNameRecord = names.bestNameRecord;const utf16BeToUtf8Alloc = names.utf16BeToUtf8Alloc;const lookup_use_mark_filtering_set = lookups.use_mark_filtering_set;const lookupGlyphIgnored = lookups.lookupGlyphIgnored;const Fvar = variations.Fvar;const Avar = variations.Avar;const Hvar = variations.Hvar;const Vvar = variations.Vvar;const Mvar = variations.Mvar;const DeltaSetIndex = variations.DeltaSetIndex;const ItemVariationStore = variations.ItemVariationStore;const fixed16FromFloat = variations.fixed16FromFloat;const normalizeVariationAxis = variations.normalizeVariationAxis;const fixed16ToF2Dot14 = variations.fixed16ToF2Dot14;const clampDesignMetric = variations.clampDesignMetric;const clampI32 = variations.clampI32;pub const Face = struct { data: []const u8, directory_offset: usize, units_per_em: u16, ascender: i16, descender: i16, line_gap: i16, typo_metrics: ?HorizontalMetrics, num_glyphs: u16, num_h_metrics: u16, num_v_metrics: u16, cmap: Cmap, hmtx: Table, vmtx: ?Table, vorg: ?Vorg, kern: ?Kern, gsub: ?Gsub, gpos: ?Gpos, gdef: ?Gdef, fvar: ?Fvar, avar: ?Avar, hvar: ?Hvar, vvar: ?Vvar, mvar: ?Mvar, math: ?Math, pub const init = initImpl; pub const initAt = initAtImpl; const initDirectory = initDirectoryImpl; pub const glyphId = glyphIdImpl; pub const tableSlice = tableSliceImpl; pub const tableSliceByTag = tableSliceByTagImpl; pub const nameUtf8Alloc = nameUtf8AllocImpl; pub const glyphIdForVariation = glyphIdForVariationImpl; pub const hasVariationSequences = hasVariationSequencesImpl; pub const variationAxisCount = variationAxisCountImpl; pub const variationAxis = variationAxisImpl; pub const variationAxisFor = variationAxisForImpl; pub const validateVariations = validateVariationsImpl; pub const normalizedVariation = normalizedVariationImpl; pub const normalizedCoordinateAt = normalizedCoordinateAtImpl; pub const advanceWidth = advanceWidthImpl; pub const advanceWidthForVariations = advanceWidthForVariationsImpl; pub const advanceHeight = advanceHeightImpl; pub const advanceHeightForVariations = advanceHeightForVariationsImpl; pub const verticalOriginY = verticalOriginYImpl; pub const metricVariationDelta = metricVariationDeltaImpl; pub const ascenderForVariations = ascenderForVariationsImpl; pub const descenderForVariations = descenderForVariationsImpl; pub const lineGapForVariations = lineGapForVariationsImpl; pub const heightUnitsForVariations = heightUnitsForVariationsImpl; pub const kerning = kerningImpl; pub const hasGpos = hasGposImpl; pub const gposLookupCount = gposLookupCountImpl; pub const gposLookupType = gposLookupTypeImpl; pub const gposVariationValue = gposVariationValueImpl; const variationContext = variationContextImpl; pub const hasGsub = hasGsubImpl; pub const usesAutomaticFractions = usesAutomaticFractionsImpl; pub const usesArabicJoiningForms = usesArabicJoiningFormsImpl; pub const gposPairAdjustment = gposPairAdjustmentImpl; pub const gposPairAdjustmentFor = gposPairAdjustmentForImpl; pub const gposPairAdjustmentBeforeFor = gposPairAdjustmentBeforeForImpl; pub const gposPairAdjustmentBeforeForRange = gposPairAdjustmentBeforeForRangeImpl; pub const gposPairAdjustmentBeforeAtLookupForRange = gposPairAdjustmentBeforeAtLookupForRangeImpl; pub const gposContextualPairAdjustmentAtFor = gposContextualPairAdjustmentAtForImpl; pub const gposContextualPairAdjustmentAtForRange = gposContextualPairAdjustmentAtForRangeImpl; pub const gposContextualPairAdjustmentAtLookupForRange = gposContextualPairAdjustmentAtLookupForRangeImpl; pub const hasGposContextualPairAdjustmentFor = hasGposContextualPairAdjustmentForImpl; pub const gposSingleAdjustment = gposSingleAdjustmentImpl; pub const gposSingleAdjustmentFor = gposSingleAdjustmentForImpl; pub const gposSingleAdjustmentAtFor = gposSingleAdjustmentAtForImpl; pub const gposSingleAdjustmentAtForRange = gposSingleAdjustmentAtForRangeImpl; pub const gposSingleAdjustmentAtLookupForRange = gposSingleAdjustmentAtLookupForRangeImpl; pub const gposCursiveAttachment = gposCursiveAttachmentImpl; pub const gposCursiveAttachmentFor = gposCursiveAttachmentForImpl; pub const gposCursiveAttachmentForRange = gposCursiveAttachmentForRangeImpl; pub const gposCursiveAttachmentAtLookupForRange = gposCursiveAttachmentAtLookupForRangeImpl; pub const gposMarkToBaseAdjustment = gposMarkToBaseAdjustmentImpl; pub const gposMarkToBaseAdjustmentFor = gposMarkToBaseAdjustmentForImpl; pub const gposMarkToBaseAdjustmentForRange = gposMarkToBaseAdjustmentForRangeImpl; pub const gposMarkToBaseAdjustmentAtLookupForRange = gposMarkToBaseAdjustmentAtLookupForRangeImpl; pub const gposMarkToLigatureAdjustment = gposMarkToLigatureAdjustmentImpl; pub const gposMarkToLigatureAdjustmentFor = gposMarkToLigatureAdjustmentForImpl; pub const gposMarkToLigatureAdjustmentForRange = gposMarkToLigatureAdjustmentForRangeImpl; pub const gposMarkToLigatureAdjustmentAtLookupForRange = gposMarkToLigatureAdjustmentAtLookupForRangeImpl; pub const gposMarkToMarkAdjustment = gposMarkToMarkAdjustmentImpl; pub const gposMarkToMarkAdjustmentFor = gposMarkToMarkAdjustmentForImpl; pub const gposMarkToMarkAdjustmentForRange = gposMarkToMarkAdjustmentForRangeImpl; pub const gposMarkToMarkAdjustmentAtLookupForRange = gposMarkToMarkAdjustmentAtLookupForRangeImpl; pub const hasGposMarkToBase = hasGposMarkToBaseImpl; pub const hasGposMarkToBaseFor = hasGposMarkToBaseForImpl; pub const hasGposMarkToLigature = hasGposMarkToLigatureImpl; pub const hasGposMarkToLigatureFor = hasGposMarkToLigatureForImpl; pub const hasGposSingleAdjustment = hasGposSingleAdjustmentImpl; pub const hasGposSingleAdjustmentFor = hasGposSingleAdjustmentForImpl; pub const hasGposCursiveAttachment = hasGposCursiveAttachmentImpl; pub const hasGposCursiveAttachmentFor = hasGposCursiveAttachmentForImpl; pub const hasGposMarkToMark = hasGposMarkToMarkImpl; pub const hasGposMarkToMarkFor = hasGposMarkToMarkForImpl; pub const glyphClass = glyphClassImpl; pub const ligatureCaretCount = ligatureCaretCountImpl; pub const ligatureCaret = ligatureCaretImpl; pub const substituteGlyph = substituteGlyphImpl; pub const substituteGlyphFor = substituteGlyphForImpl; pub const gsubLookupCount = gsubLookupCountImpl; pub const gsubLookupIsReverseFor = gsubLookupIsReverseForImpl; pub const substitutionAt = substitutionAtImpl; pub const substitutionAtFor = substitutionAtForImpl; pub const substitutionAtForRange = substitutionAtForRangeImpl; pub const substitutionGlyph = substitutionGlyphImpl; pub const mathConstants = mathConstantsImpl; pub const mathHorizontalAssemblyAlloc = mathHorizontalAssemblyAllocImpl; pub const mathHorizontalVariant = mathHorizontalVariantImpl; pub const mathTopAccentAttachment = mathTopAccentAttachmentImpl; pub const mathVerticalAssemblyAlloc = mathVerticalAssemblyAllocImpl; pub const mathVerticalVariant = mathVerticalVariantImpl; pub const heightUnits = heightUnitsImpl; const horizontalMetrics = horizontalMetricsImpl;};fn initImpl(data: []const u8) FontError!Face { return initAtImpl(data, 0);}fn initAtImpl(data: []const u8, face_index: u32) FontError!Face { const directory_offset = try fontDirectoryOffset(data, face_index); return initDirectoryImpl(data, directory_offset);}fn initDirectoryImpl(data: []const u8, directory_offset: usize) FontError!Face { if (data.len < 12) return error.InvalidFont; const version = try readU32(data, directory_offset); if (version != 0x00010000 and version != tagLiteral("true") and version != tagLiteral("OTTO")) { return error.InvalidFont; } const has_cff_outlines = version == tagLiteral("OTTO"); const num_tables = try readU16(data, directory_offset + 4); if (directory_offset > data.len or data.len - directory_offset < 12 or @as(usize, num_tables) > (data.len - directory_offset - 12) / 16) return error.InvalidFont; const head = try tableAt(data, directory_offset, "head"); const hhea = try tableAt(data, directory_offset, "hhea"); const hmtx = try tableAt(data, directory_offset, "hmtx"); const maxp = try tableAt(data, directory_offset, "maxp"); const cmap_table = try tableAt(data, directory_offset, "cmap"); if (head.len < 54 or hhea.len < 36 or maxp.len < 6) return error.InvalidFont; const units_per_em = try readU16(data, head.offset + 18); const ascender = try readI16(data, hhea.offset + 4); const descender = try readI16(data, hhea.offset + 6); const line_gap = try readI16(data, hhea.offset + 8); const num_h_metrics = try readU16(data, hhea.offset + 34); const num_glyphs = try readU16(data, maxp.offset + 4); if (num_h_metrics == 0) return error.InvalidFont; const vertical_metrics = verticalMetrics(data, directory_offset, num_glyphs); const vorg = if (tableOptionalAt(data, directory_offset, "VORG")) |vorg_table| Vorg.init(data, vorg_table) catch null else null; const typo_metrics = horizontalTypoMetrics(data, directory_offset); const kern = if (has_cff_outlines) null else if (tableOptionalAt(data, directory_offset, "kern")) |kern_table| Kern.init(data, kern_table) catch null else null; const gsub = if (tableOptionalAt(data, directory_offset, "GSUB")) |gsub_table| Gsub.init(data, gsub_table) catch null else null; const gpos = if (tableOptionalAt(data, directory_offset, "GPOS")) |gpos_table| Gpos.init(data, gpos_table) catch null else null; const fvar = if (tableOptionalAt(data, directory_offset, "fvar")) |fvar_table| Fvar.init(data, fvar_table) catch null else null; const gdef = if (tableOptionalAt(data, directory_offset, "GDEF")) |gdef_table| Gdef.init(data, gdef_table, if (fvar) |variation_table| variation_table.axis_count else null) catch null else null; const avar = if (fvar) |variation_table| if (tableOptionalAt(data, directory_offset, "avar")) |avar_table| Avar.init(data, avar_table, variation_table.axis_count) catch null else null else null; const hvar = if (fvar) |variation_table| if (tableOptionalAt(data, directory_offset, "HVAR")) |hvar_table| Hvar.init(data, hvar_table, variation_table.axis_count) catch null else null else null; const vvar = if (fvar) |variation_table| if (tableOptionalAt(data, directory_offset, "VVAR")) |vvar_table| Vvar.init(data, vvar_table, variation_table.axis_count) catch null else null else null; const mvar = if (fvar) |variation_table| if (tableOptionalAt(data, directory_offset, "MVAR")) |mvar_table| Mvar.init(data, mvar_table, variation_table.axis_count) catch null else null else null; const math = if (tableOptionalAt(data, directory_offset, "MATH")) |math_table| Math.init(data, math_table) catch null else null; return .{ .data = data, .directory_offset = directory_offset, .units_per_em = if (units_per_em == 0) 1000 else units_per_em, .ascender = ascender, .descender = descender, .line_gap = line_gap, .typo_metrics = typo_metrics, .num_glyphs = num_glyphs, .num_h_metrics = num_h_metrics, .num_v_metrics = vertical_metrics.count, .cmap = try Cmap.init(data, cmap_table), .hmtx = hmtx, .vmtx = vertical_metrics.table, .vorg = vorg, .kern = kern, .gsub = gsub, .gpos = gpos, .gdef = gdef, .fvar = fvar, .avar = avar, .hvar = hvar, .vvar = vvar, .mvar = mvar, .math = math, };}fn glyphIdImpl(self: Face, codepoint: u21) u32 { const glyph = self.cmap.map(self.data, @intCast(codepoint)) catch return 0; if (glyph >= self.num_glyphs) return 0; return glyph;}fn tableSliceImpl(self: Face, comptime name: []const u8) ?[]const u8 { const found = tableOptionalAt(self.data, self.directory_offset, name) orelse return null; return self.data[found.offset..][0..found.len];}fn tableSliceByTagImpl(self: Face, table_tag: Tag) ?[]const u8 { const found = tableOptionalAtTag(self.data, self.directory_offset, table_tag) orelse return null; return self.data[found.offset..][0..found.len];}fn mathConstantsImpl(self: Face) ?MathConstants { if (self.math) |math| return math.constants; return null;}fn mathTopAccentAttachmentImpl(self: Face, glyph_id: u32) ?MathValueRecord { const math = self.math orelse return null; return math.topAccentAttachment(self.data, glyph_id) catch null;}fn mathVerticalVariantImpl(self: Face, glyph_id: u32, target_measurement: u16) ?MathGlyphVariant { const math = self.math orelse return null; return math.verticalVariant(self.data, glyph_id, target_measurement) catch null;}fn mathHorizontalVariantImpl(self: Face, glyph_id: u32, target_measurement: u16) ?MathGlyphVariant { const math = self.math orelse return null; return math.horizontalVariant(self.data, glyph_id, target_measurement) catch null;}fn mathVerticalAssemblyAllocImpl(self: Face, allocator: std.mem.Allocator, glyph_id: u32, target_measurement: u16) std.mem.Allocator.Error!?MathGlyphAssembly { const math = self.math orelse return null; return math.verticalAssemblyAlloc(allocator, self.data, glyph_id, target_measurement) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return null, };}fn mathHorizontalAssemblyAllocImpl(self: Face, allocator: std.mem.Allocator, glyph_id: u32, target_measurement: u16) std.mem.Allocator.Error!?MathGlyphAssembly { const math = self.math orelse return null; return math.horizontalAssemblyAlloc(allocator, self.data, glyph_id, target_measurement) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return null, };}fn nameUtf8AllocImpl(self: Face, allocator: std.mem.Allocator, name_id: u16) !?[]u8 { const name_table = tableOptionalAt(self.data, self.directory_offset, "name") orelse return null; const record = (try bestNameRecord(self.data, name_table, name_id)) orelse return null; return try utf16BeToUtf8Alloc(allocator, record.bytes);}fn glyphIdForVariationImpl(self: Face, codepoint: u21, variation_selector: u21) ?u32 { const glyph = self.cmap.mapVariation(self.data, @intCast(codepoint), @intCast(variation_selector)) catch return null; const mapped = glyph orelse return null; if (mapped >= self.num_glyphs) return 0; return mapped;}fn hasVariationSequencesImpl(self: Face) bool { return self.cmap.variation != null;}fn variationAxisCountImpl(self: Face) u16 { if (self.fvar) |fvar| return fvar.axis_count; return 0;}fn variationAxisImpl(self: Face, axis_index: usize) ?VariationAxis { if (self.fvar) |fvar| return fvar.axis(self.data, axis_index) catch null; return null;}fn variationAxisForImpl(self: Face, axis_tag: Tag) ?VariationAxis { const fvar = self.fvar orelse return null; const axis_index = (fvar.axisIndex(self.data, axis_tag) catch return null) orelse return null; return fvar.axis(self.data, axis_index) catch null;}fn validateVariationsImpl(self: Face, settings: []const VariationSetting) FontError!void { if (settings.len == 0) return; const fvar = self.fvar orelse return error.InvalidVariations; for (settings) |setting| { const axis_index = (try fvar.axisIndex(self.data, setting.tag)) orelse return error.InvalidVariations; _ = try self.normalizedCoordinateAt(settings, axis_index); }}fn normalizedVariationImpl(self: Face, setting: VariationSetting) FontError!?NormalizedVariation { const fvar = self.fvar orelse return null; const axis_index = (try fvar.axisIndex(self.data, setting.tag)) orelse return null; return .{ .tag = setting.tag, .value = try self.normalizedCoordinateAt(&.{setting}, axis_index), };}fn normalizedCoordinateAtImpl(self: Face, settings: []const VariationSetting, axis_index: usize) FontError!i16 { const fvar = self.fvar orelse return error.InvalidVariations; const axis = try fvar.axis(self.data, axis_index); var user_value = axis.default_value; for (settings) |setting| { if (setting.tag != axis.tag) continue; user_value = try fixed16FromFloat(setting.value); } var normalized = normalizeVariationAxis(axis, user_value); if (self.avar) |avar| { normalized = try avar.map(self.data, axis_index, normalized); } return fixed16ToF2Dot14(normalized);}fn advanceWidthImpl(self: Face, glyph_id: u32) u16 { const metric_count: usize = self.num_h_metrics; const glyph_index = @as(usize, @intCast(glyph_id)); const metric_index = if (glyph_index < metric_count) glyph_index else metric_count - 1; const offset = self.hmtx.offset + metric_index * 4; if (offset + 2 > self.data.len) return self.units_per_em; return readU16(self.data, offset) catch self.units_per_em;}fn advanceWidthForVariationsImpl(self: Face, glyph_id: u32, settings: []const VariationSetting) i32 { const base = @as(i32, self.advanceWidth(glyph_id)); if (settings.len == 0) return base; const delta = if (self.hvar) |hvar| hvar.advanceWidthDelta(self.data, self, settings, glyph_id) catch 0 else 0; return clampDesignMetric(base, delta);}fn advanceHeightImpl(self: Face, glyph_id: u32) u16 { const metrics = self.vmtx orelse return self.heightUnits(); const metric_count: usize = self.num_v_metrics; if (metric_count == 0) return self.heightUnits(); const glyph_index = @as(usize, @intCast(glyph_id)); const metric_index = if (glyph_index < metric_count) glyph_index else metric_count - 1; if (metric_index > (std.math.maxInt(usize) - metrics.offset) / 4) return self.heightUnits(); const offset = metrics.offset + metric_index * 4; if (offset + 2 > self.data.len) return self.heightUnits(); return readU16(self.data, offset) catch self.heightUnits();}fn advanceHeightForVariationsImpl(self: Face, glyph_id: u32, settings: []const VariationSetting) i32 { const base = @as(i32, self.advanceHeight(glyph_id)); if (settings.len == 0) return base; const delta = if (self.vvar) |vvar| vvar.advanceHeightDelta(self.data, self, settings, glyph_id) catch 0 else 0; return clampDesignMetric(base, delta);}fn verticalOriginYImpl(self: Face, glyph_id: u32) ?i16 { const vorg = self.vorg orelse return null; return vorg.originY(self.data, glyph_id) catch null;}fn metricVariationDeltaImpl(self: Face, metric_tag: Tag, settings: []const VariationSetting) i32 { if (settings.len == 0) return 0; const mvar = self.mvar orelse return 0; return mvar.valueDelta(self.data, self, settings, metric_tag) catch 0;}fn ascenderForVariationsImpl(self: Face, settings: []const VariationSetting) i32 { const metrics = self.horizontalMetrics(); return clampI32(@as(i64, metrics.ascender) + @as(i64, self.metricVariationDelta(tagLiteral("hasc"), settings)));}fn descenderForVariationsImpl(self: Face, settings: []const VariationSetting) i32 { const metrics = self.horizontalMetrics(); return clampI32(@as(i64, metrics.descender) + @as(i64, self.metricVariationDelta(tagLiteral("hdsc"), settings)));}fn lineGapForVariationsImpl(self: Face, settings: []const VariationSetting) i32 { const metrics = self.horizontalMetrics(); return clampI32(@as(i64, metrics.line_gap) + @as(i64, self.metricVariationDelta(tagLiteral("hlgp"), settings)));}fn heightUnitsForVariationsImpl(self: Face, settings: []const VariationSetting) i32 { const height = @as(i64, self.ascenderForVariations(settings)) - @as(i64, self.descenderForVariations(settings)); if (height <= 0) return self.units_per_em; return clampI32(height);}fn kerningImpl(self: Face, left_glyph: u32, right_glyph: u32) i16 { if (self.kern) |kern| { return kern.value(self.data, left_glyph, right_glyph) catch 0; } return 0;}fn hasGposImpl(self: Face) bool { return self.gpos != null;}fn gposLookupCountImpl(self: Face) u16 { if (self.gpos) |gpos| { return gpos.lookupCount(self.data) catch 0; } return 0;}fn gposLookupTypeImpl(self: Face, lookup_index: u16) u16 { if (self.gpos) |gpos| { return gpos.lookupResolvedType(self.data, lookup_index) catch 0; } return 0;}fn gposVariationValueImpl(self: Face, base: i32, settings: []const VariationSetting, packed_index: u32) i32 { return gposVariationValueForFace(base, self, settings, packed_index);}fn variationContextImpl(self: *const Face) lookups.VariationContext { return .{ .ptr = self, .variation_axis_count = faceVariationAxisCount, .normalized_coordinate_at = faceNormalizedCoordinateAt, };}fn hasGsubImpl(self: Face) bool { return self.gsub != null;}fn usesAutomaticFractionsImpl(self: Face, selection: LayoutSelection) bool { if (self.gsub) |gsub| { return gsub.usesAutomaticFractions(self.data, selection) catch false; } return false;}fn usesArabicJoiningFormsImpl(self: Face, selection: LayoutSelection) bool { if (self.gsub) |gsub| { return gsub.usesArabicJoiningForms(self.data, selection) catch false; } return false;}fn gposPairAdjustmentImpl(self: Face, left_glyph: u32, right_glyph: u32) GposPairAdjustment { return self.gposPairAdjustmentFor(.{}, left_glyph, right_glyph);}fn gposPairAdjustmentForImpl(self: Face, selection: LayoutSelection, left_glyph: u32, right_glyph: u32) GposPairAdjustment { if (self.gpos) |gpos| { return gpos.pairAdjustment(self.data, self.variationContext(), selection, self.gdef, left_glyph, right_glyph) catch .{}; } return .{};}fn gposPairAdjustmentBeforeForImpl(self: Face, selection: LayoutSelection, glyph_ids: []const u32, glyph_index: usize) ?GposPairAdjustmentBefore { return self.gposPairAdjustmentBeforeForRange(selection, glyph_ids, glyph_index, null);}fn gposPairAdjustmentBeforeForRangeImpl(self: Face, selection: LayoutSelection, glyph_ids: []const u32, glyph_index: usize, source: ?FeatureRange) ?GposPairAdjustmentBefore { if (self.gpos) |gpos| { return gpos.pairAdjustmentBefore(self.data, self.variationContext(), selection, self.gdef, glyph_ids, glyph_index, source) catch null; } return null;}fn gposPairAdjustmentBeforeAtLookupForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, glyph_ids: []const u32, glyph_index: usize, source: ?FeatureRange) ?GposPairAdjustmentBefore { if (self.gpos) |gpos| { return gpos.pairAdjustmentBeforeAtLookup(self.data, self.variationContext(), selection, self.gdef, lookup_index, glyph_ids, glyph_index, source) catch null; } return null;}fn gposContextualPairAdjustmentAtForImpl(self: Face, selection: LayoutSelection, glyph_ids: []const u32, glyph_index: usize) ?GposContextualPairAdjustment { return self.gposContextualPairAdjustmentAtForRange(selection, glyph_ids, glyph_index, null);}fn gposContextualPairAdjustmentAtForRangeImpl(self: Face, selection: LayoutSelection, glyph_ids: []const u32, glyph_index: usize, source: ?FeatureRange) ?GposContextualPairAdjustment { if (self.gpos) |gpos| { return gpos.contextualPairAdjustmentAt(self.data, self.variationContext(), selection, self.gdef, glyph_ids, glyph_index, source) catch null; } return null;}fn gposContextualPairAdjustmentAtLookupForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, glyph_ids: []const u32, glyph_index: usize, source: ?FeatureRange) ?GposContextualPairAdjustment { if (self.gpos) |gpos| { return gpos.contextualPairAdjustmentAtLookup(self.data, self.variationContext(), selection, self.gdef, lookup_index, glyph_ids, glyph_index, source) catch null; } return null;}fn hasGposContextualPairAdjustmentForImpl(self: Face, selection: LayoutSelection) bool { if (self.gpos) |gpos| { return gpos.hasActiveContextualPairAdjustment(self.data, self.variationContext(), selection) catch false; } return false;}fn gposSingleAdjustmentImpl(self: Face, glyph_id: u32) GposValueAdjustment { return self.gposSingleAdjustmentFor(.{}, glyph_id);}fn gposSingleAdjustmentForImpl(self: Face, selection: LayoutSelection, glyph_id: u32) GposValueAdjustment { var glyphs = [_]u32{glyph_id}; const result = self.gposSingleAdjustmentAtFor(selection, &glyphs, 0) orelse return .{}; if (result.target_offset != 0) return .{}; return result.adjustment;}fn gposSingleAdjustmentAtForImpl(self: Face, selection: LayoutSelection, glyph_ids: []const u32, glyph_index: usize) ?GposSingleAdjustment { return self.gposSingleAdjustmentAtForRange(selection, glyph_ids, glyph_index, null);}fn gposSingleAdjustmentAtForRangeImpl(self: Face, selection: LayoutSelection, glyph_ids: []const u32, glyph_index: usize, source: ?FeatureRange) ?GposSingleAdjustment { if (self.gpos) |gpos| { return gpos.singleAdjustmentAt(self.data, self.variationContext(), selection, self.gdef, glyph_ids, glyph_index, source) catch null; } return null;}fn gposSingleAdjustmentAtLookupForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, glyph_ids: []const u32, glyph_index: usize, source: ?FeatureRange) ?GposSingleAdjustment { if (self.gpos) |gpos| { return gpos.singleAdjustmentAtLookup(self.data, self.variationContext(), selection, self.gdef, lookup_index, glyph_ids, glyph_index, source) catch null; } return null;}fn gposCursiveAttachmentImpl(self: Face, exit_glyph: u32, entry_glyph: u32) ?GposCursiveAttachment { return self.gposCursiveAttachmentFor(.{}, exit_glyph, entry_glyph);}fn gposCursiveAttachmentForImpl(self: Face, selection: LayoutSelection, exit_glyph: u32, entry_glyph: u32) ?GposCursiveAttachment { return self.gposCursiveAttachmentForRange(selection, exit_glyph, entry_glyph, null);}fn gposCursiveAttachmentForRangeImpl(self: Face, selection: LayoutSelection, exit_glyph: u32, entry_glyph: u32, source: ?FeatureRange) ?GposCursiveAttachment { if (self.gpos) |gpos| { return gpos.cursiveAttachment(self.data, self.variationContext(), selection, self.gdef, exit_glyph, entry_glyph, source) catch null; } return null;}fn gposCursiveAttachmentAtLookupForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, exit_glyph: u32, entry_glyph: u32, source: ?FeatureRange) ?GposCursiveAttachment { if (self.gpos) |gpos| { return gpos.cursiveAttachmentAtLookup(self.data, self.variationContext(), selection, self.gdef, lookup_index, exit_glyph, entry_glyph, source) catch null; } return null;}fn gposMarkToBaseAdjustmentImpl(self: Face, base_glyph: u32, mark_glyph: u32) ?GposValueAdjustment { return self.gposMarkToBaseAdjustmentFor(.{}, base_glyph, mark_glyph);}fn gposMarkToBaseAdjustmentForImpl(self: Face, selection: LayoutSelection, base_glyph: u32, mark_glyph: u32) ?GposValueAdjustment { return self.gposMarkToBaseAdjustmentForRange(selection, base_glyph, mark_glyph, null);}fn gposMarkToBaseAdjustmentForRangeImpl(self: Face, selection: LayoutSelection, base_glyph: u32, mark_glyph: u32, source: ?FeatureRange) ?GposValueAdjustment { if (self.gpos) |gpos| { return gpos.markToBaseAdjustment(self.data, self.variationContext(), selection, self.gdef, base_glyph, mark_glyph, source) catch null; } return null;}fn gposMarkToBaseAdjustmentAtLookupForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, base_glyph: u32, mark_glyph: u32, source: ?FeatureRange) ?GposValueAdjustment { if (self.gpos) |gpos| { return gpos.markToBaseAdjustmentAtLookup(self.data, self.variationContext(), selection, self.gdef, lookup_index, base_glyph, mark_glyph, source) catch null; } return null;}fn gposMarkToLigatureAdjustmentImpl(self: Face, ligature_glyph: u32, mark_glyph: u32, component_index: u16) ?GposValueAdjustment { return self.gposMarkToLigatureAdjustmentFor(.{}, ligature_glyph, mark_glyph, component_index);}fn gposMarkToLigatureAdjustmentForImpl(self: Face, selection: LayoutSelection, ligature_glyph: u32, mark_glyph: u32, component_index: u16) ?GposValueAdjustment { return self.gposMarkToLigatureAdjustmentForRange(selection, ligature_glyph, mark_glyph, component_index, null);}fn gposMarkToLigatureAdjustmentForRangeImpl(self: Face, selection: LayoutSelection, ligature_glyph: u32, mark_glyph: u32, component_index: u16, source: ?FeatureRange) ?GposValueAdjustment { if (self.gpos) |gpos| { return gpos.markToLigatureAdjustment(self.data, self.variationContext(), selection, self.gdef, ligature_glyph, mark_glyph, component_index, source) catch null; } return null;}fn gposMarkToLigatureAdjustmentAtLookupForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, ligature_glyph: u32, mark_glyph: u32, component_index: u16, source: ?FeatureRange) ?GposValueAdjustment { if (self.gpos) |gpos| { return gpos.markToLigatureAdjustmentAtLookup(self.data, self.variationContext(), selection, self.gdef, lookup_index, ligature_glyph, mark_glyph, component_index, source) catch null; } return null;}fn gposMarkToMarkAdjustmentImpl(self: Face, base_mark_glyph: u32, mark_glyph: u32) ?GposValueAdjustment { return self.gposMarkToMarkAdjustmentFor(.{}, base_mark_glyph, mark_glyph);}fn gposMarkToMarkAdjustmentForImpl(self: Face, selection: LayoutSelection, base_mark_glyph: u32, mark_glyph: u32) ?GposValueAdjustment { return self.gposMarkToMarkAdjustmentForRange(selection, base_mark_glyph, mark_glyph, null);}fn gposMarkToMarkAdjustmentForRangeImpl(self: Face, selection: LayoutSelection, base_mark_glyph: u32, mark_glyph: u32, source: ?FeatureRange) ?GposValueAdjustment { if (self.gpos) |gpos| { return gpos.markToMarkAdjustment(self.data, self.variationContext(), selection, self.gdef, base_mark_glyph, mark_glyph, source) catch null; } return null;}fn gposMarkToMarkAdjustmentAtLookupForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, base_mark_glyph: u32, mark_glyph: u32, source: ?FeatureRange) ?GposValueAdjustment { if (self.gpos) |gpos| { return gpos.markToMarkAdjustmentAtLookup(self.data, self.variationContext(), selection, self.gdef, lookup_index, base_mark_glyph, mark_glyph, source) catch null; } return null;}fn hasGposMarkToBaseImpl(self: Face) bool { return self.hasGposMarkToBaseFor(.{});}fn hasGposMarkToBaseForImpl(self: Face, selection: LayoutSelection) bool { if (self.gpos) |gpos| { return gpos.hasActiveLookupType(self.data, self.variationContext(), selection, 4) catch false; } return false;}fn hasGposMarkToLigatureImpl(self: Face) bool { return self.hasGposMarkToLigatureFor(.{});}fn hasGposMarkToLigatureForImpl(self: Face, selection: LayoutSelection) bool { if (self.gpos) |gpos| { return gpos.hasActiveLookupType(self.data, self.variationContext(), selection, 5) catch false; } return false;}fn hasGposSingleAdjustmentImpl(self: Face) bool { return self.hasGposSingleAdjustmentFor(.{});}fn hasGposSingleAdjustmentForImpl(self: Face, selection: LayoutSelection) bool { if (self.gpos) |gpos| { return gpos.hasActiveSingleAdjustment(self.data, self.variationContext(), selection) catch false; } return false;}fn hasGposCursiveAttachmentImpl(self: Face) bool { return self.hasGposCursiveAttachmentFor(.{});}fn hasGposCursiveAttachmentForImpl(self: Face, selection: LayoutSelection) bool { if (self.gpos) |gpos| { return gpos.hasActiveLookupType(self.data, self.variationContext(), selection, 3) catch false; } return false;}fn hasGposMarkToMarkImpl(self: Face) bool { return self.hasGposMarkToMarkFor(.{});}fn hasGposMarkToMarkForImpl(self: Face, selection: LayoutSelection) bool { if (self.gpos) |gpos| { return gpos.hasActiveLookupType(self.data, self.variationContext(), selection, 6) catch false; } return false;}fn glyphClassImpl(self: Face, glyph_id: u32) GlyphClass { if (self.gdef) |gdef| { return gdef.glyphClass(self.data, glyph_id) catch .unknown; } return .unknown;}fn ligatureCaretCountImpl(self: Face, glyph_id: u32) u16 { if (self.gdef) |gdef| { return gdef.ligatureCaretCount(self.data, glyph_id) catch 0; } return 0;}fn ligatureCaretImpl(self: Face, glyph_id: u32, caret_index: usize) ?LigatureCaret { if (self.gdef) |gdef| { return gdef.ligatureCaret(self.data, glyph_id, caret_index) catch null; } return null;}fn substituteGlyphImpl(self: Face, glyph_id: u32) u32 { return self.substituteGlyphFor(.{}, glyph_id);}fn substituteGlyphForImpl(self: Face, selection: LayoutSelection, glyph_id: u32) u32 { var glyphs = [_]u32{glyph_id}; const lookup_count = self.gsubLookupCount() catch return glyph_id; for (0..lookup_count) |lookup_index| { const substitute = self.substitutionAtFor(selection, @intCast(lookup_index), &glyphs, 0) catch return glyph_id; if (substitute) |result| { if (result.component_count == 1 and result.replacement_count == 1 and result.glyph_id < self.num_glyphs) return result.glyph_id; } } return glyph_id;}fn gsubLookupCountImpl(self: Face) FontError!u16 { if (self.gsub) |gsub| return gsub.lookupCount(self.data); return 0;}fn gsubLookupIsReverseForImpl(self: Face, selection: LayoutSelection, lookup_index: u16) FontError!bool { if (self.gsub) |gsub| return gsub.lookupIsReverse(self.data, selection, lookup_index); return false;}fn substitutionAtImpl(self: Face, lookup_index: u16, glyph_ids: []const u32, glyph_index: usize) FontError!?GsubSubstitution { return self.substitutionAtFor(.{}, lookup_index, glyph_ids, glyph_index);}fn substitutionAtForImpl(self: Face, selection: LayoutSelection, lookup_index: u16, glyph_ids: []const u32, glyph_index: usize) FontError!?GsubSubstitution { return self.substitutionAtForRange(selection, lookup_index, glyph_ids, glyph_index, null);}fn substitutionAtForRangeImpl(self: Face, selection: LayoutSelection, lookup_index: u16, glyph_ids: []const u32, glyph_index: usize, source: ?FeatureRange) FontError!?GsubSubstitution { if (self.gsub) |gsub| { const result = try gsub.substitutionAtWithGdef(self.data, self.variationContext(), selection, self.gdef, lookup_index, glyph_ids, glyph_index, source); if (result) |substitution| { if (substitution.component_count == 0 or substitution.replacement_count == 0) return null; for (0..substitution.replacement_count) |replacement_index| { _ = self.substitutionGlyph(substitution, replacement_index) orelse return null; } return substitution; } } return null;}fn substitutionGlyphImpl(self: Face, substitution: GsubSubstitution, replacement_index: usize) ?u32 { if (replacement_index >= substitution.replacement_count) return null; if (substitution.replacement_glyphs_offset == 0) { if (replacement_index != 0) return null; return if (substitution.glyph_id < self.num_glyphs) substitution.glyph_id else null; } const byte_delta = replacement_index * 2; if (substitution.replacement_glyphs_offset > std.math.maxInt(usize) - byte_delta) return null; const offset = substitution.replacement_glyphs_offset + byte_delta; const glyph = readU16(self.data, offset) catch return null; return if (glyph < self.num_glyphs) glyph else null;}fn heightUnitsImpl(self: Face) u16 { const height = @as(i32, self.ascender) - @as(i32, self.descender); if (height <= 0 or height > std.math.maxInt(u16)) return self.units_per_em; return @intCast(height);}fn horizontalMetricsImpl(self: Face) HorizontalMetrics { return self.typo_metrics orelse .{ .ascender = self.ascender, .descender = self.descender, .line_gap = self.line_gap, };}fn faceVariationAxisCount(ptr: *const anyopaque) u16 { const face: *const Face = @ptrCast(@alignCast(ptr)); return face.variationAxisCount();}fn faceNormalizedCoordinateAt(ptr: *const anyopaque, settings: []const VariationSetting, axis_index: usize) FontError!i16 { const face: *const Face = @ptrCast(@alignCast(ptr)); return try face.normalizedCoordinateAt(settings, axis_index);}fn gposVariationValueForFace(base: i32, face: Face, settings: []const VariationSetting, packed_index: u32) i32 { if (packed_index == noGposVariationIndex or settings.len == 0) return base; const gdef = face.gdef orelse return base; const index = DeltaSetIndex{ .outer = packed_index >> 16, .inner = packed_index & 0xffff, }; const delta = gdef.variationDelta(face.data, face, settings, index) catch return base; return clampI32(@as(i64, base) + @as(i64, delta));}test "Face rejects empty data" { try std.testing.expectError(error.InvalidFont, Face.init(&.{}));}test "Face maps BMP codepoints through cmap format 4" { const bytes = try test_font.create(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(u16, 1000), face.units_per_em); try std.testing.expectEqual(@as(u16, 500), face.advanceWidth(face.glyphId('A'))); try std.testing.expectEqual(@as(u32, 'A' - 31), face.glyphId('A')); try std.testing.expectEqual(@as(u32, 96), face.glyphId(0x25cc)); try std.testing.expectEqual(@as(u32, 0), face.glyphId(0x20ac));}test "Face opens indexed OpenType collection faces" { const bytes = try test_font.createCollection(std.testing.allocator); defer std.testing.allocator.free(bytes); const first = try Face.init(bytes); const second = try Face.initAt(bytes, 1); const a = first.glyphId('A'); try std.testing.expectEqual(a, first.substituteGlyph(a)); try std.testing.expectEqual(second.glyphId('Z'), second.substituteGlyph(second.glyphId('A'))); try std.testing.expectError(error.InvalidFont, Face.initAt(bytes, 2)); const plain = try test_font.create(std.testing.allocator); defer std.testing.allocator.free(plain); try std.testing.expectError(error.InvalidFont, Face.initAt(plain, 1));}test "Face exposes table slices for indexed faces" { const bytes = try test_font.createCollection(std.testing.allocator); defer std.testing.allocator.free(bytes); const second = try Face.initAt(bytes, 1); const head = second.tableSlice("head").?; const by_tag = second.tableSliceByTag(tag("head")).?; try std.testing.expectEqual(@as(usize, 54), head.len); try std.testing.expectEqualSlices(u8, head, by_tag); try std.testing.expectEqual(@as(?[]const u8, null), second.tableSlice("COLR"));}test "Face decodes UTF-16BE name records" { const bytes = try test_font.create(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const family = (try face.nameUtf8Alloc(std.testing.allocator, 1)).?; defer std.testing.allocator.free(family); const full = (try face.nameUtf8Alloc(std.testing.allocator, 4)).?; defer std.testing.allocator.free(full); try std.testing.expectEqualStrings("Tiny Fixture", family); try std.testing.expectEqualStrings("Tiny Fixture Regular", full); try std.testing.expectEqual(@as(?[]u8, null), try face.nameUtf8Alloc(std.testing.allocator, 6));}test "Face reads vertical advance metrics from vmtx" { const bytes = try test_font.createWithVerticalMetrics(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(u16, 700), face.advanceHeight(face.glyphId('A'))); try std.testing.expectEqual(@as(u16, 900), face.advanceHeight(face.glyphId('B'))); try std.testing.expectEqual(@as(u16, 700), face.advanceHeight(face.glyphId('!')));}test "Face reads vertical origin metrics from VORG" { const bytes = try test_font.createWithVerticalOrigins(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(?i16, 760), face.verticalOriginY(face.glyphId('A'))); try std.testing.expectEqual(@as(?i16, 880), face.verticalOriginY(face.glyphId('B')));}test "Face falls back to line height without vertical metrics" { const bytes = try test_font.create(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(u16, 1000), face.advanceHeight(face.glyphId('A')));}test "Face maps non-default Unicode variation sequences through cmap format 14" { const bytes = try test_font.createWithVariationSequences(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(u32, 'A' - 31), face.glyphId('A')); try std.testing.expectEqual(@as(?u32, 96), face.glyphIdForVariation('A', 0xfe0f)); try std.testing.expectEqual(@as(?u32, null), face.glyphIdForVariation('B', 0xfe0f)); try std.testing.expectEqual(@as(?u32, null), face.glyphIdForVariation('A', 0xfe0e));}test "Face reads fvar variation axes" { const bytes = try test_font.createWithVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(u16, 2), face.variationAxisCount()); const weight = face.variationAxis(0).?; try std.testing.expectEqual(tag("wght"), weight.tag); try std.testing.expectEqual(@as(i32, 100 << 16), weight.min_value); try std.testing.expectEqual(@as(i32, 400 << 16), weight.default_value); try std.testing.expectEqual(@as(i32, 900 << 16), weight.max_value); try std.testing.expectEqual(@as(u16, 0), weight.flags); try std.testing.expectEqual(@as(u16, 256), weight.name_id); try std.testing.expectEqual(@as(f32, 400), fixed16ToFloat(weight.default_value)); const width = face.variationAxisFor(tag("wdth")).?; try std.testing.expectEqual(@as(i32, 50 << 16), width.min_value); try std.testing.expectEqual(@as(i32, 100 << 16), width.default_value); try std.testing.expectEqual(@as(i32, 200 << 16), width.max_value); try std.testing.expectEqual(@as(u16, 1), width.flags); try std.testing.expectEqual(@as(?VariationAxis, null), face.variationAxis(2)); try std.testing.expectEqual(@as(?VariationAxis, null), face.variationAxisFor(tag("opsz")));}test "Face normalizes fvar variation coordinates" { const bytes = try test_font.createWithVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(i16, 0), try face.normalizedCoordinateAt(&.{}, 0)); try std.testing.expectEqual(@as(i16, -8192), try face.normalizedCoordinateAt(&.{.{ .tag = tag("wght"), .value = 250.0 }}, 0)); try std.testing.expectEqual(@as(i16, 8192), try face.normalizedCoordinateAt(&.{.{ .tag = tag("wght"), .value = 650.0 }}, 0)); try std.testing.expectEqual(@as(i16, 16384), try face.normalizedCoordinateAt(&.{.{ .tag = tag("wght"), .value = 1200.0 }}, 0)); try std.testing.expectEqual(@as(i16, -16384), try face.normalizedCoordinateAt(&.{.{ .tag = tag("wdth"), .value = 25.0 }}, 1)); const normalized = (try face.normalizedVariation(.{ .tag = tag("wght"), .value = 650.0 })).?; try std.testing.expectEqual(tag("wght"), normalized.tag); try std.testing.expectEqual(@as(i16, 8192), normalized.value); try std.testing.expectEqual(@as(?NormalizedVariation, null), try face.normalizedVariation(.{ .tag = tag("opsz"), .value = 12.0 })); try std.testing.expectError(error.InvalidVariations, face.validateVariations(&.{.{ .tag = tag("opsz"), .value = 12.0 }})); try std.testing.expectError(error.InvalidVariations, face.validateVariations(&.{.{ .tag = tag("wght"), .value = std.math.nan(f32) }}));}test "Face applies avar variation coordinate remapping" { const bytes = try test_font.createWithAxisVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(i16, -4096), try face.normalizedCoordinateAt(&.{.{ .tag = tag("wght"), .value = 250.0 }}, 0)); try std.testing.expectEqual(@as(i16, 12288), try face.normalizedCoordinateAt(&.{.{ .tag = tag("wght"), .value = 650.0 }}, 0)); try std.testing.expectEqual(@as(i16, 8192), try face.normalizedCoordinateAt(&.{.{ .tag = tag("wdth"), .value = 150.0 }}, 1));}test "Face applies HVAR advance width deltas" { const bytes = try test_font.createWithHorizontalMetricVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const v = face.glyphId('V'); try std.testing.expectEqual(@as(u16, 500), face.advanceWidth(a)); try std.testing.expectEqual(@as(i32, 500), face.advanceWidthForVariations(a, &.{})); try std.testing.expectEqual(@as(i32, 500), face.advanceWidthForVariations(a, &.{.{ .tag = tag("wght"), .value = 400.0 }})); try std.testing.expectEqual(@as(i32, 550), face.advanceWidthForVariations(a, &.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, 600), face.advanceWidthForVariations(a, &.{.{ .tag = tag("wght"), .value = 900.0 }})); try std.testing.expectEqual(@as(i32, 500), face.advanceWidthForVariations(v, &.{.{ .tag = tag("wght"), .value = 900.0 }}));}test "Face applies avar-normalized coordinates to HVAR" { const bytes = try test_font.createWithHorizontalMetricAxisVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); try std.testing.expectEqual(@as(i32, 575), face.advanceWidthForVariations(a, &.{.{ .tag = tag("wght"), .value = 650.0 }}));}test "Face applies VVAR advance height deltas" { const bytes = try test_font.createWithVerticalMetricVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const b = face.glyphId('B'); try std.testing.expectEqual(@as(u16, 700), face.advanceHeight(a)); try std.testing.expectEqual(@as(i32, 700), face.advanceHeightForVariations(a, &.{})); try std.testing.expectEqual(@as(i32, 700), face.advanceHeightForVariations(a, &.{.{ .tag = tag("wght"), .value = 400.0 }})); try std.testing.expectEqual(@as(i32, 750), face.advanceHeightForVariations(a, &.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, 800), face.advanceHeightForVariations(a, &.{.{ .tag = tag("wght"), .value = 900.0 }})); try std.testing.expectEqual(@as(i32, 900), face.advanceHeightForVariations(b, &.{.{ .tag = tag("wght"), .value = 900.0 }}));}test "Face applies avar-normalized coordinates to VVAR" { const bytes = try test_font.createWithVerticalMetricAxisVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); try std.testing.expectEqual(@as(i32, 775), face.advanceHeightForVariations(a, &.{.{ .tag = tag("wght"), .value = 650.0 }}));}test "Face applies MVAR horizontal metric deltas" { const bytes = try test_font.createWithMetricVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(i32, 800), face.ascenderForVariations(&.{})); try std.testing.expectEqual(@as(i32, 800), face.ascenderForVariations(&.{.{ .tag = tag("wght"), .value = 400.0 }})); try std.testing.expectEqual(@as(i32, 860), face.ascenderForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, 920), face.ascenderForVariations(&.{.{ .tag = tag("wght"), .value = 900.0 }})); try std.testing.expectEqual(@as(i32, -200), face.descenderForVariations(&.{})); try std.testing.expectEqual(@as(i32, -240), face.descenderForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, -280), face.descenderForVariations(&.{.{ .tag = tag("wght"), .value = 900.0 }})); try std.testing.expectEqual(@as(i32, 40), face.lineGapForVariations(&.{})); try std.testing.expectEqual(@as(i32, 50), face.lineGapForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, 60), face.lineGapForVariations(&.{.{ .tag = tag("wght"), .value = 900.0 }})); try std.testing.expectEqual(@as(i32, 0), face.metricVariationDelta(tag("vasc"), &.{.{ .tag = tag("wght"), .value = 900.0 }})); try std.testing.expectEqual(@as(i32, 1000), face.heightUnitsForVariations(&.{})); try std.testing.expectEqual(@as(i32, 1100), face.heightUnitsForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, 1200), face.heightUnitsForVariations(&.{.{ .tag = tag("wght"), .value = 900.0 }}));}test "Face applies avar-normalized coordinates to MVAR" { const bytes = try test_font.createWithMetricAxisVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(@as(i32, 890), face.ascenderForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, -260), face.descenderForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, 55), face.lineGapForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }})); try std.testing.expectEqual(@as(i32, 1150), face.heightUnitsForVariations(&.{.{ .tag = tag("wght"), .value = 650.0 }}));}test "Face reads classic kern format 0 pairs" { const bytes = try test_font.createWithKern(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const v = face.glyphId('V'); try std.testing.expectEqual(@as(i16, -80), face.kerning(a, v)); try std.testing.expectEqual(@as(i16, 0), face.kerning(v, a));}test "Face applies GSUB required single substitution" { const bytes = try test_font.createWithGsubSingleSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const z = face.glyphId('Z'); try std.testing.expectEqual(z, face.substituteGlyph(a)); try std.testing.expectEqual(face.glyphId('B'), face.substituteGlyph(face.glyphId('B')));}test "Face applies GSUB single substitutions with mark filters" { const bytes = try test_font.createWithGsubSingleIgnoreMarks(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const mark = face.glyphId('^'); const glyphs = [_]u32{ a, mark }; const substitution = (try face.substitutionAt(0, &glyphs, 0)).?; try std.testing.expectEqual(face.glyphId('Z'), substitution.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &glyphs, 1));}test "Face applies GSUB alternate substitutions" { const bytes = try test_font.createWithGsubAlternateSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const glyphs = [_]u32{a}; const substitution = (try face.substitutionAt(0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u32, 96), face.substituteGlyph(a)); try std.testing.expectEqual(@as(u32, 96), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(face.glyphId('B'), face.substituteGlyph(face.glyphId('B')));}test "Face applies GSUB contextual substitutions" { const bytes = try test_font.createWithGsubContextualSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const substitution = (try face.substitutionAt(0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u16, 2), try face.gsubLookupCount()); try std.testing.expectEqual(@as(u32, face.glyphId('Z')), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(@as(usize, 1), substitution.target_offset); try std.testing.expectEqual(@as(usize, 3), substitution.skip_count); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &mismatch, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(1, &glyphs, 1));}test "Face applies GSUB class contextual substitutions" { const bytes = try test_font.createWithGsubClassContextualSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const substitution = (try face.substitutionAt(0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u16, 2), try face.gsubLookupCount()); try std.testing.expectEqual(@as(u32, face.glyphId('Z')), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(@as(usize, 1), substitution.target_offset); try std.testing.expectEqual(@as(usize, 3), substitution.skip_count); try std.testing.expectEqual(face.glyphId('B'), face.substituteGlyph(face.glyphId('B'))); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &mismatch, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(1, &glyphs, 1));}test "Face applies GSUB coverage contextual substitutions" { const bytes = try test_font.createWithGsubCoverageContextualSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const substitution = (try face.substitutionAt(0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u16, 2), try face.gsubLookupCount()); try std.testing.expectEqual(@as(u32, face.glyphId('Z')), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(@as(usize, 1), substitution.target_offset); try std.testing.expectEqual(@as(usize, 3), substitution.skip_count); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &mismatch, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(1, &glyphs, 1));}test "Face applies GSUB chained contextual substitutions" { const bytes = try test_font.createWithGsubChainedContextualSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const missing_backtrack = [_]u32{ face.glyphId('!'), face.glyphId('B'), face.glyphId('C') }; const missing_lookahead = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const substitution = (try face.substitutionAt(0, &glyphs, 1)).?; try std.testing.expectEqual(@as(u16, 2), try face.gsubLookupCount()); try std.testing.expectEqual(@as(u32, face.glyphId('Z')), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(@as(usize, 0), substitution.target_offset); try std.testing.expectEqual(@as(usize, 1), substitution.skip_count); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &missing_backtrack, 1)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &missing_lookahead, 1)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(1, &glyphs, 1));}test "Face applies GSUB chained simple contextual substitutions" { const bytes = try test_font.createWithGsubChainedSimpleContextualSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const missing_backtrack = [_]u32{ face.glyphId('!'), face.glyphId('B'), face.glyphId('C') }; const missing_lookahead = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const substitution = (try face.substitutionAt(0, &glyphs, 1)).?; try std.testing.expectEqual(@as(u16, 2), try face.gsubLookupCount()); try std.testing.expectEqual(@as(u32, face.glyphId('Z')), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(@as(usize, 0), substitution.target_offset); try std.testing.expectEqual(@as(usize, 1), substitution.skip_count); try std.testing.expectEqual(face.glyphId('B'), face.substituteGlyph(face.glyphId('B'))); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &missing_backtrack, 1)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &missing_lookahead, 1)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(1, &glyphs, 1));}test "Face applies GSUB chained class contextual substitutions" { const bytes = try test_font.createWithGsubChainedClassContextualSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const missing_backtrack = [_]u32{ face.glyphId('!'), face.glyphId('B'), face.glyphId('C') }; const missing_lookahead = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const substitution = (try face.substitutionAt(0, &glyphs, 1)).?; try std.testing.expectEqual(@as(u16, 2), try face.gsubLookupCount()); try std.testing.expectEqual(@as(u32, face.glyphId('Z')), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(@as(usize, 0), substitution.target_offset); try std.testing.expectEqual(@as(usize, 1), substitution.skip_count); try std.testing.expectEqual(face.glyphId('B'), face.substituteGlyph(face.glyphId('B'))); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &missing_backtrack, 1)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &missing_lookahead, 1)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(1, &glyphs, 1));}test "Face exposes GSUB default ligature substitutions" { const bytes = try test_font.createWithGsubLigature(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('f'), face.glyphId('i') }; const substitution = (try face.substitutionAt(0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u16, 1), try face.gsubLookupCount()); try std.testing.expectEqual(@as(u32, 96), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 2), substitution.component_count); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &glyphs, 1));}test "Face applies GSUB localized forms by default" { const bytes = try test_font.createWithGsubLocalizedForms(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{face.glyphId('A')}; const disabled = LayoutSelection{ .features = &.{.{ .tag = tag("locl"), .value = 0 }} }; try std.testing.expectEqual(face.glyphId('Z'), (try face.substitutionAtFor(.{}, 0, &glyphs, 0)).?.glyph_id); try std.testing.expectEqual(face.glyphId('Z'), (try face.substitutionAtFor(.{ .vertical = true }, 0, &glyphs, 0)).?.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(disabled, 0, &glyphs, 0));}test "Face applies GSUB contextual ligatures as a horizontal default" { const bytes = try test_font.createWithGsubContextualLigature(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('f'), face.glyphId('i') }; const disabled = LayoutSelection{ .features = &.{.{ .tag = tag("clig"), .value = 0 }} }; const vertical_enabled = LayoutSelection{ .vertical = true, .features = &.{.{ .tag = tag("clig"), .value = 1 }} }; const substitution = (try face.substitutionAtFor(.{}, 0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u32, 96), substitution.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{ .vertical = true }, 0, &glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(disabled, 0, &glyphs, 0)); try std.testing.expectEqual(@as(u32, 96), (try face.substitutionAtFor(vertical_enabled, 0, &glyphs, 0)).?.glyph_id);}test "Face applies GSUB contextual alternates as horizontal defaults" { const calt_bytes = try test_font.createWithGsubContextualAlternates(std.testing.allocator); defer std.testing.allocator.free(calt_bytes); const rclt_bytes = try test_font.createWithGsubRequiredContextualAlternates(std.testing.allocator); defer std.testing.allocator.free(rclt_bytes); const calt_face = try Face.init(calt_bytes); const rclt_face = try Face.init(rclt_bytes); const calt_glyphs = [_]u32{ calt_face.glyphId('A'), calt_face.glyphId('B'), calt_face.glyphId('C') }; const rclt_glyphs = [_]u32{ rclt_face.glyphId('A'), rclt_face.glyphId('B'), rclt_face.glyphId('C') }; const disabled_calt = LayoutSelection{ .features = &.{.{ .tag = tag("calt"), .value = 0 }} }; const disabled_rclt = LayoutSelection{ .features = &.{.{ .tag = tag("rclt"), .value = 0 }} }; try std.testing.expectEqual(calt_face.glyphId('Z'), (try calt_face.substitutionAtFor(.{}, 0, &calt_glyphs, 0)).?.glyph_id); try std.testing.expectEqual(rclt_face.glyphId('Z'), (try rclt_face.substitutionAtFor(.{}, 0, &rclt_glyphs, 0)).?.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try calt_face.substitutionAtFor(.{ .vertical = true }, 0, &calt_glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try rclt_face.substitutionAtFor(.{ .vertical = true }, 0, &rclt_glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try calt_face.substitutionAtFor(disabled_calt, 0, &calt_glyphs, 0)); try std.testing.expectEqual(rclt_face.glyphId('Z'), (try rclt_face.substitutionAtFor(disabled_rclt, 0, &rclt_glyphs, 0)).?.glyph_id);}test "Face applies GSUB direction alternates by resolved direction" { const ltra_bytes = try test_font.createWithGsubLeftToRightAlternates(std.testing.allocator); defer std.testing.allocator.free(ltra_bytes); const ltrm_bytes = try test_font.createWithGsubLeftToRightMirroredForms(std.testing.allocator); defer std.testing.allocator.free(ltrm_bytes); const rtla_bytes = try test_font.createWithGsubRightToLeftAlternates(std.testing.allocator); defer std.testing.allocator.free(rtla_bytes); const rtlm_bytes = try test_font.createWithGsubRightToLeftMirroredForms(std.testing.allocator); defer std.testing.allocator.free(rtlm_bytes); const ltra_face = try Face.init(ltra_bytes); const ltrm_face = try Face.init(ltrm_bytes); const rtla_face = try Face.init(rtla_bytes); const rtlm_face = try Face.init(rtlm_bytes); const ltra_glyphs = [_]u32{ltra_face.glyphId('A')}; const ltrm_glyphs = [_]u32{ltrm_face.glyphId('A')}; const rtla_glyphs = [_]u32{rtla_face.glyphId('A')}; const rtlm_glyphs = [_]u32{rtlm_face.glyphId('A')}; const rtl_selection = LayoutSelection{ .right_to_left = true }; const disabled_ltra = LayoutSelection{ .features = &.{.{ .tag = tag("ltra"), .value = 0 }} }; const enabled_ltra_rtl = LayoutSelection{ .right_to_left = true, .features = &.{.{ .tag = tag("ltra"), .value = 1 }} }; const disabled_rtla_rtl = LayoutSelection{ .right_to_left = true, .features = &.{.{ .tag = tag("rtla"), .value = 0 }} }; try std.testing.expectEqual(ltra_face.glyphId('Z'), (try ltra_face.substitutionAtFor(.{}, 0, <ra_glyphs, 0)).?.glyph_id); try std.testing.expectEqual(ltrm_face.glyphId('Z'), (try ltrm_face.substitutionAtFor(.{}, 0, <rm_glyphs, 0)).?.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try ltra_face.substitutionAtFor(rtl_selection, 0, <ra_glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try ltrm_face.substitutionAtFor(rtl_selection, 0, <rm_glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try ltra_face.substitutionAtFor(disabled_ltra, 0, <ra_glyphs, 0)); try std.testing.expectEqual(ltra_face.glyphId('Z'), (try ltra_face.substitutionAtFor(enabled_ltra_rtl, 0, <ra_glyphs, 0)).?.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try rtla_face.substitutionAtFor(.{}, 0, &rtla_glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try rtlm_face.substitutionAtFor(.{}, 0, &rtlm_glyphs, 0)); try std.testing.expectEqual(rtla_face.glyphId('Z'), (try rtla_face.substitutionAtFor(rtl_selection, 0, &rtla_glyphs, 0)).?.glyph_id); try std.testing.expectEqual(rtlm_face.glyphId('Z'), (try rtlm_face.substitutionAtFor(rtl_selection, 0, &rtlm_glyphs, 0)).?.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try rtla_face.substitutionAtFor(disabled_rtla_rtl, 0, &rtla_glyphs, 0));}test "Face detects automatic fraction feature availability" { const frac_bytes = try test_font.createWithGsubFractions(std.testing.allocator); defer std.testing.allocator.free(frac_bytes); const component_bytes = try test_font.createWithGsubFractionComponents(std.testing.allocator); defer std.testing.allocator.free(component_bytes); const ordinary_bytes = try test_font.create(std.testing.allocator); defer std.testing.allocator.free(ordinary_bytes); const frac_face = try Face.init(frac_bytes); const component_face = try Face.init(component_bytes); const ordinary_face = try Face.init(ordinary_bytes); try std.testing.expect(frac_face.usesAutomaticFractions(.{})); try std.testing.expect(component_face.usesAutomaticFractions(.{})); try std.testing.expect(!ordinary_face.usesAutomaticFractions(.{}));}test "Face detects Arabic joining form feature availability" { const joining_bytes = try test_font.createWithGsubArabicJoiningForms(std.testing.allocator); defer std.testing.allocator.free(joining_bytes); const ordinary_bytes = try test_font.create(std.testing.allocator); defer std.testing.allocator.free(ordinary_bytes); const joining_face = try Face.init(joining_bytes); const ordinary_face = try Face.init(ordinary_bytes); try std.testing.expect(joining_face.usesArabicJoiningForms(.{})); try std.testing.expect(!ordinary_face.usesArabicJoiningForms(.{})); try std.testing.expectEqual(@as(u32, 'A' - 31), joining_face.glyphId(0x0628)); try std.testing.expectEqual(@as(u32, 'B' - 31), joining_face.glyphId(0x0627));}test "Face applies GSUB feature variations" { const bytes = try test_font.createWithGsubFeatureVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{face.glyphId('A')}; const heavy = [_]VariationSetting{.{ .tag = tag("wght"), .value = 900.0 }}; const default_substitution = (try face.substitutionAtFor(.{}, 0, &glyphs, 0)).?; try std.testing.expectEqual(face.glyphId('Z'), default_substitution.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{}, 1, &glyphs, 0)); const heavy_selection = LayoutSelection{ .variations = &heavy }; const heavy_substitution = (try face.substitutionAtFor(heavy_selection, 1, &glyphs, 0)).?; try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(heavy_selection, 0, &glyphs, 0)); try std.testing.expectEqual(face.glyphId('B'), heavy_substitution.glyph_id); try std.testing.expectEqual(face.glyphId('B'), face.substituteGlyphFor(heavy_selection, face.glyphId('A')));}test "Face applies GSUB required variation alternates" { const bytes = try test_font.createWithGsubRequiredVariationAlternates(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{face.glyphId('A')}; const ligature_glyphs = [_]u32{ face.glyphId('f'), face.glyphId('i') }; const heavy = [_]VariationSetting{.{ .tag = tag("wght"), .value = 900.0 }}; const heavy_selection = LayoutSelection{ .variations = &heavy }; const disabled_selection = LayoutSelection{ .features = &.{.{ .tag = tag("rvrn"), .value = 0 }} }; const disabled_heavy_selection = LayoutSelection{ .features = disabled_selection.features, .variations = &heavy }; const default_substitution = (try face.substitutionAtFor(.{}, 0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u16, 3), try face.gsubLookupCount()); try std.testing.expectEqual(face.glyphId('Z'), default_substitution.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{}, 1, &glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{}, 2, &ligature_glyphs, 0)); try std.testing.expectEqual(face.glyphId('Z'), face.substituteGlyphFor(.{}, face.glyphId('A'))); const heavy_substitution = (try face.substitutionAtFor(heavy_selection, 1, &glyphs, 0)).?; try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(heavy_selection, 0, &glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(heavy_selection, 2, &ligature_glyphs, 0)); try std.testing.expectEqual(face.glyphId('B'), heavy_substitution.glyph_id); try std.testing.expectEqual(face.glyphId('B'), face.substituteGlyphFor(heavy_selection, face.glyphId('A'))); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(disabled_selection, 0, &glyphs, 0)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(disabled_heavy_selection, 1, &glyphs, 0)); try std.testing.expectEqual(face.glyphId('A'), face.substituteGlyphFor(disabled_selection, face.glyphId('A')));}test "Face applies GSUB vertical alternates only for vertical selection" { const bytes = try test_font.createWithGsubVerticalAlternates(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{face.glyphId('A')}; try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{}, 0, &glyphs, 0)); const vertical_substitution = (try face.substitutionAtFor(.{ .vertical = true }, 0, &glyphs, 0)).?; try std.testing.expectEqual(face.glyphId('B'), vertical_substitution.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{ .vertical = true, .features = &.{.{ .tag = tag("vert"), .value = 0 }}, }, 0, &glyphs, 0));}test "Face applies GSUB vertical rotation alternates only for vertical selection" { const bytes = try test_font.createWithGsubVerticalRotationAlternates(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{face.glyphId('A')}; try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{}, 0, &glyphs, 0)); const vertical_substitution = (try face.substitutionAtFor(.{ .vertical = true }, 0, &glyphs, 0)).?; try std.testing.expectEqual(face.glyphId('C'), vertical_substitution.glyph_id); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{ .vertical = true, .features = &.{.{ .tag = tag("vrt2"), .value = 0 }}, }, 0, &glyphs, 0));}test "Face activates GSUB substitutions for selected script tags" { const bytes = try test_font.createWithGsubLigature(std.testing.allocator); defer std.testing.allocator.free(bytes); try fixture_binary.replaceLayoutScript(bytes, "GSUB", "latn"); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('f'), face.glyphId('i') }; try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &glyphs, 0)); const substitution = (try face.substitutionAtFor(.{ .script = tag("latn") }, 0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u32, 96), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 2), substitution.component_count); const fallback = try face.substitutionAtFor(.{ .script = tag("cyrl") }, 0, &glyphs, 0); try std.testing.expectEqual(@as(?GsubSubstitution, null), fallback);}test "Face resolves GSUB substitutions from ordered script tags" { const bytes = try test_font.createWithGsubLigature(std.testing.allocator); defer std.testing.allocator.free(bytes); try fixture_binary.replaceLayoutScript(bytes, "GSUB", "dev2"); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('f'), face.glyphId('i') }; const script_tags = [_]Tag{ tag("dev3"), tag("dev2"), tag("deva") }; try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAtFor(.{ .script = tag("deva") }, 0, &glyphs, 0)); const substitution = (try face.substitutionAtFor(.{ .script = tag("deva"), .script_tags = &script_tags }, 0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u32, 96), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 2), substitution.component_count);}test "Face exposes GSUB default multiple substitutions" { const bytes = try test_font.createWithGsubMultipleSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const glyphs = [_]u32{ a, face.glyphId('!') }; const substitution = (try face.substitutionAt(0, &glyphs, 0)).?; try std.testing.expectEqual(@as(u16, 1), try face.gsubLookupCount()); try std.testing.expectEqual(face.glyphId('X'), substitution.glyph_id); try std.testing.expectEqual(@as(u16, 1), substitution.component_count); try std.testing.expectEqual(@as(u16, 2), substitution.replacement_count); try std.testing.expectEqual(@as(?u32, face.glyphId('X')), face.substitutionGlyph(substitution, 0)); try std.testing.expectEqual(@as(?u32, face.glyphId('Y')), face.substitutionGlyph(substitution, 1)); try std.testing.expectEqual(@as(?u32, null), face.substitutionGlyph(substitution, 2)); try std.testing.expectEqual(a, face.substituteGlyph(a)); try std.testing.expectEqual(@as(?GsubSubstitution, null), try face.substitutionAt(0, &glyphs, 1));}test "Face applies GPOS pair adjustments" { const bytes = try test_font.createWithGposPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const v = face.glyphId('V'); const adjustment = face.gposPairAdjustment(a, v); try std.testing.expectEqual(@as(i32, -80), adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.right.x_advance); try std.testing.expect(face.gposPairAdjustment(v, a).isZero()); try std.testing.expect(!face.hasGposSingleAdjustment()); try std.testing.expect(!face.hasGposCursiveAttachment()); try std.testing.expect(!face.hasGposMarkToBase()); try std.testing.expect(!face.hasGposMarkToLigature()); try std.testing.expect(!face.hasGposMarkToMark());}test "Face applies GPOS vertical pair adjustments only for vertical selection" { const bytes = try test_font.createWithGposVerticalPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const v = face.glyphId('V'); try std.testing.expect(face.gposPairAdjustmentFor(.{}, a, v).isZero()); const vertical = face.gposPairAdjustmentFor(.{ .vertical = true }, a, v); try std.testing.expectEqual(@as(i32, 0), vertical.left.x_advance); try std.testing.expectEqual(@as(i32, -80), vertical.left.y_advance); try std.testing.expectEqual(@as(i32, 0), vertical.right.y_advance); const disabled = face.gposPairAdjustmentFor(.{ .vertical = true, .features = &.{.{ .tag = tag("vkrn"), .value = 0 }}, }, a, v); try std.testing.expect(disabled.isZero());}test "Face applies GPOS pair adjustments across ignored marks" { const bytes = try test_font.createWithGposPairIgnoreMarks(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('^'), face.glyphId('V'), }; const adjustment = face.gposPairAdjustmentBeforeFor(.{}, &glyphs, 2).?; try std.testing.expectEqual(@as(usize, 2), adjustment.left_offset); try std.testing.expectEqual(@as(i32, -80), adjustment.adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.adjustment.right.x_advance); try std.testing.expectEqual(@as(?GposPairAdjustmentBefore, null), face.gposPairAdjustmentBeforeFor(.{}, &glyphs, 1));}test "Face keeps unfiltered GPOS pair adjustments adjacent" { const bytes = try test_font.createWithGposPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('^'), face.glyphId('V'), }; try std.testing.expectEqual(@as(?GposPairAdjustmentBefore, null), face.gposPairAdjustmentBeforeFor(.{}, &glyphs, 2));}test "Face applies GPOS contextual pair adjustments" { const bytes = try test_font.createWithGposContextualPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('V'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('V'), face.glyphId('!') }; const adjustment = face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 0).?; try std.testing.expect(face.gposPairAdjustment(face.glyphId('A'), face.glyphId('V')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.adjustment.right.x_advance); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 1)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &mismatch, 0));}test "Face applies GPOS simple contextual pair adjustments" { const bytes = try test_font.createWithGposSimpleContextualPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('V'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('V'), face.glyphId('!') }; const adjustment = face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 0).?; try std.testing.expect(face.gposPairAdjustment(face.glyphId('A'), face.glyphId('V')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.adjustment.right.x_advance); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 1)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &mismatch, 0));}test "Face applies GPOS class contextual pair adjustments" { const bytes = try test_font.createWithGposClassContextualPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 0).?; try std.testing.expect(face.gposPairAdjustment(face.glyphId('A'), face.glyphId('B')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.adjustment.right.x_advance); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 1)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &mismatch, 0));}test "Face applies GPOS chained contextual pair adjustments" { const bytes = try test_font.createWithGposChainedContextualPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('V'), face.glyphId('C'), face.glyphId('!') }; const lookahead_mismatch = [_]u32{ face.glyphId('A'), face.glyphId('V'), face.glyphId('C'), face.glyphId('A') }; const adjustment = face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 1).?; try std.testing.expect(face.gposPairAdjustment(face.glyphId('V'), face.glyphId('C')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.adjustment.right.x_advance); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 0)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &lookahead_mismatch, 1));}test "Face applies GPOS chained simple contextual pair adjustments" { const bytes = try test_font.createWithGposChainedSimpleContextualPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const missing_backtrack = [_]u32{ face.glyphId('B'), face.glyphId('C'), face.glyphId('!') }; const missing_input = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 1).?; try std.testing.expect(face.gposPairAdjustment(face.glyphId('B'), face.glyphId('C')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.adjustment.right.x_advance); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 0)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &missing_backtrack, 0)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &missing_input, 1));}test "Face applies GPOS chained class contextual pair adjustments" { const bytes = try test_font.createWithGposChainedClassContextualPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const missing_backtrack = [_]u32{ face.glyphId('B'), face.glyphId('C'), face.glyphId('!') }; const missing_input = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 1).?; try std.testing.expect(face.gposPairAdjustment(face.glyphId('B'), face.glyphId('C')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.adjustment.right.x_advance); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &glyphs, 0)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &missing_backtrack, 0)); try std.testing.expectEqual(@as(?GposContextualPairAdjustment, null), face.gposContextualPairAdjustmentAtFor(.{}, &missing_input, 1));}test "Face activates GPOS adjustments for selected script tags" { const bytes = try test_font.createWithGposPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); try fixture_binary.replaceLayoutScript(bytes, "GPOS", "latn"); const face = try Face.init(bytes); const a = face.glyphId('A'); const v = face.glyphId('V'); try std.testing.expect(face.gposPairAdjustment(a, v).isZero()); const adjustment = face.gposPairAdjustmentFor(.{ .script = tag("latn") }, a, v); try std.testing.expectEqual(@as(i32, -80), adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.right.x_advance); try std.testing.expect(face.gposPairAdjustmentFor(.{ .script = tag("cyrl") }, a, v).isZero());}test "Face resolves GPOS adjustments from ordered script tags" { const bytes = try test_font.createWithGposPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); try fixture_binary.replaceLayoutScript(bytes, "GPOS", "bng2"); const face = try Face.init(bytes); const a = face.glyphId('A'); const v = face.glyphId('V'); const script_tags = [_]Tag{ tag("bng3"), tag("bng2"), tag("beng") }; try std.testing.expect(face.gposPairAdjustmentFor(.{ .script = tag("beng") }, a, v).isZero()); const adjustment = face.gposPairAdjustmentFor(.{ .script = tag("beng"), .script_tags = &script_tags }, a, v); try std.testing.expectEqual(@as(i32, -80), adjustment.left.x_advance); try std.testing.expectEqual(@as(i32, 0), adjustment.right.x_advance);}test "Face applies GPOS single adjustments" { const bytes = try test_font.createWithGposSingleAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const b = face.glyphId('B'); const bang = face.glyphId('!'); try std.testing.expect(face.hasGposSingleAdjustment()); const a_adjustment = face.gposSingleAdjustment(a); try std.testing.expectEqual(@as(i32, 20), a_adjustment.x_placement); try std.testing.expectEqual(@as(i32, 30), a_adjustment.y_placement); try std.testing.expectEqual(@as(i32, -40), a_adjustment.x_advance); const b_adjustment = face.gposSingleAdjustment(b); try std.testing.expectEqual(@as(i32, -10), b_adjustment.x_placement); try std.testing.expectEqual(@as(i32, 0), b_adjustment.y_placement); try std.testing.expectEqual(@as(i32, 0), b_adjustment.x_advance); try std.testing.expect(face.gposSingleAdjustment(bang).isZero()); const vertical = LayoutSelection{ .vertical = true }; const vertical_enabled = LayoutSelection{ .vertical = true, .features = &.{.{ .tag = tag("dist"), .value = 1 }} }; const glyphs = [_]u32{a}; try std.testing.expect(!face.hasGposSingleAdjustmentFor(vertical)); try std.testing.expect(face.hasGposSingleAdjustmentFor(vertical_enabled)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(vertical, &glyphs, 0)); try std.testing.expectEqual(@as(i32, 20), face.gposSingleAdjustmentAtFor(vertical_enabled, &glyphs, 0).?.adjustment.x_placement);}test "Face applies GPOS feature variations" { const bytes = try test_font.createWithGposFeatureVariations(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyph = face.glyphId('A'); const heavy = [_]VariationSetting{.{ .tag = tag("wght"), .value = 900.0 }}; const heavy_selection = LayoutSelection{ .variations = &heavy }; const default_adjustment = face.gposSingleAdjustmentFor(.{}, glyph); try std.testing.expectEqual(@as(i32, -40), default_adjustment.x_advance); try std.testing.expectEqual(@as(i32, 0), default_adjustment.x_placement); const heavy_adjustment = face.gposSingleAdjustmentFor(heavy_selection, glyph); try std.testing.expect(face.hasGposSingleAdjustmentFor(heavy_selection)); try std.testing.expectEqual(@as(i32, -120), heavy_adjustment.x_advance); try std.testing.expectEqual(@as(i32, 0), heavy_adjustment.x_placement);}test "Face applies GPOS contextual single adjustments" { const bytes = try test_font.createWithGposContextualSingleAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposSingleAdjustmentAtFor(.{}, &glyphs, 0).?; try std.testing.expect(face.hasGposSingleAdjustment()); try std.testing.expect(face.gposSingleAdjustment(face.glyphId('B')).isZero()); try std.testing.expectEqual(@as(usize, 1), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 3), adjustment.skip_count); try std.testing.expectEqual(@as(i32, 50), adjustment.adjustment.x_placement); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.x_advance); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &mismatch, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &glyphs, 1));}test "Face applies GPOS simple contextual single adjustments" { const bytes = try test_font.createWithGposSimpleContextualSingleAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposSingleAdjustmentAtFor(.{}, &glyphs, 0).?; try std.testing.expect(face.hasGposSingleAdjustment()); try std.testing.expect(face.gposSingleAdjustment(face.glyphId('B')).isZero()); try std.testing.expectEqual(@as(usize, 1), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 3), adjustment.skip_count); try std.testing.expectEqual(@as(i32, 50), adjustment.adjustment.x_placement); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.x_advance); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &mismatch, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &glyphs, 1));}test "Face applies GPOS class contextual single adjustments" { const bytes = try test_font.createWithGposClassContextualSingleAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposSingleAdjustmentAtFor(.{}, &glyphs, 0).?; try std.testing.expect(face.hasGposSingleAdjustment()); try std.testing.expect(face.gposSingleAdjustment(face.glyphId('B')).isZero()); try std.testing.expectEqual(@as(usize, 1), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 3), adjustment.skip_count); try std.testing.expectEqual(@as(i32, 50), adjustment.adjustment.x_placement); try std.testing.expectEqual(@as(i32, -100), adjustment.adjustment.x_advance); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &mismatch, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &glyphs, 1));}test "Face applies GPOS chained contextual single adjustments" { const bytes = try test_font.createWithGposChainedContextualSingleAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C'), face.glyphId('!') }; const lookahead_mismatch = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C'), face.glyphId('A') }; const adjustment = face.gposSingleAdjustmentAtFor(.{}, &glyphs, 1).?; try std.testing.expect(face.hasGposSingleAdjustment()); try std.testing.expect(face.gposSingleAdjustment(face.glyphId('B')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 2), adjustment.skip_count); try std.testing.expectEqual(@as(i32, 75), adjustment.adjustment.x_placement); try std.testing.expectEqual(@as(i32, -125), adjustment.adjustment.x_advance); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &glyphs, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &lookahead_mismatch, 1));}test "Face applies GPOS chained simple contextual single adjustments" { const bytes = try test_font.createWithGposChainedSimpleContextualSingleAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const missing_backtrack = [_]u32{ face.glyphId('B'), face.glyphId('C'), face.glyphId('!') }; const missing_lookahead = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposSingleAdjustmentAtFor(.{}, &glyphs, 1).?; try std.testing.expect(face.hasGposSingleAdjustment()); try std.testing.expect(face.gposSingleAdjustment(face.glyphId('B')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, 75), adjustment.adjustment.x_placement); try std.testing.expectEqual(@as(i32, -125), adjustment.adjustment.x_advance); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &glyphs, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &missing_backtrack, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &missing_lookahead, 1));}test "Face applies GPOS chained class contextual single adjustments" { const bytes = try test_font.createWithGposChainedClassContextualSingleAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const glyphs = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('C') }; const missing_backtrack = [_]u32{ face.glyphId('B'), face.glyphId('C'), face.glyphId('!') }; const missing_lookahead = [_]u32{ face.glyphId('A'), face.glyphId('B'), face.glyphId('!') }; const adjustment = face.gposSingleAdjustmentAtFor(.{}, &glyphs, 1).?; try std.testing.expect(face.hasGposSingleAdjustment()); try std.testing.expect(face.gposSingleAdjustment(face.glyphId('B')).isZero()); try std.testing.expectEqual(@as(usize, 0), adjustment.target_offset); try std.testing.expectEqual(@as(usize, 1), adjustment.skip_count); try std.testing.expectEqual(@as(i32, 75), adjustment.adjustment.x_placement); try std.testing.expectEqual(@as(i32, -125), adjustment.adjustment.x_advance); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &glyphs, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &missing_backtrack, 0)); try std.testing.expectEqual(@as(?GposSingleAdjustment, null), face.gposSingleAdjustmentAtFor(.{}, &missing_lookahead, 1));}test "Face applies GPOS cursive attachments" { const bytes = try test_font.createWithGposCursiveAttachment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const b = face.glyphId('B'); try std.testing.expect(face.hasGposCursiveAttachment()); const attachment = face.gposCursiveAttachment(a, b).?; try std.testing.expectEqual(@as(i32, 460), attachment.x_anchor_delta); try std.testing.expectEqual(@as(i32, 100), attachment.y_anchor_delta); try std.testing.expect(face.gposCursiveAttachment(b, a) == null); try std.testing.expect(face.gposCursiveAttachment(a, face.glyphId('!')) == null); const vertical = LayoutSelection{ .vertical = true }; const vertical_enabled = LayoutSelection{ .vertical = true, .features = &.{.{ .tag = tag("curs"), .value = 1 }} }; try std.testing.expect(!face.hasGposCursiveAttachmentFor(vertical)); try std.testing.expect(face.hasGposCursiveAttachmentFor(vertical_enabled)); try std.testing.expect(face.gposCursiveAttachmentFor(vertical, a, b) == null); try std.testing.expectEqual(@as(i32, 460), face.gposCursiveAttachmentFor(vertical_enabled, a, b).?.x_anchor_delta);}test "Face applies GPOS mark-to-base attachment" { const bytes = try test_font.createWithGposMarkToBase(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const a = face.glyphId('A'); const mark = face.glyphId('^'); try std.testing.expect(face.hasGposMarkToBase()); const adjustment = face.gposMarkToBaseAdjustment(a, mark).?; try std.testing.expectEqual(@as(i32, 150), adjustment.x_placement); try std.testing.expectEqual(@as(i32, 700), adjustment.y_placement); try std.testing.expect(face.gposMarkToBaseAdjustment(mark, a) == null); try std.testing.expect(face.gposMarkToBaseAdjustment(a, face.glyphId('!')) == null);}test "Face applies GPOS above and below-base mark positioning by default" { const abvm_bytes = try test_font.createWithGposAboveBaseMarkPositioning(std.testing.allocator); defer std.testing.allocator.free(abvm_bytes); const blwm_bytes = try test_font.createWithGposBelowBaseMarkPositioning(std.testing.allocator); defer std.testing.allocator.free(blwm_bytes); const abvm_face = try Face.init(abvm_bytes); const blwm_face = try Face.init(blwm_bytes); const abvm_base = abvm_face.glyphId('A'); const abvm_mark = abvm_face.glyphId('^'); const blwm_base = blwm_face.glyphId('A'); const blwm_mark = blwm_face.glyphId('^'); const disabled_abvm = LayoutSelection{ .features = &.{.{ .tag = tag("abvm"), .value = 0 }} }; const disabled_blwm = LayoutSelection{ .features = &.{.{ .tag = tag("blwm"), .value = 0 }} }; try std.testing.expectEqual(@as(i32, 150), abvm_face.gposMarkToBaseAdjustment(abvm_base, abvm_mark).?.x_placement); try std.testing.expectEqual(@as(i32, 700), abvm_face.gposMarkToBaseAdjustmentFor(.{ .vertical = true }, abvm_base, abvm_mark).?.y_placement); try std.testing.expectEqual(@as(i32, 150), blwm_face.gposMarkToBaseAdjustment(blwm_base, blwm_mark).?.x_placement); try std.testing.expectEqual(@as(i32, 700), blwm_face.gposMarkToBaseAdjustmentFor(.{ .vertical = true }, blwm_base, blwm_mark).?.y_placement); try std.testing.expect(abvm_face.gposMarkToBaseAdjustmentFor(disabled_abvm, abvm_base, abvm_mark) == null); try std.testing.expect(blwm_face.gposMarkToBaseAdjustmentFor(disabled_blwm, blwm_base, blwm_mark) == null);}test "Face applies GPOS mark-to-ligature attachment" { const bytes = try test_font.createWithGposMarkToLigature(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const ligature = @as(u32, 96); const mark = face.glyphId('^'); try std.testing.expect(face.hasGposMarkToLigature()); const first_component = face.gposMarkToLigatureAdjustment(ligature, mark, 0).?; try std.testing.expectEqual(@as(i32, 80), first_component.x_placement); try std.testing.expectEqual(@as(i32, 700), first_component.y_placement); const second_component = face.gposMarkToLigatureAdjustment(ligature, mark, 1).?; try std.testing.expectEqual(@as(i32, 230), second_component.x_placement); try std.testing.expectEqual(@as(i32, 650), second_component.y_placement); try std.testing.expect(face.gposMarkToLigatureAdjustment(ligature, mark, 2) == null); try std.testing.expect(face.gposMarkToLigatureAdjustment(mark, ligature, 0) == null); try std.testing.expect(face.gposMarkToLigatureAdjustment(ligature, face.glyphId('!'), 0) == null);}test "Face applies GPOS mark-to-mark attachment" { const bytes = try test_font.createWithGposMarkToMark(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const base_mark = face.glyphId('^'); const mark = face.glyphId('_'); try std.testing.expect(face.hasGposMarkToMark()); const adjustment = face.gposMarkToMarkAdjustment(base_mark, mark).?; try std.testing.expectEqual(@as(i32, 20), adjustment.x_placement); try std.testing.expectEqual(@as(i32, 300), adjustment.y_placement); try std.testing.expect(face.gposMarkToMarkAdjustment(mark, base_mark) == null); try std.testing.expect(face.gposMarkToMarkAdjustment(base_mark, face.glyphId('!')) == null);}test "Face reads GDEF glyph classes" { const bytes = try test_font.createWithGdefGlyphClasses(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); try std.testing.expectEqual(GlyphClass.base, face.glyphClass(face.glyphId('A'))); try std.testing.expectEqual(GlyphClass.mark, face.glyphClass(face.glyphId('^'))); try std.testing.expectEqual(GlyphClass.component, face.glyphClass(face.glyphId('_'))); try std.testing.expectEqual(GlyphClass.ligature, face.glyphClass(96)); try std.testing.expectEqual(GlyphClass.unknown, face.glyphClass(face.glyphId('!')));}test "Face applies GPOS mark-to-base with matching mark attachment type" { const bytes = try test_font.createWithGposMarkToBase(std.testing.allocator); defer std.testing.allocator.free(bytes); try fixture_binary.setLayoutLookupFlag(bytes, "GPOS", 0, 0x0300); const gdef_offset = try fixture_binary.tableOffset(bytes, "GDEF"); fixture_binary.writeU16(bytes, gdef_offset + 10, 12); const face = try Face.init(bytes); const adjustment = face.gposMarkToBaseAdjustment(face.glyphId('A'), face.glyphId('^')).?; try std.testing.expectEqual(@as(i32, 150), adjustment.x_placement); try std.testing.expectEqual(@as(i32, 700), adjustment.y_placement);}test "Face filters GPOS mark-to-base with mismatched mark attachment type" { const bytes = try test_font.createWithGposMarkToBase(std.testing.allocator); defer std.testing.allocator.free(bytes); try fixture_binary.setLayoutLookupFlag(bytes, "GPOS", 0, 0x0200); const gdef_offset = try fixture_binary.tableOffset(bytes, "GDEF"); fixture_binary.writeU16(bytes, gdef_offset + 10, 12); const face = try Face.init(bytes); try std.testing.expect(face.gposMarkToBaseAdjustment(face.glyphId('A'), face.glyphId('^')) == null);}test "Face reads GDEF ligature caret coordinates" { const bytes = try test_font.createWithGsubLigatureAndGdefCarets(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const caret = face.ligatureCaret(96, 0).?; try std.testing.expectEqual(@as(u16, 1), face.ligatureCaretCount(96)); try std.testing.expectEqual(@as(i16, 250), caret.coordinate); try std.testing.expectEqual(false, caret.synthesized); try std.testing.expect(face.ligatureCaret(96, 1) == null); try std.testing.expectEqual(@as(u16, 0), face.ligatureCaretCount(face.glyphId('A'))); try std.testing.expect(face.ligatureCaret(face.glyphId('A'), 0) == null);}test "Face exposes horizontal MATH assemblies" { const bytes = try test_font.createWithMathAssembly(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const arrow = face.glyphId(0x2192); var assembly = (try face.mathHorizontalAssemblyAlloc(std.testing.allocator, arrow, 1300)).?; defer assembly.deinit(std.testing.allocator); try std.testing.expect(arrow != 0); try std.testing.expect(face.mathHorizontalVariant(arrow, 1300) == null); try std.testing.expectEqual(@as(u16, 1300), assembly.advance_measurement); try std.testing.expectEqual(@as(usize, 4), assembly.parts.len);}test "Face exposes MATH top accent attachments" { const bytes = try test_font.createWithMathAssembly(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const base = face.mathTopAccentAttachment(face.glyphId('A')).?; const accent = face.mathTopAccentAttachment(face.glyphId('`')).?; try std.testing.expectEqual(@as(i16, 420), base.value); try std.testing.expectEqual(@as(i16, 80), accent.value); try std.testing.expect(face.mathTopAccentAttachment(face.glyphId('B')) == null);}test "Face ignores malformed optional GSUB table" { const bytes = try test_font.createWithGsubSingleSubstitution(std.testing.allocator); defer std.testing.allocator.free(bytes); const gsub_table = try table(bytes, "GSUB"); std.mem.writeInt(u16, bytes[gsub_table.offset..][0..2], 2, .big); const face = try Face.init(bytes); const a = face.glyphId('A'); try std.testing.expectEqual(a, face.substituteGlyph(a));}test "Face ignores malformed optional GPOS table" { const bytes = try test_font.createWithGposPairAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const gpos_table = try table(bytes, "GPOS"); std.mem.writeInt(u16, bytes[gpos_table.offset..][0..2], 2, .big); const face = try Face.init(bytes); try std.testing.expect(face.gposPairAdjustment(face.glyphId('A'), face.glyphId('V')).isZero());}test "Face ignores malformed optional GDEF table" { const bytes = try test_font.createWithGdefGlyphClasses(std.testing.allocator); defer std.testing.allocator.free(bytes); const gdef_table = try table(bytes, "GDEF"); std.mem.writeInt(u16, bytes[gdef_table.offset..][0..2], 2, .big); const face = try Face.init(bytes); try std.testing.expectEqual(GlyphClass.unknown, face.glyphClass(face.glyphId('A')));}test "Face ignores malformed optional kern table" { const bytes = try test_font.createWithKern(std.testing.allocator); defer std.testing.allocator.free(bytes); const kern_table = try table(bytes, "kern"); std.mem.writeInt(u16, bytes[kern_table.offset + 6 ..][0..2], 2, .big); const face = try Face.init(bytes); try std.testing.expectEqual(@as(i16, 0), face.kerning(face.glyphId('A'), face.glyphId('V')));}test "GPOS value records apply GDEF variation deltas" { const bytes = try test_font.createWithGposSingleVariationAdjustment(std.testing.allocator); defer std.testing.allocator.free(bytes); const face = try Face.init(bytes); const adjustment = face.gposSingleAdjustment(face.glyphId('A')); try std.testing.expectEqual(@as(i32, 20), adjustment.x_placement); try std.testing.expectEqual(@as(i32, 20), adjustment.withVariations(face, &.{}).x_placement); try std.testing.expectEqual(@as(i32, 60), adjustment.withVariations(face, &.{.{ .tag = tag("wght"), .value = 650.0 }}).x_placement); try std.testing.expectEqual(@as(i32, 100), adjustment.withVariations(face, &.{.{ .tag = tag("wght"), .value = 900.0 }}).x_placement);}test "GDEF reads mark attachment classes and mark glyph sets" { const bytes = try createGdefMarkFilterFixture(std.testing.allocator); defer std.testing.allocator.free(bytes); const gdef = try Gdef.init(bytes, .{ .offset = 0, .len = bytes.len }, null); try std.testing.expectEqual(GlyphClass.mark, try gdef.glyphClass(bytes, '^' - 31)); try std.testing.expectEqual(@as(u16, 1), try gdef.markAttachmentClass(bytes, '^' - 31)); try std.testing.expectEqual(@as(u16, 2), try gdef.markAttachmentClass(bytes, '_' - 31)); try std.testing.expect(try gdef.markGlyphSetContains(bytes, 0, '^' - 31)); try std.testing.expect(!try gdef.markGlyphSetContains(bytes, 0, '_' - 31));}test "lookup filters use GDEF mark classes and mark glyph sets" { const bytes = try createGdefMarkFilterFixture(std.testing.allocator); defer std.testing.allocator.free(bytes); const gdef = try Gdef.init(bytes, .{ .offset = 0, .len = bytes.len }, null); try std.testing.expect(!try lookupGlyphIgnored(bytes, gdef, .{ .flags = 0x0100 }, '^' - 31)); try std.testing.expect(try lookupGlyphIgnored(bytes, gdef, .{ .flags = 0x0100 }, '_' - 31)); try std.testing.expect(!try lookupGlyphIgnored(bytes, gdef, .{ .flags = lookup_use_mark_filtering_set, .mark_filtering_set = 0 }, '^' - 31)); try std.testing.expect(try lookupGlyphIgnored(bytes, gdef, .{ .flags = lookup_use_mark_filtering_set, .mark_filtering_set = 0 }, '_' - 31));}fn createGdefMarkFilterFixture(allocator: std.mem.Allocator) ![]u8 { var out = std.ArrayListUnmanaged(u8).empty; errdefer out.deinit(allocator); try fixture_binary.appendU16(allocator, &out, 1); try fixture_binary.appendU16(allocator, &out, 2); try fixture_binary.appendU16(allocator, &out, 14); try fixture_binary.appendU16(allocator, &out, 0); try fixture_binary.appendU16(allocator, &out, 0); try fixture_binary.appendU16(allocator, &out, 30); try fixture_binary.appendU16(allocator, &out, 46); try fixture_binary.appendU16(allocator, &out, 2); try fixture_binary.appendU16(allocator, &out, 2); try fixture_binary.appendU16(allocator, &out, '^' - 31); try fixture_binary.appendU16(allocator, &out, '^' - 31); try fixture_binary.appendU16(allocator, &out, 3); try fixture_binary.appendU16(allocator, &out, '_' - 31); try fixture_binary.appendU16(allocator, &out, '_' - 31); try fixture_binary.appendU16(allocator, &out, 3); try fixture_binary.appendU16(allocator, &out, 2); try fixture_binary.appendU16(allocator, &out, 2); try fixture_binary.appendU16(allocator, &out, '^' - 31); try fixture_binary.appendU16(allocator, &out, '^' - 31); try fixture_binary.appendU16(allocator, &out, 1); try fixture_binary.appendU16(allocator, &out, '_' - 31); try fixture_binary.appendU16(allocator, &out, '_' - 31); try fixture_binary.appendU16(allocator, &out, 2); try fixture_binary.appendU16(allocator, &out, 1); try fixture_binary.appendU16(allocator, &out, 1); try fixture_binary.appendU32(allocator, &out, 8); try fixture_binary.appendU16(allocator, &out, 1); try fixture_binary.appendU16(allocator, &out, 1); try fixture_binary.appendU16(allocator, &out, '^' - 31); return out.toOwnedSlice(allocator);}Source: lib/filigree/src/font/root.zig:6
zig
pub const face = @import("face.zig");Audit
| Definitions | 1 |
|---|---|
| Public names | 1 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |