1use super::{fixed::Fixed, raw_tag, Array, Bytes, RawFont, RawTag, U24};
4
5pub const FVAR: RawTag = raw_tag(b"fvar");
6pub const AVAR: RawTag = raw_tag(b"avar");
7pub const HVAR: RawTag = raw_tag(b"HVAR");
8pub const VVAR: RawTag = raw_tag(b"VVAR");
9pub const MVAR: RawTag = raw_tag(b"MVAR");
10
11#[derive(Copy, Clone)]
13pub struct Fvar<'a> {
14 data: Bytes<'a>,
15 axis_offset: u16,
16 axis_count: u16,
17 axis_size: u16,
18 inst_count: u16,
19 inst_size: u16,
20}
21
22impl<'a> Fvar<'a> {
23 pub fn new(data: &'a [u8]) -> Self {
24 let b = Bytes::new(data);
25 let axis_offset = b.read_or_default::<u16>(4);
26 let axis_count = b.read_or_default::<u16>(8);
27 let axis_size = b.read_or_default::<u16>(10);
28 let inst_count = b.read_or_default::<u16>(12);
29 let inst_size = b.read_or_default::<u16>(14);
30 Self {
31 data: b,
32 axis_offset,
33 axis_count,
34 axis_size,
35 inst_count,
36 inst_size,
37 }
38 }
39
40 pub fn from_font(font: impl RawFont<'a>) -> Option<Self> {
41 Some(Self::new(font.table_data(FVAR)?))
42 }
43
44 pub fn axis_count(&self) -> u16 {
45 self.axis_count
46 }
47
48 pub fn get_axis(&self, index: u16) -> Option<VarAxis> {
49 if index >= self.axis_count {
50 return None;
51 }
52 let b = &self.data;
53 let base = self.axis_offset as usize;
54 let offset = base + index as usize * self.axis_size as usize;
55 let tag = b.read::<u32>(offset)?;
56 let min = Fixed(b.read::<i32>(offset + 4)?);
57 let default = Fixed(b.read::<i32>(offset + 8)?);
58 let max = Fixed(b.read::<i32>(offset + 12)?);
59 let flags = b.read::<u16>(offset + 16)?;
60 let name_id = b.read::<u16>(offset + 18)?;
61 Some(VarAxis {
62 index,
63 tag,
64 name_id,
65 flags,
66 min,
67 default,
68 max,
69 })
70 }
71
72 pub fn get_axis_by_tag(&self, tag: RawTag) -> Option<VarAxis> {
73 let b = &self.data;
74 let base = self.axis_offset as usize;
75 let axis_size = self.axis_size as usize;
76 for i in 0..self.axis_count as usize {
77 let tag_offset = base + i * axis_size;
78 if b.read_u32(tag_offset) == Some(tag) {
79 return self.get_axis(i as u16);
80 }
81 }
82 None
83 }
84
85 pub fn instance_count(&self) -> u16 {
86 self.inst_count
87 }
88
89 pub fn get_instance(&self, index: u16) -> Option<VarInstance<'a>> {
90 if index >= self.inst_count {
91 return None;
92 }
93 let b = &self.data;
94 let base = self.axis_offset as usize + (self.axis_count as usize * self.axis_size as usize);
95 let offset = base + index as usize * self.inst_size as usize;
96 let name_id = b.read::<u16>(offset)?;
97 let values = b.read_array::<Fixed>(offset + 4, self.axis_count as usize)?;
98 let ps_name_offset = 4 + self.axis_count as usize * 4;
99 let postscript_name_id = if ps_name_offset == self.inst_size as usize - 2 {
100 b.read::<u16>(ps_name_offset)
101 } else {
102 None
103 };
104 Some(VarInstance {
105 index,
106 name_id,
107 postscript_name_id,
108 values,
109 })
110 }
111}
112
113#[derive(Copy, Clone, Default)]
115pub struct VarAxis {
116 pub index: u16,
117 pub tag: RawTag,
118 pub name_id: u16,
119 pub flags: u16,
120 pub min: Fixed,
121 pub default: Fixed,
122 pub max: Fixed,
123}
124
125impl VarAxis {
126 pub fn is_hidden(&self) -> bool {
128 self.flags & 1 != 0
129 }
130
131 pub fn normalized_coord(&self, mut value: Fixed, avar: Option<(&[u8], u32)>) -> i16 {
134 use core::cmp::Ordering::*;
135 if value < self.min {
136 value = self.min;
137 } else if value > self.max {
138 value = self.max;
139 }
140 value = match value.cmp(&self.default) {
141 Less => -((self.default - value) / (self.default - self.min)),
142 Greater => (value - self.default) / (self.max - self.default),
143 Equal => Fixed(0),
144 };
145 value = value.min(Fixed::ONE).max(-Fixed::ONE);
146 value = avar
147 .and_then(|(data, avar)| adjust_axis(data, avar, self.index, value))
148 .unwrap_or(value);
149 value.to_f2dot14()
150 }
151}
152
153#[derive(Copy, Clone)]
155pub struct VarInstance<'a> {
156 pub index: u16,
157 pub name_id: u16,
158 pub postscript_name_id: Option<u16>,
159 pub values: Array<'a, Fixed>,
160}
161
162pub struct Mvar<'a> {
164 data: Bytes<'a>,
165 coords: &'a [i16],
166 rec_size: usize,
167 rec_count: usize,
168 store: u32,
169}
170
171impl<'a> Mvar<'a> {
172 pub fn new(data: &'a [u8], mvar: u32, coords: &'a [i16]) -> Option<Self> {
173 let b = Bytes::with_offset(data, mvar as usize)?;
174 let rec_size = b.read::<u16>(6)? as usize;
175 let rec_count = b.read::<u16>(8)? as usize;
176 let store = b.read::<u16>(10)? as u32;
177 if rec_count == 0 || store == 0 {
178 return None;
179 }
180 Some(Self {
181 data: b,
182 coords,
183 rec_size,
184 rec_count,
185 store,
186 })
187 }
188
189 pub fn delta(&self, metric: RawTag) -> f32 {
190 self.read_delta(metric).map(|d| d.to_f32()).unwrap_or(0.)
191 }
192
193 #[inline(always)]
194 fn read_delta(&self, metric: RawTag) -> Option<Fixed> {
195 let base = 12;
196 let b = &self.data;
197 let rec_size = self.rec_size;
198 let mut l = 0;
199 let mut h = self.rec_count;
200 while l < h {
201 use core::cmp::Ordering::*;
202 let i = (l + h) / 2;
203 let offset = base + i * rec_size;
204 let t = b.read::<u32>(offset)?;
205 match metric.cmp(&t) {
206 Less => h = i,
207 Greater => l = i + 1,
208 Equal => {
209 let inner = b.read::<u16>(offset + 4)?;
210 let outer = b.read::<u16>(offset + 6)?;
211 return item_delta(b.data(), self.store, outer, inner, self.coords);
212 }
213 }
214 }
215 None
216 }
217}
218
219pub fn advance_delta(data: &[u8], xvar: u32, glyph_id: u16, coords: &[i16]) -> f32 {
221 metric_delta(data, xvar, 8, glyph_id, coords)
222 .map(|d| d.to_f32())
223 .unwrap_or(0.)
224}
225
226pub fn sb_delta(data: &[u8], xvar: u32, glyph_id: u16, coords: &[i16]) -> f32 {
228 metric_delta(data, xvar, 12, glyph_id, coords)
229 .map(|d| d.to_f32())
230 .unwrap_or(0.)
231}
232
233pub fn adjust_axis(data: &[u8], avar: u32, axis: u16, coord: Fixed) -> Option<Fixed> {
236 use skrifa::raw::{tables::avar::Avar, types::Fixed, FontData, FontRead};
237
238 if avar == 0 {
239 return None;
240 }
241 let avar = Avar::read(FontData::new(data.get(avar as usize..)?)).ok()?;
242 let mapping = avar
243 .axis_segment_maps()
244 .get(axis as usize)
245 .transpose()
246 .ok()??;
247 Some(Fixed(mapping.apply(Fixed::from_bits(coord.0)).to_bits()))
248}
249
250pub fn item_delta(
252 data: &[u8],
253 offset: u32,
254 outer: u16,
255 inner: u16,
256 coords: &[i16],
257) -> Option<Fixed> {
258 if offset == 0 {
259 return None;
260 }
261 let b = Bytes::new(data);
262 let store = offset as usize;
263 if outer >= b.read::<u16>(store + 6)? {
264 return None;
265 }
266 let region_base = store + b.read::<u32>(store + 2)? as usize;
267 let axis_count = b.read::<u16>(region_base)? as usize;
268 let region_record_size = axis_count * 6;
269 let region_count = b.read::<u16>(region_base + 2)? as usize;
270 let data_base = store + b.read::<u32>(store + 8 + outer as usize * 4)? as usize;
271 let region_index_base = data_base + 6;
272 let region_index_count = b.read::<u16>(data_base + 4)? as usize;
273 let (short_count, mut delta_base) = {
274 let inner = inner as usize;
275 let short_count = b.read::<u16>(data_base + 2)? as usize;
276 let count = region_index_count;
277 let base = data_base + 6 + count * 2;
278 let elem_len = (count - short_count) + short_count * 2;
279 let offset = base + inner * elem_len;
280 (short_count, offset)
281 };
282 const ZERO: Fixed = Fixed::ZERO;
283 const ONE: Fixed = Fixed::ONE;
284 let mut idx = 0;
285 let mut delta = ZERO;
286 for i in 0..region_index_count {
287 let region_index = b.read::<u16>(region_index_base + i * 2)? as usize;
288 if region_index >= region_count {
289 return None;
290 }
291 let region_offset = region_base + 4 + region_index * region_record_size;
292 let mut scalar = ONE;
293 for axis in 0..axis_count {
294 let region_axis_base = region_offset + axis * 6;
295 let start = Fixed::from_f2dot14(b.read::<i16>(region_axis_base)?);
296 let peak = Fixed::from_f2dot14(b.read::<i16>(region_axis_base + 2)?);
297 let end = Fixed::from_f2dot14(b.read::<i16>(region_axis_base + 4)?);
298 let coord = coords
299 .get(axis)
300 .map(|c| Fixed::from_f2dot14(*c))
301 .unwrap_or(ZERO);
302 if start > peak || peak > end || peak == ZERO || start < ZERO && end > ZERO {
303 continue;
304 } else if coord < start || coord > end {
305 scalar = ZERO;
306 break;
307 } else if coord == peak {
308 continue;
309 } else if coord < peak {
310 scalar = scalar * (coord - start) / (peak - start);
311 } else {
312 scalar = scalar * (end - coord) / (end - peak);
313 };
314 }
315 let val = if idx >= short_count {
316 delta_base += 1;
317 b.read::<i8>(delta_base - 1)? as i16
318 } else {
319 delta_base += 2;
320 b.read::<i16>(delta_base - 2)?
321 };
322 idx += 1;
323 delta += scalar * Fixed::from_i32(val as i32);
324 }
325 Some(delta)
326}
327
328#[inline(always)]
329fn metric_delta(
330 data: &[u8],
331 base: u32,
332 which: usize,
333 glyph_id: u16,
334 coords: &[i16],
335) -> Option<Fixed> {
336 if base == 0 {
337 return None;
338 }
339 let b = Bytes::new(data);
340 let mut store = b.read::<u32>(base as usize + 4)?;
341 if store == 0 {
342 return None;
343 }
344 store += base;
345 let mut offset = b.read::<u32>(base as usize + which)? as usize;
346 if offset == 0 {
347 if which == 8 {
348 return item_delta(data, store, 0, glyph_id, coords);
349 } else {
350 return None;
351 }
352 }
353 offset += base as usize;
354 let format = b.read::<u16>(offset)? as u32;
355 let count = b.read::<u16>(offset + 2)?;
356 let bit_count = (format & 0xF) + 1;
357 let entry_size = ((format & 0x30) >> 4) + 1;
358 let base = offset + 4;
359 let index = if glyph_id >= count {
360 count - 1
361 } else {
362 glyph_id
363 } as usize;
364 let entry = match entry_size {
365 1 => b.read::<u8>(base + index)? as u32,
366 2 => b.read::<u16>(base + index * 2)? as u32,
367 3 => b.read::<U24>(base + index * 3)?.0,
368 4 => b.read::<u32>(base + index * 4)?,
369 _ => return None,
370 };
371 let outer = entry >> bit_count;
372 let inner = entry & ((1 << bit_count) - 1);
373 item_delta(data, store, outer as u16, inner as u16, coords)
374}
375
376pub mod mvar_tags {
378 use super::{raw_tag, RawTag};
379
380 pub const HASC: RawTag = raw_tag(b"hasc");
382 pub const HDSC: RawTag = raw_tag(b"hdsc");
384 pub const HLGP: RawTag = raw_tag(b"hlgp");
386
387 pub const HCRS: RawTag = raw_tag(b"hcrs");
389 pub const HCRN: RawTag = raw_tag(b"hcrn");
391 pub const HCOF: RawTag = raw_tag(b"hcof");
393
394 pub const HCLA: RawTag = raw_tag(b"hcla");
396 pub const HCLD: RawTag = raw_tag(b"hcld");
398
399 pub const VASC: RawTag = raw_tag(b"vasc");
401 pub const VDSC: RawTag = raw_tag(b"vdsc");
403 pub const VLGP: RawTag = raw_tag(b"vlgp");
405
406 pub const VCRS: RawTag = raw_tag(b"vcrs");
408 pub const VCRN: RawTag = raw_tag(b"vcrn");
410 pub const VCOF: RawTag = raw_tag(b"vcof");
412
413 pub const XHGT: RawTag = raw_tag(b"xhgt");
415 pub const CPHT: RawTag = raw_tag(b"cpht");
417
418 pub const UNDO: RawTag = raw_tag(b"undo");
420 pub const UNDS: RawTag = raw_tag(b"unds");
422
423 pub const STRO: RawTag = raw_tag(b"stro");
425 pub const STRS: RawTag = raw_tag(b"strs");
427
428 pub const SBXO: RawTag = raw_tag(b"sbxo");
430 pub const SBYO: RawTag = raw_tag(b"sbyo");
432 pub const SBXS: RawTag = raw_tag(b"sbxs");
434 pub const SBYS: RawTag = raw_tag(b"sbys");
436
437 pub const SPXO: RawTag = raw_tag(b"spxo");
439 pub const SPYO: RawTag = raw_tag(b"spyo");
441 pub const SPXS: RawTag = raw_tag(b"spxs");
443 pub const SPYS: RawTag = raw_tag(b"spys");
445}