pdfrum_edit/font/
instance.rs1use skrifa::instance::{Location, NormalizedCoord};
12use skrifa::raw::TableProvider as _;
13use skrifa::{FontRef, MetadataProvider as _, Tag};
14
15#[derive(Debug, Clone, PartialEq, Default)]
24pub enum FontInstance {
25 #[default]
27 Default,
28 Normalized(Vec<i16>),
31 User(Vec<AxisValue>),
33}
34
35#[derive(Debug, Clone, Copy, PartialEq)]
37pub struct AxisValue {
38 pub tag: [u8; 4],
40 pub value: f32,
42}
43
44pub(crate) fn user_values(font: &FontRef<'_>, instance: &FontInstance) -> Vec<AxisValue> {
47 match instance {
48 FontInstance::Default => Vec::new(),
49 FontInstance::User(values) => values.clone(),
50 FontInstance::Normalized(coords) if coords.iter().all(|&c| c == 0) => Vec::new(),
51 FontInstance::Normalized(coords) => {
52 let maps = segment_maps(font);
53 font.axes()
54 .iter()
55 .zip(coords)
56 .enumerate()
57 .map(|(index, (axis, &coord))| {
58 let normalized = f32::from(coord) / 16384.0;
59 let before_avar = maps
60 .get(index)
61 .map_or(normalized, |map| invert(map, normalized));
62 AxisValue {
63 tag: axis.tag().to_be_bytes(),
64 value: denormalize(
65 before_avar,
66 axis.min_value(),
67 axis.default_value(),
68 axis.max_value(),
69 ),
70 }
71 })
72 .collect()
73 }
74 }
75}
76
77pub(crate) fn location(font: &FontRef<'_>, instance: &FontInstance) -> Location {
79 match instance {
80 FontInstance::Default => Location::default(),
81 FontInstance::Normalized(coords) => {
82 let mut location = Location::new(coords.len());
83 location
84 .coords_mut()
85 .iter_mut()
86 .zip(coords)
87 .for_each(|(slot, &coord)| *slot = NormalizedCoord::from_bits(coord));
88 location
89 }
90 FontInstance::User(values) => font
91 .axes()
92 .location(values.iter().map(|axis| (Tag::new(&axis.tag), axis.value))),
93 }
94}
95
96fn segment_maps(font: &FontRef<'_>) -> Vec<Vec<(f32, f32)>> {
98 let Ok(avar) = font.avar() else {
99 return Vec::new();
100 };
101 avar.axis_segment_maps()
102 .iter()
103 .map(|maps| {
104 maps.map(|maps| {
105 maps.axis_value_maps()
106 .iter()
107 .map(|pair| {
108 (
109 pair.from_coordinate().to_f32(),
110 pair.to_coordinate().to_f32(),
111 )
112 })
113 .collect()
114 })
115 .unwrap_or_default()
116 })
117 .collect()
118}
119
120fn invert(map: &[(f32, f32)], to: f32) -> f32 {
123 map.windows(2)
124 .find_map(|pair| match pair {
125 [(from_a, to_a), (from_b, to_b)] if *to_a <= to && to <= *to_b => {
126 Some(if (to_b - to_a).abs() < f32::EPSILON {
127 *from_a
128 } else {
129 from_a + (to - to_a) * (from_b - from_a) / (to_b - to_a)
130 })
131 }
132 _ => None,
133 })
134 .unwrap_or(to)
135}
136
137fn denormalize(value: f32, min: f32, default: f32, max: f32) -> f32 {
140 if value < 0.0 {
141 default + value * (default - min)
142 } else {
143 default + value * (max - default)
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::{denormalize, invert};
150
151 #[test]
152 fn denormalizing_follows_each_side_of_the_default() {
153 for (normalized, want) in [(-1.0, 100.0), (-0.5, 250.0), (0.0, 400.0), (1.0, 900.0)] {
154 assert!(
155 (denormalize(normalized, 100.0, 400.0, 900.0) - want).abs() < 1e-4,
156 "{normalized}"
157 );
158 }
159 }
160
161 #[test]
162 fn avar_is_read_backwards() {
163 let map = [(-1.0, -1.0), (0.0, 0.0), (0.5, 0.8), (1.0, 1.0)];
164 assert!((invert(&map, 0.8) - 0.5).abs() < 1e-6);
165 assert!((invert(&map, 0.4) - 0.25).abs() < 1e-6);
166 assert!((invert(&map, 0.9) - 0.75).abs() < 1e-6);
167 assert!((invert(&[], 0.3) - 0.3).abs() < 1e-6);
168 }
169}