lib/filigree/src/font/model.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 
  3 pub const FontError = error{
  4     InvalidFont,
  5     MissingTable,
  6     UnsupportedCmap,
  7     InvalidCmap,
  8     InvalidKern,
  9     InvalidGsub,
 10     InvalidGpos,
 11     InvalidGdef,
 12     InvalidVariations,
 13 };
 14 
 15 pub const Tag = u32;
 16 
 17 pub fn tag(comptime name: []const u8) Tag {
 18     if (name.len != 4) @compileError("OpenType tags are four bytes");
 19     return (@as(u32, name[0]) << 24) |
 20         (@as(u32, name[1]) << 16) |
 21         (@as(u32, name[2]) << 8) |
 22         @as(u32, name[3]);
 23 }
 24 
 25 pub const GlyphClass = enum {
 26     unknown,
 27     base,
 28     ligature,
 29     mark,
 30     component,
 31 };
 32 
 33 pub const LigatureCaret = struct {
 34     coordinate: i16,
 35     synthesized: bool = false,
 36 };
 37 
 38 pub const defaultScriptTag: Tag = tag("DFLT");
 39 
 40 pub const FeatureRange = struct {
 41     start: u32 = 0,
 42     end: u32 = std.math.maxInt(u32),
 43 };
 44 
 45 pub const FeatureSetting = struct {
 46     tag: Tag,
 47     value: u32 = 1,
 48     source: ?FeatureRange = null,
 49 };
 50 
 51 pub const VariationAxis = struct {
 52     tag: Tag,
 53     min_value: i32,
 54     default_value: i32,
 55     max_value: i32,
 56     flags: u16 = 0,
 57     name_id: u16 = 0,
 58 };
 59 
 60 pub const VariationSetting = struct {
 61     tag: Tag,
 62     value: f32,
 63 };
 64 
 65 pub const NormalizedVariation = struct {
 66     tag: Tag,
 67     value: i16,
 68 };
 69 
 70 pub const LayoutSelection = struct {
 71     script: Tag = defaultScriptTag,
 72     script_tags: []const Tag = &.{},
 73     language: ?Tag = null,
 74     features: []const FeatureSetting = &.{},
 75     variations: []const VariationSetting = &.{},
 76     vertical: bool = false,
 77     right_to_left: bool = false,
 78 };
 79 
 80 pub fn featureEnabled(selection: LayoutSelection, feature_tag: Tag, default_on: bool, source: ?FeatureRange) bool {
 81     var enabled = default_on;
 82     for (selection.features) |setting| {
 83         if (setting.tag != feature_tag) continue;
 84         if (!featureSettingApplies(setting, source)) continue;
 85         enabled = setting.value != 0;
 86     }
 87     return enabled;
 88 }
 89 
 90 pub fn fixed16ToFloat(value: i32) f32 {
 91     return @as(f32, @floatFromInt(value)) / 65536.0;
 92 }
 93 
 94 fn featureSettingApplies(setting: FeatureSetting, source: ?FeatureRange) bool {
 95     const setting_source = setting.source orelse return true;
 96     const lookup_source = source orelse return false;
 97     return setting_source.start < lookup_source.end and lookup_source.start < setting_source.end;
 98 }
 99 
100 pub const noGposVariationIndex: u32 = std.math.maxInt(u32);
101 
102 pub const GsubSubstitution = struct {
103     glyph_id: u32,
104     component_count: u16,
105     replacement_count: u16 = 1,
106     replacement_glyphs_offset: usize = 0,
107     target_offset: usize = 0,
108     skip_count: usize = 0,
109 };
110 
111 pub const GposValueAdjustment = struct {
112     x_placement: i32 = 0,
113     y_placement: i32 = 0,
114     x_advance: i32 = 0,
115     y_advance: i32 = 0,
116     x_placement_variation: u32 = noGposVariationIndex,
117     y_placement_variation: u32 = noGposVariationIndex,
118     x_advance_variation: u32 = noGposVariationIndex,
119     y_advance_variation: u32 = noGposVariationIndex,
120 
121     pub fn isZero(self: GposValueAdjustment) bool {
122         return self.x_placement == 0 and
123             self.y_placement == 0 and
124             self.x_advance == 0 and
125             self.y_advance == 0 and
126             self.x_placement_variation == noGposVariationIndex and
127             self.y_placement_variation == noGposVariationIndex and
128             self.x_advance_variation == noGposVariationIndex and
129             self.y_advance_variation == noGposVariationIndex;
130     }
131 
132     pub fn withVariations(self: GposValueAdjustment, face: anytype, settings: []const VariationSetting) GposValueAdjustment {
133         return .{
134             .x_placement = face.gposVariationValue(self.x_placement, settings, self.x_placement_variation),
135             .y_placement = face.gposVariationValue(self.y_placement, settings, self.y_placement_variation),
136             .x_advance = face.gposVariationValue(self.x_advance, settings, self.x_advance_variation),
137             .y_advance = face.gposVariationValue(self.y_advance, settings, self.y_advance_variation),
138         };
139     }
140 
141     pub fn add(self: *GposValueAdjustment, other: GposValueAdjustment) void {
142         self.x_placement += other.x_placement;
143         self.y_placement += other.y_placement;
144         self.x_advance += other.x_advance;
145         self.y_advance += other.y_advance;
146         self.x_placement_variation = mergeGposVariationIndex(self.x_placement_variation, other.x_placement_variation);
147         self.y_placement_variation = mergeGposVariationIndex(self.y_placement_variation, other.y_placement_variation);
148         self.x_advance_variation = mergeGposVariationIndex(self.x_advance_variation, other.x_advance_variation);
149         self.y_advance_variation = mergeGposVariationIndex(self.y_advance_variation, other.y_advance_variation);
150     }
151 };
152 
153 pub const GposPairAdjustment = struct {
154     left: GposValueAdjustment = .{},
155     right: GposValueAdjustment = .{},
156 
157     pub fn isZero(self: GposPairAdjustment) bool {
158         return self.left.isZero() and self.right.isZero();
159     }
160 
161     pub fn add(self: *GposPairAdjustment, other: GposPairAdjustment) void {
162         self.left.add(other.left);
163         self.right.add(other.right);
164     }
165 };
166 
167 pub const GposPairAdjustmentBefore = struct {
168     adjustment: GposPairAdjustment = .{},
169     left_offset: usize = 0,
170 
171     pub fn isZero(self: GposPairAdjustmentBefore) bool {
172         return self.adjustment.isZero();
173     }
174 };
175 
176 pub const GposContextualPairAdjustment = struct {
177     adjustment: GposPairAdjustment = .{},
178     target_offset: usize = 0,
179     skip_count: usize = 0,
180 
181     pub fn isZero(self: GposContextualPairAdjustment) bool {
182         return self.adjustment.isZero();
183     }
184 };
185 
186 pub const GposSingleAdjustment = struct {
187     adjustment: GposValueAdjustment = .{},
188     target_offset: usize = 0,
189     skip_count: usize = 0,
190 
191     pub fn isZero(self: GposSingleAdjustment) bool {
192         return self.adjustment.isZero();
193     }
194 };
195 
196 pub const GposCursiveAttachment = struct {
197     x_anchor_delta: i32 = 0,
198     y_anchor_delta: i32 = 0,
199 };
200 
201 fn mergeGposVariationIndex(existing: u32, incoming: u32) u32 {
202     if (incoming == noGposVariationIndex) return existing;
203     if (existing == noGposVariationIndex) return incoming;
204     return existing;
205 }
206 
207 test "tag packs OpenType tags" {
208     try std.testing.expectEqual(@as(u32, 0x68656164), tag("head"));
209 }