1use read_fonts::{
4 tables::postscript::{charstring::CommandSink, dict::Blues},
5 types::Fixed,
6};
7
8const ICF_TOP: Fixed = Fixed::from_i32(880);
11const ICF_BOTTOM: Fixed = Fixed::from_i32(-120);
12
13const MAX_BLUES: usize = 7;
15const MAX_OTHER_BLUES: usize = 5;
16const MAX_BLUE_ZONES: usize = MAX_BLUES + MAX_OTHER_BLUES;
17
18const MAX_HINTS: usize = 96;
20
21const HINT_MASK_SIZE: usize = MAX_HINTS.div_ceil(8);
24
25const MIN_COUNTER: Fixed = Fixed::from_bits(0x8000);
28
29const EPSILON: Fixed = Fixed::from_bits(1);
31
32#[derive(Clone)]
35pub(crate) struct HintParams {
36 pub blues: Blues,
37 pub family_blues: Blues,
38 pub other_blues: Blues,
39 pub family_other_blues: Blues,
40 pub blue_scale: Fixed,
41 pub blue_shift: Fixed,
42 pub blue_fuzz: Fixed,
43 pub language_group: i32,
44}
45
46impl Default for HintParams {
47 fn default() -> Self {
48 Self {
49 blues: Blues::default(),
50 other_blues: Blues::default(),
51 family_blues: Blues::default(),
52 family_other_blues: Blues::default(),
53 blue_scale: Fixed::from_f64(0.039625),
55 blue_shift: Fixed::from_i32(7),
56 blue_fuzz: Fixed::ONE,
57 language_group: 0,
58 }
59 }
60}
61
62#[derive(Copy, Clone, PartialEq, Default, Debug)]
64struct BlueZone {
65 is_bottom: bool,
66 cs_bottom_edge: Fixed,
67 cs_top_edge: Fixed,
68 cs_flat_edge: Fixed,
69 ds_flat_edge: Fixed,
70}
71
72#[derive(Copy, Clone, PartialEq, Default)]
78pub(crate) struct HintState {
79 scale: Fixed,
80 blue_scale: Fixed,
81 blue_shift: Fixed,
82 blue_fuzz: Fixed,
83 language_group: i32,
84 suppress_overshoot: bool,
85 do_em_box_hints: bool,
86 boost: Fixed,
87 darken_y: Fixed,
88 zones: [BlueZone; MAX_BLUE_ZONES],
89 zone_count: usize,
90}
91
92impl HintState {
93 pub fn new(params: &HintParams, scale: Fixed) -> Self {
94 let mut state = Self {
95 scale,
96 blue_scale: params.blue_scale,
97 blue_shift: params.blue_shift,
98 blue_fuzz: params.blue_fuzz,
99 language_group: params.language_group,
100 suppress_overshoot: false,
101 do_em_box_hints: false,
102 boost: Fixed::ZERO,
103 darken_y: Fixed::ZERO,
104 zones: [BlueZone::default(); MAX_BLUE_ZONES],
105 zone_count: 0,
106 };
107 state.build_zones(params);
108 state
109 }
110
111 fn zones(&self) -> &[BlueZone] {
112 &self.zones[..self.zone_count]
113 }
114
115 fn build_zones(&mut self, params: &HintParams) {
119 self.do_em_box_hints = false;
120 match (self.language_group, params.blues.values().len()) {
122 (1, 2) => {
123 let blues = params.blues.values();
124 if blues[0].0 < ICF_BOTTOM
125 && blues[0].1 < ICF_BOTTOM
126 && blues[1].0 > ICF_TOP
127 && blues[1].1 > ICF_TOP
128 {
129 self.do_em_box_hints = true;
132 return;
133 }
134 }
135 (1, 0) => {
136 self.do_em_box_hints = true;
137 return;
138 }
139 _ => {}
140 }
141 let mut zones = [BlueZone::default(); MAX_BLUE_ZONES];
142 let mut max_zone_height = Fixed::ZERO;
143 let mut zone_ix = 0usize;
144 for blue in params.blues.values().iter().take(MAX_BLUES) {
146 let (bottom, top) = *blue;
147 let zone_height = top - bottom;
148 if zone_height < Fixed::ZERO {
149 continue;
151 }
152 max_zone_height = max_zone_height.max(zone_height);
153 let zone = &mut zones[zone_ix];
154 zone.cs_bottom_edge = bottom;
155 zone.cs_top_edge = top;
156 if zone_ix == 0 {
157 zone.is_bottom = true;
159 zone.cs_flat_edge = top;
160 } else {
161 zone.is_bottom = false;
163 zone.cs_top_edge += twice(self.darken_y);
165 zone.cs_bottom_edge += twice(self.darken_y);
166 zone.cs_flat_edge = zone.cs_bottom_edge;
167 }
168 zone_ix += 1;
169 }
170 for blue in params.other_blues.values().iter().take(MAX_OTHER_BLUES) {
171 let (bottom, top) = *blue;
172 let zone_height = top - bottom;
173 if zone_height < Fixed::ZERO {
174 continue;
176 }
177 max_zone_height = max_zone_height.max(zone_height);
178 let zone = &mut zones[zone_ix];
179 zone.is_bottom = true;
181 zone.cs_bottom_edge = bottom;
182 zone.cs_top_edge = top;
183 zone.cs_flat_edge = top;
184 zone_ix += 1;
185 }
186 let units_per_pixel = Fixed::ONE / self.scale;
188 for zone in &mut zones[..zone_ix] {
189 let flat = zone.cs_flat_edge;
190 let mut min_diff = Fixed::MAX;
191 if zone.is_bottom {
192 for blue in params.family_other_blues.values() {
196 let family_flat = blue.1;
197 let diff = (flat - family_flat).abs();
198 if diff < min_diff && diff < units_per_pixel {
199 zone.cs_flat_edge = family_flat;
200 min_diff = diff;
201 if diff == Fixed::ZERO {
202 break;
203 }
204 }
205 }
206 if !params.family_blues.values().is_empty() {
209 let family_flat = params.family_blues.values()[0].1;
210 let diff = (flat - family_flat).abs();
211 if diff < min_diff && diff < units_per_pixel {
212 zone.cs_flat_edge = family_flat;
213 }
214 }
215 } else {
216 for blue in params.family_blues.values().iter().skip(1) {
221 let family_flat = blue.0 + twice(self.darken_y);
222 let diff = (flat - family_flat).abs();
223 if diff < min_diff && diff < units_per_pixel {
224 zone.cs_flat_edge = family_flat;
225 min_diff = diff;
226 if diff == Fixed::ZERO {
227 break;
228 }
229 }
230 }
231 }
232 }
233 if max_zone_height > Fixed::ZERO && self.blue_scale > (Fixed::ONE / max_zone_height) {
234 self.blue_scale = Fixed::ONE / max_zone_height;
236 }
237 if self.scale < self.blue_scale {
239 self.suppress_overshoot = true;
240 self.boost =
241 Fixed::from_f64(0.6) - Fixed::from_f64(0.6).mul_div(self.scale, self.blue_scale);
242 self.boost = self.boost.min(Fixed::from_bits(0x7FFF));
244 }
245 if self.darken_y != Fixed::ZERO {
246 self.boost = Fixed::ZERO;
247 }
248 let scale = self.scale;
251 let boost = self.boost;
252 for zone in &mut zones[..zone_ix] {
253 let boost = if zone.is_bottom { -boost } else { boost };
254 zone.ds_flat_edge = (zone.cs_flat_edge * scale + boost).round();
255 }
256 self.zones = zones;
257 self.zone_count = zone_ix;
258 }
259
260 fn capture(&self, bottom_edge: &mut Hint, top_edge: &mut Hint) -> bool {
264 let fuzz = self.blue_fuzz;
268 let mut captured = false;
269 let mut adjustment = Fixed::ZERO;
270 for zone in self.zones() {
271 if zone.is_bottom
272 && bottom_edge.is_bottom()
273 && zone.cs_bottom_edge.wrapping_sub(fuzz) <= bottom_edge.cs_coord
274 && bottom_edge.cs_coord <= zone.cs_top_edge.wrapping_add(fuzz)
275 {
276 adjustment = if self.suppress_overshoot {
278 zone.ds_flat_edge
279 } else if zone.cs_top_edge.wrapping_sub(bottom_edge.cs_coord) >= self.blue_shift {
280 bottom_edge
282 .ds_coord
283 .round()
284 .min(zone.ds_flat_edge - Fixed::ONE)
285 } else {
286 bottom_edge.ds_coord.round()
287 };
288 adjustment -= bottom_edge.ds_coord;
289 captured = true;
290 break;
291 }
292 if !zone.is_bottom
293 && top_edge.is_top()
294 && zone.cs_bottom_edge.wrapping_sub(fuzz) <= top_edge.cs_coord
295 && top_edge.cs_coord <= zone.cs_top_edge.wrapping_add(fuzz)
296 {
297 adjustment = if self.suppress_overshoot {
299 zone.ds_flat_edge
300 } else if top_edge.cs_coord.wrapping_sub(zone.cs_bottom_edge) >= self.blue_shift {
301 top_edge
303 .ds_coord
304 .round()
305 .max(zone.ds_flat_edge + Fixed::ONE)
306 } else {
307 top_edge.ds_coord.round()
308 };
309 adjustment -= top_edge.ds_coord;
310 captured = true;
311 break;
312 }
313 }
314 if captured {
315 if bottom_edge.is_valid() {
317 bottom_edge.ds_coord += adjustment;
318 bottom_edge.lock();
319 }
320 if top_edge.is_valid() {
321 top_edge.ds_coord += adjustment;
322 top_edge.lock();
323 }
324 }
325 captured
326 }
327}
328
329#[derive(Copy, Clone, Default)]
331struct StemHint {
332 is_used: bool,
334 min: Fixed,
336 max: Fixed,
337 ds_min: Fixed,
339 ds_max: Fixed,
340}
341
342const GHOST_BOTTOM: u8 = 0x1;
344const GHOST_TOP: u8 = 0x2;
345const PAIR_BOTTOM: u8 = 0x4;
346const PAIR_TOP: u8 = 0x8;
347const LOCKED: u8 = 0x10;
348const SYNTHETIC: u8 = 0x20;
349
350#[derive(Copy, Clone, PartialEq, Default, Debug)]
352struct Hint {
353 flags: u8,
354 index: u8,
356 cs_coord: Fixed,
357 ds_coord: Fixed,
358 scale: Fixed,
359}
360
361impl Hint {
362 fn is_valid(&self) -> bool {
363 self.flags != 0
364 }
365
366 fn is_bottom(&self) -> bool {
367 self.flags & (GHOST_BOTTOM | PAIR_BOTTOM) != 0
368 }
369
370 fn is_top(&self) -> bool {
371 self.flags & (GHOST_TOP | PAIR_TOP) != 0
372 }
373
374 fn is_pair(&self) -> bool {
375 self.flags & (PAIR_BOTTOM | PAIR_TOP) != 0
376 }
377
378 fn is_pair_top(&self) -> bool {
379 self.flags & PAIR_TOP != 0
380 }
381
382 fn is_locked(&self) -> bool {
383 self.flags & LOCKED != 0
384 }
385
386 fn is_synthetic(&self) -> bool {
387 self.flags & SYNTHETIC != 0
388 }
389
390 fn lock(&mut self) {
391 self.flags |= LOCKED
392 }
393
394 fn setup(
398 &mut self,
399 stem: &StemHint,
400 index: u8,
401 origin: Fixed,
402 scale: Fixed,
403 darken_y: Fixed,
404 is_bottom: bool,
405 ) {
406 const GHOST_BOTTOM_WIDTH: Fixed = Fixed::from_i32(-21);
412 const GHOST_TOP_WIDTH: Fixed = Fixed::from_i32(-20);
413 let width = stem.max - stem.min;
414 if width == GHOST_BOTTOM_WIDTH {
415 if is_bottom {
416 self.cs_coord = stem.max;
417 self.flags = GHOST_BOTTOM;
418 } else {
419 self.flags = 0;
420 }
421 } else if width == GHOST_TOP_WIDTH {
422 if !is_bottom {
423 self.cs_coord = stem.min;
424 self.flags = GHOST_TOP;
425 } else {
426 self.flags = 0;
427 }
428 } else if width < Fixed::ZERO {
429 if is_bottom {
432 self.cs_coord = stem.max;
433 self.flags = PAIR_BOTTOM;
434 } else {
435 self.cs_coord = stem.min;
436 self.flags = PAIR_TOP;
437 }
438 } else {
439 if is_bottom {
441 self.cs_coord = stem.min;
442 self.flags = PAIR_BOTTOM;
443 } else {
444 self.cs_coord = stem.max;
445 self.flags = PAIR_TOP;
446 }
447 }
448 if self.is_top() {
449 self.cs_coord += twice(darken_y);
452 }
453 self.cs_coord += origin;
454 self.scale = scale;
455 self.index = index;
456 if self.flags != 0 && stem.is_used {
458 if self.is_top() {
459 self.ds_coord = stem.ds_max;
460 } else {
461 self.ds_coord = stem.ds_min;
462 }
463 self.lock();
464 } else {
465 self.ds_coord = self.cs_coord * scale;
466 }
467 }
468}
469
470#[derive(Copy, Clone)]
474struct HintMap {
475 edges: [Hint; MAX_HINTS],
476 len: usize,
477 is_valid: bool,
478 scale: Fixed,
479}
480
481impl HintMap {
482 fn new(scale: Fixed) -> Self {
483 Self {
484 edges: [Hint::default(); MAX_HINTS],
485 len: 0,
486 is_valid: false,
487 scale,
488 }
489 }
490
491 fn clear(&mut self) {
492 self.len = 0;
493 self.is_valid = false;
494 }
495
496 fn transform(&self, coord: Fixed) -> Fixed {
500 if self.len == 0 {
501 return coord * self.scale;
502 }
503 let limit = self.len - 1;
504 let mut i = 0;
505 while i < limit && coord >= self.edges[i + 1].cs_coord {
506 i += 1;
507 }
508 while i > 0 && coord < self.edges[i].cs_coord {
509 i -= 1;
510 }
511 let first_edge = &self.edges[0];
512 if i == 0 && coord < first_edge.cs_coord {
513 ((coord - first_edge.cs_coord) * self.scale) + first_edge.ds_coord
515 } else {
516 let edge = &self.edges[i];
518 ((coord - edge.cs_coord) * edge.scale) + edge.ds_coord
519 }
520 }
521
522 fn insert(&mut self, bottom: &Hint, top: &Hint, initial: Option<&HintMap>) {
526 let (is_pair, mut first_edge) = if !bottom.is_valid() {
527 (false, *top)
529 } else if !top.is_valid() {
530 (false, *bottom)
532 } else {
533 (true, *bottom)
535 };
536 let mut second_edge = *top;
537 if is_pair && top.cs_coord < bottom.cs_coord {
538 return;
540 }
541 let edge_count = if is_pair { 2 } else { 1 };
542 if self.len + edge_count > MAX_HINTS {
543 return;
545 }
546 let mut insert_ix = 0;
548 while insert_ix < self.len {
549 if self.edges[insert_ix].cs_coord >= first_edge.cs_coord {
550 break;
551 }
552 insert_ix += 1;
553 }
554 if insert_ix < self.len {
556 let current = &self.edges[insert_ix];
557 if (current.cs_coord == first_edge.cs_coord)
559 || (is_pair && current.cs_coord <= second_edge.cs_coord)
561 || current.is_pair_top()
563 {
564 return;
565 }
566 }
567 if !first_edge.is_locked() {
569 if let Some(initial) = initial {
570 if is_pair {
571 let mid =
575 initial.transform(midpoint(first_edge.cs_coord, second_edge.cs_coord));
576 let half_width = half(second_edge.cs_coord - first_edge.cs_coord) * self.scale;
577 first_edge.ds_coord = mid - half_width;
578 second_edge.ds_coord = mid + half_width;
579 } else {
580 first_edge.ds_coord = initial.transform(first_edge.cs_coord);
581 }
582 }
583 }
584 if insert_ix > 0 && first_edge.ds_coord < self.edges[insert_ix - 1].ds_coord {
586 return;
588 }
589 if insert_ix < self.len
590 && ((is_pair && second_edge.ds_coord > self.edges[insert_ix].ds_coord)
591 || first_edge.ds_coord > self.edges[insert_ix].ds_coord)
592 {
593 return;
595 }
596 if insert_ix != self.len {
598 let mut src_index = self.len - 1;
599 let mut dst_index = self.len + edge_count - 1;
600 loop {
601 self.edges[dst_index] = self.edges[src_index];
602 if src_index == insert_ix {
603 break;
604 }
605 src_index -= 1;
606 dst_index -= 1;
607 }
608 }
609 self.edges[insert_ix] = first_edge;
610 if is_pair {
611 self.edges[insert_ix + 1] = second_edge;
612 }
613 self.len += edge_count;
614 }
615
616 fn adjust(&mut self) {
620 let mut saved = [(0usize, Fixed::ZERO); MAX_HINTS];
621 let mut saved_count = 0usize;
622 let mut i = 0;
623 let limit = self.len;
630 while i < limit {
631 let is_pair = self.edges[i].is_pair();
632 let j = if is_pair { i + 1 } else { i };
633 if !self.edges[i].is_locked() {
634 let frac_down = self.edges[i].ds_coord.fract();
636 let frac_up = self.edges[j].ds_coord.fract();
637 let down_move_down = Fixed::ZERO - frac_down;
640 let up_move_down = Fixed::ZERO - frac_up;
641 let down_move_up = if frac_down == Fixed::ZERO {
642 Fixed::ZERO
643 } else {
644 Fixed::ONE - frac_down
645 };
646 let up_move_up = if frac_up == Fixed::ZERO {
647 Fixed::ZERO
648 } else {
649 Fixed::ONE - frac_up
650 };
651 let move_up = down_move_up.min(up_move_up);
653 let move_down = down_move_down.max(up_move_down);
655 let mut save_edge = false;
656 let adjustment;
657 if j >= self.len - 1
661 || self.edges[j + 1].ds_coord
662 >= (self.edges[j].ds_coord + move_up + MIN_COUNTER)
663 {
664 if i == 0
666 || self.edges[i - 1].ds_coord
667 <= (self.edges[i].ds_coord + move_down - MIN_COUNTER)
668 {
669 adjustment = if -move_down < move_up {
671 move_down
672 } else {
673 move_up
674 };
675 } else {
676 adjustment = move_up;
677 }
678 } else if i == 0
679 || self.edges[i - 1].ds_coord
680 <= (self.edges[i].ds_coord + move_down - MIN_COUNTER)
681 {
682 adjustment = move_down;
684 save_edge = move_up < -move_down;
686 } else {
687 adjustment = Fixed::ZERO;
689 save_edge = true;
690 }
691 if save_edge && j < self.len - 1 && !self.edges[j + 1].is_locked() {
695 saved[saved_count] = (j, move_up - adjustment);
697 saved_count += 1;
698 }
699 self.edges[i].ds_coord += adjustment;
701 if is_pair {
702 self.edges[j].ds_coord += adjustment;
703 }
704 }
705 if i > 0 && self.edges[i].cs_coord != self.edges[i - 1].cs_coord {
707 let a = self.edges[i];
708 let b = self.edges[i - 1];
709 self.edges[i - 1].scale = (a.ds_coord - b.ds_coord) / (a.cs_coord - b.cs_coord);
710 }
711 if is_pair {
712 if self.edges[j].cs_coord != self.edges[j - 1].cs_coord {
713 let a = self.edges[j];
714 let b = self.edges[j - 1];
715 self.edges[j - 1].scale = (a.ds_coord - b.ds_coord) / (a.cs_coord - b.cs_coord);
716 }
717 i += 1;
718 }
719 i += 1;
720 }
721 for (j, adjustment) in saved[..saved_count].iter().copied().rev() {
724 if self.edges[j + 1].ds_coord >= (self.edges[j].ds_coord + adjustment + MIN_COUNTER) {
725 self.edges[j].ds_coord += adjustment;
726 if self.edges[j].is_pair() {
727 self.edges[j - 1].ds_coord += adjustment;
728 }
729 }
730 }
731 }
732
733 fn build(
740 &mut self,
741 state: &HintState,
742 mask: Option<HintMask>,
743 mut initial_map: Option<&mut HintMap>,
744 stems: &mut [StemHint],
745 origin: Fixed,
746 is_initial: bool,
747 ) {
748 let scale = state.scale;
749 let darken_y = Fixed::ZERO;
750 if !is_initial {
751 if let Some(initial_map) = &mut initial_map {
752 if !initial_map.is_valid {
753 initial_map.build(state, Some(HintMask::all()), None, stems, origin, true);
756 }
757 }
758 }
759 let initial_map = initial_map.map(|x| x as &HintMap);
760 self.clear();
761 let mut mask = mask.unwrap_or_else(HintMask::all);
763 if !mask.is_valid {
764 mask = HintMask::all();
765 }
766 if state.do_em_box_hints {
767 let mut bottom = Hint::default();
772 bottom.cs_coord = ICF_BOTTOM - EPSILON;
773 bottom.ds_coord = (bottom.cs_coord * scale).round() - MIN_COUNTER;
774 bottom.scale = scale;
775 bottom.flags = GHOST_BOTTOM | LOCKED | SYNTHETIC;
776 let mut top = Hint::default();
777 top.cs_coord = ICF_TOP + EPSILON + twice(state.darken_y);
778 top.ds_coord = (top.cs_coord * scale).round() + MIN_COUNTER;
779 top.scale = scale;
780 top.flags = GHOST_TOP | LOCKED | SYNTHETIC;
781 let invalid = Hint::default();
782 self.insert(&bottom, &invalid, initial_map);
783 self.insert(&invalid, &top, initial_map);
784 }
785 let mut tmp_mask = mask;
786 for (i, stem) in stems.iter().enumerate() {
790 if !tmp_mask.get(i) {
791 continue;
792 }
793 let hint_ix = i as u8;
794 let mut bottom = Hint::default();
795 let mut top = Hint::default();
796 bottom.setup(stem, hint_ix, origin, scale, darken_y, true);
797 top.setup(stem, hint_ix, origin, scale, darken_y, false);
798 if bottom.is_locked() || top.is_locked() || state.capture(&mut bottom, &mut top) {
800 if is_initial {
801 self.insert(&bottom, &top, None);
802 } else {
803 self.insert(&bottom, &top, initial_map);
804 }
805 tmp_mask.clear(i);
807 }
808 }
809 if is_initial {
810 if self.len == 0
814 || self.edges[0].cs_coord > Fixed::ZERO
815 || self.edges[self.len - 1].cs_coord < Fixed::ZERO
816 {
817 let edge = Hint {
818 flags: GHOST_BOTTOM | LOCKED | SYNTHETIC,
819 scale,
820 ..Default::default()
821 };
822 let invalid = Hint::default();
823 self.insert(&edge, &invalid, None);
824 }
825 } else {
826 for (i, stem) in stems.iter().enumerate() {
828 if !tmp_mask.get(i) {
829 continue;
830 }
831 let hint_ix = i as u8;
832 let mut bottom = Hint::default();
833 let mut top = Hint::default();
834 bottom.setup(stem, hint_ix, origin, scale, darken_y, true);
835 top.setup(stem, hint_ix, origin, scale, darken_y, false);
836 self.insert(&bottom, &top, initial_map);
837 }
838 }
839 self.adjust();
841 if !is_initial {
842 for edge in &self.edges[..self.len] {
844 if edge.is_synthetic() {
845 continue;
846 }
847 let stem = &mut stems[edge.index as usize];
848 if edge.is_top() {
849 stem.ds_max = edge.ds_coord;
850 } else {
851 stem.ds_min = edge.ds_coord;
852 }
853 stem.is_used = true;
854 }
855 }
856 self.is_valid = true;
857 }
858}
859
860#[derive(Copy, Clone, PartialEq, Default)]
870struct HintMask {
871 mask: [u8; HINT_MASK_SIZE],
872 is_valid: bool,
873}
874
875impl HintMask {
876 fn new(bytes: &[u8]) -> Option<Self> {
877 let len = bytes.len();
878 if len > HINT_MASK_SIZE {
879 return None;
880 }
881 let mut mask = Self::default();
882 mask.mask[..len].copy_from_slice(&bytes[..len]);
883 mask.is_valid = true;
884 Some(mask)
885 }
886
887 fn all() -> Self {
888 Self {
889 mask: [0xFF; HINT_MASK_SIZE],
890 is_valid: true,
891 }
892 }
893
894 fn clear(&mut self, bit: usize) {
895 self.mask[bit >> 3] &= !msb_mask(bit);
896 }
897
898 fn get(&self, bit: usize) -> bool {
899 self.mask[bit >> 3] & msb_mask(bit) != 0
900 }
901}
902
903fn msb_mask(bit: usize) -> u8 {
906 1 << (7 - (bit & 0x7))
907}
908
909pub(super) struct HintingSink<'a, S> {
910 state: &'a HintState,
911 sink: &'a mut S,
912 stem_hints: [StemHint; MAX_HINTS],
913 stem_count: u8,
914 mask: HintMask,
915 initial_map: HintMap,
916 map: HintMap,
917 start_point: Option<[Fixed; 2]>,
919 pending_line: Option<[Fixed; 4]>,
922}
923
924impl<'a, S: CommandSink> HintingSink<'a, S> {
925 pub fn new(state: &'a HintState, sink: &'a mut S) -> Self {
926 let scale = state.scale;
927 Self {
928 state,
929 sink,
930 stem_hints: [StemHint::default(); MAX_HINTS],
931 stem_count: 0,
932 mask: HintMask::all(),
933 initial_map: HintMap::new(scale),
934 map: HintMap::new(scale),
935 start_point: None,
936 pending_line: None,
937 }
938 }
939
940 pub fn finish(&mut self) {
941 self.maybe_close_subpath();
942 }
943
944 fn maybe_close_subpath(&mut self) {
945 match (self.start_point.take(), self.pending_line.take()) {
960 (Some(start), Some([cs_x, cs_y, ds_x, ds_y])) => {
961 if start != [cs_x, cs_y] {
962 self.sink.line_to(ds_x, ds_y);
963 }
964 self.sink.close();
965 }
966 (Some(_), _) => self.sink.close(),
967 _ => {}
968 }
969 }
970
971 fn flush_pending_line(&mut self) {
972 if let Some([_, _, x, y]) = self.pending_line.take() {
973 self.sink.line_to(x, y);
974 }
975 }
976
977 fn hint(&mut self, coord: Fixed) -> Fixed {
978 if !self.map.is_valid {
979 self.build_hint_map(Some(self.mask), Fixed::ZERO);
980 }
981 trunc(self.map.transform(coord))
982 }
983
984 fn scale(&self, coord: Fixed) -> Fixed {
985 trunc(coord * self.state.scale)
986 }
987
988 fn add_stem(&mut self, min: Fixed, max: Fixed) {
989 let index = self.stem_count as usize;
990 if index >= MAX_HINTS || self.map.is_valid {
991 return;
992 }
993 let stem = &mut self.stem_hints[index];
994 stem.min = min;
995 stem.max = max;
996 stem.is_used = false;
997 stem.ds_min = Fixed::ZERO;
998 stem.ds_max = Fixed::ZERO;
999 self.stem_count = index as u8 + 1;
1000 }
1001
1002 fn build_hint_map(&mut self, mask: Option<HintMask>, origin: Fixed) {
1003 self.map.build(
1004 self.state,
1005 mask,
1006 Some(&mut self.initial_map),
1007 &mut self.stem_hints[..self.stem_count as usize],
1008 origin,
1009 false,
1010 );
1011 }
1012}
1013
1014impl<S: CommandSink> CommandSink for HintingSink<'_, S> {
1015 fn hstem(&mut self, min: Fixed, max: Fixed) {
1016 self.add_stem(min, max);
1017 }
1018
1019 fn hint_mask(&mut self, mask: &[u8]) {
1020 let mask = HintMask::new(mask).unwrap_or_else(HintMask::all);
1023 if mask != self.mask {
1024 self.mask = mask;
1025 self.map.is_valid = false;
1026 }
1027 }
1028
1029 fn counter_mask(&mut self, mask: &[u8]) {
1030 let mask = HintMask::new(mask).unwrap_or_else(HintMask::all);
1036 let mut map = HintMap::new(self.state.scale);
1037 map.build(
1038 self.state,
1039 Some(mask),
1040 Some(&mut self.initial_map),
1041 &mut self.stem_hints[..self.stem_count as usize],
1042 Fixed::ZERO,
1043 false,
1044 );
1045 }
1046
1047 fn move_to(&mut self, x: Fixed, y: Fixed) {
1048 self.maybe_close_subpath();
1049 self.start_point = Some([x, y]);
1050 let x = self.scale(x);
1051 let y = self.hint(y);
1052 self.sink.move_to(x, y);
1053 }
1054
1055 fn line_to(&mut self, x: Fixed, y: Fixed) {
1056 self.flush_pending_line();
1057 let ds_x = self.scale(x);
1058 let ds_y = self.hint(y);
1059 self.pending_line = Some([x, y, ds_x, ds_y]);
1060 }
1061
1062 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
1063 self.flush_pending_line();
1064 let cx1 = self.scale(cx1);
1065 let cy1 = self.hint(cy1);
1066 let cx2 = self.scale(cx2);
1067 let cy2 = self.hint(cy2);
1068 let x = self.scale(x);
1069 let y = self.hint(y);
1070 self.sink.curve_to(cx1, cy1, cx2, cy2, x, y);
1071 }
1072
1073 fn close(&mut self) {
1074 }
1077}
1078
1079fn trunc(value: Fixed) -> Fixed {
1083 Fixed::from_bits(value.to_bits() & !0x3FF)
1084}
1085
1086fn half(value: Fixed) -> Fixed {
1087 Fixed::from_bits(value.to_bits() / 2)
1088}
1089
1090fn twice(value: Fixed) -> Fixed {
1091 Fixed::from_bits(value.to_bits().wrapping_mul(2))
1092}
1093
1094fn midpoint(a: Fixed, b: Fixed) -> Fixed {
1097 a + half(b - a)
1098}
1099
1100#[cfg(test)]
1101mod tests {
1102 use read_fonts::{tables::postscript::charstring::CommandSink, types::F2Dot14, FontRef};
1103
1104 use super::{
1105 BlueZone, Blues, Fixed, Hint, HintMap, HintMask, HintParams, HintState, HintingSink,
1106 StemHint, GHOST_BOTTOM, GHOST_TOP, HINT_MASK_SIZE, LOCKED, PAIR_BOTTOM, PAIR_TOP,
1107 };
1108
1109 fn make_hint_state() -> HintState {
1110 fn make_blues(values: &[f64]) -> Blues {
1111 Blues::new(values.iter().copied().map(Fixed::from_f64))
1112 }
1113 let params = HintParams {
1119 blues: make_blues(&[
1120 -15.0, 0.0, 536.0, 547.0, 571.0, 582.0, 714.0, 726.0, 760.0, 772.0,
1121 ]),
1122 other_blues: make_blues(&[-255.0, -240.0]),
1123 blue_scale: Fixed::from_f64(0.05),
1124 blue_shift: Fixed::from_i32(7),
1125 blue_fuzz: Fixed::ZERO,
1126 ..Default::default()
1127 };
1128 HintState::new(¶ms, Fixed::ONE / Fixed::from_i32(64))
1129 }
1130
1131 #[test]
1132 fn scaled_blue_zones() {
1133 let state = make_hint_state();
1134 assert!(!state.do_em_box_hints);
1135 assert_eq!(state.zone_count, 6);
1136 assert_eq!(state.boost, Fixed::from_bits(27035));
1137 assert!(state.suppress_overshoot);
1138 let expected_zones = &[
1140 BlueZone {
1146 cs_bottom_edge: Fixed::from_bits(-983040),
1147 is_bottom: true,
1148 ..Default::default()
1149 },
1150 BlueZone {
1156 cs_bottom_edge: Fixed::from_bits(35127296),
1157 cs_top_edge: Fixed::from_bits(35848192),
1158 cs_flat_edge: Fixed::from_bits(35127296),
1159 ds_flat_edge: Fixed::from_bits(589824),
1160 is_bottom: false,
1161 },
1162 BlueZone {
1168 cs_bottom_edge: Fixed::from_bits(37421056),
1169 cs_top_edge: Fixed::from_bits(38141952),
1170 cs_flat_edge: Fixed::from_bits(37421056),
1171 ds_flat_edge: Fixed::from_bits(589824),
1172 is_bottom: false,
1173 },
1174 BlueZone {
1180 cs_bottom_edge: Fixed::from_bits(46792704),
1181 cs_top_edge: Fixed::from_bits(47579136),
1182 cs_flat_edge: Fixed::from_bits(46792704),
1183 ds_flat_edge: Fixed::from_bits(786432),
1184 is_bottom: false,
1185 },
1186 BlueZone {
1192 cs_bottom_edge: Fixed::from_bits(49807360),
1193 cs_top_edge: Fixed::from_bits(50593792),
1194 cs_flat_edge: Fixed::from_bits(49807360),
1195 ds_flat_edge: Fixed::from_bits(786432),
1196 is_bottom: false,
1197 },
1198 BlueZone {
1204 cs_bottom_edge: Fixed::from_bits(-16711680),
1205 cs_top_edge: Fixed::from_bits(-15728640),
1206 cs_flat_edge: Fixed::from_bits(-15728640),
1207 ds_flat_edge: Fixed::from_bits(-262144),
1208 is_bottom: true,
1209 },
1210 ];
1211 assert_eq!(state.zones(), expected_zones);
1212 }
1213
1214 #[test]
1215 fn blue_zone_capture() {
1216 let state = make_hint_state();
1217 let bottom_edge = Hint {
1218 flags: PAIR_BOTTOM,
1219 ds_coord: Fixed::from_f64(2.3),
1220 ..Default::default()
1221 };
1222 let top_edge = Hint {
1223 flags: PAIR_TOP,
1224 cs_coord: Fixed::from_bits(35127297),
1226 ds_coord: Fixed::from_f64(2.3),
1227 ..Default::default()
1228 };
1229 {
1231 let (mut bottom_edge, mut top_edge) = (bottom_edge, top_edge);
1232 assert!(state.capture(&mut bottom_edge, &mut top_edge));
1233 assert!(bottom_edge.is_locked());
1234 assert!(top_edge.is_locked());
1235 }
1236 {
1238 let min_cs_coord = Fixed::MIN;
1241 let mut bottom_edge = Hint {
1242 cs_coord: min_cs_coord,
1243 ..bottom_edge
1244 };
1245 let mut top_edge = Hint {
1246 cs_coord: min_cs_coord,
1247 ..top_edge
1248 };
1249 assert!(!state.capture(&mut bottom_edge, &mut top_edge));
1250 assert!(!bottom_edge.is_locked());
1251 assert!(!top_edge.is_locked());
1252 }
1253 {
1255 let mut bottom_edge = bottom_edge;
1256 let mut top_edge = Hint {
1257 flags: 0,
1259 ..top_edge
1260 };
1261 assert!(state.capture(&mut bottom_edge, &mut top_edge));
1262 assert!(bottom_edge.is_locked());
1263 assert!(!top_edge.is_locked());
1264 }
1265 {
1267 let mut bottom_edge = Hint {
1268 flags: 0,
1270 ..bottom_edge
1271 };
1272 let mut top_edge = top_edge;
1273 assert!(state.capture(&mut bottom_edge, &mut top_edge));
1274 assert!(!bottom_edge.is_locked());
1275 assert!(top_edge.is_locked());
1276 }
1277 }
1278
1279 #[test]
1280 fn hint_mask_ops() {
1281 const MAX_BITS: usize = HINT_MASK_SIZE * 8;
1282 let all_bits = HintMask::all();
1283 for i in 0..MAX_BITS {
1284 assert!(all_bits.get(i));
1285 }
1286 let odd_bits = HintMask::new(&[0b01010101; HINT_MASK_SIZE]).unwrap();
1287 for i in 0..MAX_BITS {
1288 assert_eq!(i & 1 != 0, odd_bits.get(i));
1289 }
1290 let mut cleared_bits = odd_bits;
1291 for i in 0..MAX_BITS {
1292 if i & 1 != 0 {
1293 cleared_bits.clear(i);
1294 }
1295 }
1296 assert_eq!(cleared_bits.mask, HintMask::default().mask);
1297 }
1298
1299 #[test]
1300 fn hint_mapping() {
1301 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
1302 let cff_font = super::super::Outlines::new(&font).unwrap();
1303 let state = cff_font
1304 .subfont(0, Some(8.0), &[F2Dot14::from_f32(-1.0); 2])
1305 .unwrap()
1306 .hint_state;
1307 let mut initial_map = HintMap::new(state.scale);
1308 let mut map = HintMap::new(state.scale);
1309 let mut stems = [
1311 StemHint {
1312 min: Fixed::from_bits(1376256),
1313 max: Fixed::ZERO,
1314 ..Default::default()
1315 },
1316 StemHint {
1317 min: Fixed::from_bits(16318464),
1318 max: Fixed::from_bits(17563648),
1319 ..Default::default()
1320 },
1321 StemHint {
1322 min: Fixed::from_bits(45481984),
1323 max: Fixed::from_bits(44171264),
1324 ..Default::default()
1325 },
1326 ];
1327 map.build(
1328 &state,
1329 Some(HintMask::all()),
1330 Some(&mut initial_map),
1331 &mut stems,
1332 Fixed::ZERO,
1333 false,
1334 );
1335 let expected_edges = [
1343 Hint {
1344 index: 0,
1345 cs_coord: Fixed::from_f64(0.0),
1346 ds_coord: Fixed::from_f64(0.0),
1347 scale: Fixed::from_bits(526),
1348 flags: GHOST_BOTTOM | LOCKED,
1349 },
1350 Hint {
1351 index: 1,
1352 cs_coord: Fixed::from_bits(16318464),
1353 ds_coord: Fixed::from_bits(131072),
1354 scale: Fixed::from_bits(524),
1355 flags: PAIR_BOTTOM,
1356 },
1357 Hint {
1358 index: 1,
1359 cs_coord: Fixed::from_bits(17563648),
1360 ds_coord: Fixed::from_bits(141028),
1361 scale: Fixed::from_bits(592),
1362 flags: PAIR_TOP,
1363 },
1364 Hint {
1365 index: 2,
1366 cs_coord: Fixed::from_bits(45481984),
1367 ds_coord: Fixed::from_bits(393216),
1368 scale: Fixed::from_bits(524),
1369 flags: GHOST_TOP | LOCKED,
1370 },
1371 ];
1372 assert_eq!(expected_edges, &map.edges[..map.len]);
1373 let mappings = [
1375 (0, 0), (44302336, 382564), (45481984, 393216), (16318464, 131072), (17563648, 141028), (49676288, 426752), (56754176, 483344), (57868288, 492252), (50069504, 429896), ];
1386 for (coord, expected) in mappings {
1387 assert_eq!(
1388 map.transform(Fixed::from_bits(coord)),
1389 Fixed::from_bits(expected)
1390 );
1391 }
1392 }
1393
1394 #[test]
1395 fn midpoint_avoids_overflow() {
1396 let a = i16::MAX as i32;
1402 let b = a - 1;
1403 assert!(a + b > i16::MAX as i32);
1404 let mid = super::midpoint(Fixed::from_i32(a), Fixed::from_i32(b));
1405 assert_eq!((a + b) / 2, mid.to_bits() >> 16);
1406 }
1407
1408 #[test]
1412 fn hinting_sink_omits_closing_line_that_matches_start() {
1413 let state = HintState {
1414 scale: Fixed::ONE,
1415 ..Default::default()
1416 };
1417 let mut path = Path::default();
1418 let mut sink = HintingSink::new(&state, &mut path);
1419 let move1_2 = [Fixed::from_f64(1.0), Fixed::from_f64(2.0)];
1420 let line2_3 = [Fixed::from_f64(2.0), Fixed::from_f64(3.0)];
1421 let line1_2 = [Fixed::from_f64(1.0), Fixed::from_f64(2.0)];
1422 let line3_4 = [Fixed::from_f64(3.0), Fixed::from_f64(4.0)];
1423 let curve = [
1424 Fixed::from_f64(3.0),
1425 Fixed::from_f64(4.0),
1426 Fixed::from_f64(5.0),
1427 Fixed::from_f64(6.0),
1428 Fixed::from_f64(1.0),
1429 Fixed::from_f64(2.0),
1430 ];
1431 sink.move_to(move1_2[0], move1_2[1]);
1433 sink.line_to(line2_3[0], line2_3[1]);
1434 sink.line_to(line1_2[0], line1_2[1]);
1435 sink.move_to(move1_2[0], move1_2[1]);
1437 sink.line_to(line2_3[0], line2_3[1]);
1438 sink.line_to(line3_4[0], line3_4[1]);
1439 sink.move_to(move1_2[0], move1_2[1]);
1442 sink.line_to(line2_3[0], line2_3[1]);
1443 sink.curve_to(curve[0], curve[1], curve[2], curve[3], curve[4], curve[5]);
1444 sink.finish();
1445 assert_eq!(
1448 &path.0,
1449 &[
1450 MoveTo(move1_2),
1452 LineTo(line2_3),
1453 Close,
1455 MoveTo(move1_2),
1457 LineTo(line2_3),
1458 LineTo(line3_4),
1459 Close,
1460 MoveTo(move1_2),
1462 LineTo(line2_3),
1463 CurveTo(curve),
1464 Close,
1465 ]
1466 );
1467 }
1468
1469 #[derive(Copy, Clone, PartialEq, Debug)]
1470 enum Command {
1471 MoveTo([Fixed; 2]),
1472 LineTo([Fixed; 2]),
1473 CurveTo([Fixed; 6]),
1474 Close,
1475 }
1476
1477 use Command::*;
1478
1479 #[derive(Default)]
1480 struct Path(Vec<Command>);
1481
1482 impl CommandSink for Path {
1483 fn move_to(&mut self, x: Fixed, y: Fixed) {
1484 self.0.push(MoveTo([x, y]));
1485 }
1486 fn line_to(&mut self, x: Fixed, y: Fixed) {
1487 self.0.push(LineTo([x, y]));
1488 }
1489 fn curve_to(&mut self, cx0: Fixed, cy0: Fixed, cx1: Fixed, cy1: Fixed, x: Fixed, y: Fixed) {
1490 self.0.push(CurveTo([cx0, cy0, cx1, cy1, x, y]));
1491 }
1492 fn close(&mut self) {
1493 self.0.push(Close);
1494 }
1495 }
1496}