1mod hint;
4
5use super::{GlyphHMetrics, OutlinePen};
6use hint::{HintParams, HintState, HintingSink};
7use raw::{tables::postscript::dict::normalize_font_matrix, FontRef};
8use read_fonts::{
9 tables::{
10 postscript::{
11 charstring::{self, CommandSink},
12 dict, BlendState, Error, FdSelect, Index,
13 },
14 variations::ItemVariationStore,
15 },
16 types::{F2Dot14, Fixed, GlyphId},
17 FontData, FontRead, ReadError, TableProvider,
18};
19use std::ops::Range;
20
21#[derive(Clone)]
39pub(crate) struct Outlines<'a> {
40 pub(crate) font: FontRef<'a>,
41 pub(crate) glyph_metrics: GlyphHMetrics<'a>,
42 offset_data: FontData<'a>,
43 global_subrs: Index<'a>,
44 top_dict: TopDict<'a>,
45 version: u16,
46 units_per_em: u16,
47}
48
49impl<'a> Outlines<'a> {
50 pub fn new(font: &FontRef<'a>) -> Option<Self> {
55 let units_per_em = font.head().ok()?.units_per_em();
56 Self::from_cff2(font, units_per_em).or_else(|| Self::from_cff(font, units_per_em))
57 }
58
59 pub fn from_cff(font: &FontRef<'a>, units_per_em: u16) -> Option<Self> {
60 let cff1 = font.cff().ok()?;
61 let glyph_metrics = GlyphHMetrics::new(font)?;
62 let top_dict_data = cff1.top_dicts().get(0).ok()?;
68 let top_dict = TopDict::new(cff1.offset_data().as_bytes(), top_dict_data, false).ok()?;
69 Some(Self {
70 font: font.clone(),
71 glyph_metrics,
72 offset_data: cff1.offset_data(),
73 global_subrs: cff1.global_subrs().into(),
74 top_dict,
75 version: 1,
76 units_per_em,
77 })
78 }
79
80 pub fn from_cff2(font: &FontRef<'a>, units_per_em: u16) -> Option<Self> {
81 let cff2 = font.cff2().ok()?;
82 let glyph_metrics = GlyphHMetrics::new(font)?;
83 let table_data = cff2.offset_data().as_bytes();
84 let top_dict = TopDict::new(table_data, cff2.top_dict_data(), true).ok()?;
85 Some(Self {
86 font: font.clone(),
87 glyph_metrics,
88 offset_data: cff2.offset_data(),
89 global_subrs: cff2.global_subrs().into(),
90 top_dict,
91 version: 2,
92 units_per_em,
93 })
94 }
95
96 pub fn is_cff2(&self) -> bool {
97 self.version == 2
98 }
99
100 pub fn units_per_em(&self) -> u16 {
101 self.units_per_em
102 }
103
104 pub fn glyph_count(&self) -> usize {
106 self.top_dict.charstrings.count() as usize
107 }
108
109 pub fn subfont_count(&self) -> u32 {
111 self.top_dict.font_dicts.count().max(1)
113 }
114
115 pub fn subfont_index(&self, glyph_id: GlyphId) -> u32 {
118 self.top_dict
129 .fd_select
130 .as_ref()
131 .and_then(|select| select.font_index(glyph_id))
132 .unwrap_or(0) as u32
133 }
134
135 pub fn subfont(
141 &self,
142 index: u32,
143 size: Option<f32>,
144 coords: &[F2Dot14],
145 ) -> Result<Subfont, Error> {
146 let font_dict = self.parse_font_dict(index)?;
147 let blend_state = self
148 .top_dict
149 .var_store
150 .clone()
151 .map(|store| BlendState::new(store, coords, 0))
152 .transpose()?;
153 let private_dict =
154 PrivateDict::new(self.offset_data, font_dict.private_dict_range, blend_state)?;
155 let upem = self.units_per_em as i32;
156 let mut scale = match size {
157 Some(ppem) if upem > 0 => {
158 Some(Fixed::from_bits((ppem * 64.) as i32) / Fixed::from_bits(upem))
161 }
162 _ => None,
163 };
164 let scale_requested = size.is_some();
165 let font_matrix = if let Some((top_matrix, top_upem)) = self.top_dict.font_matrix {
168 if let Some((sub_matrix, sub_upem)) = font_dict.font_matrix {
170 let scaling = if top_upem > 1 && sub_upem > 1 {
171 top_upem.min(sub_upem)
172 } else {
173 1
174 };
175 let matrix = matrix_mul_scaled(&top_matrix, &sub_matrix, scaling);
177 let upem = Fixed::from_bits(sub_upem)
178 .mul_div(Fixed::from_bits(top_upem), Fixed::from_bits(scaling));
179 Some(normalize_font_matrix(matrix, upem.to_bits()))
181 } else {
182 Some((top_matrix, top_upem))
184 }
185 } else if let Some((matrix, upem)) = font_dict.font_matrix {
186 Some(normalize_font_matrix(matrix, upem))
188 } else {
189 None
190 };
191 let mut font_matrix = if let Some((matrix, matrix_upem)) = font_matrix {
194 if matrix_upem != upem {
197 let original_scale = scale.unwrap_or(Fixed::from_i32(64));
201 scale = Some(
202 original_scale.mul_div(Fixed::from_bits(upem), Fixed::from_bits(matrix_upem)),
203 );
204 }
205 Some(matrix)
206 } else {
207 None
208 };
209 if font_matrix
210 == Some([
211 Fixed::ONE,
212 Fixed::ZERO,
213 Fixed::ZERO,
214 Fixed::ONE,
215 Fixed::ZERO,
216 Fixed::ZERO,
217 ])
218 {
219 font_matrix = None;
222 }
223 let hint_scale = scale_for_hinting(scale);
224 let hint_state = HintState::new(&private_dict.hint_params, hint_scale);
225 Ok(Subfont {
226 is_cff2: self.is_cff2(),
227 scale,
228 scale_requested,
229 subrs_offset: private_dict.subrs_offset,
230 hint_state,
231 store_index: private_dict.store_index,
232 font_matrix,
233 })
234 }
235
236 pub fn draw(
248 &self,
249 subfont: &Subfont,
250 glyph_id: GlyphId,
251 coords: &[F2Dot14],
252 hint: bool,
253 pen: &mut impl OutlinePen,
254 ) -> Result<(), Error> {
255 let cff_data = self.offset_data.as_bytes();
256 let charstrings = self.top_dict.charstrings.clone();
257 let charstring_data = charstrings.get(glyph_id.to_u32() as usize)?;
258 let subrs = subfont.subrs(self)?;
259 let blend_state = subfont.blend_state(self, coords)?;
260 let cs_eval = CharstringEvaluator {
261 cff_data,
262 charstrings,
263 global_subrs: self.global_subrs.clone(),
264 subrs,
265 blend_state,
266 charstring_data,
267 };
268 let apply_hinting = hint && subfont.scale_requested;
270 let mut pen_sink = PenSink::new(pen);
271 let mut simplifying_adapter = NopFilteringSink::new(&mut pen_sink);
272 if let Some(matrix) = subfont.font_matrix {
273 if apply_hinting {
274 let mut transform_sink =
275 HintedTransformingSink::new(&mut simplifying_adapter, matrix);
276 let mut hinting_adapter =
277 HintingSink::new(&subfont.hint_state, &mut transform_sink);
278 cs_eval.evaluate(&mut hinting_adapter)?;
279 hinting_adapter.finish();
280 } else {
281 let mut transform_sink =
282 ScalingTransformingSink::new(&mut simplifying_adapter, matrix, subfont.scale);
283 cs_eval.evaluate(&mut transform_sink)?;
284 }
285 } else if apply_hinting {
286 let mut hinting_adapter =
287 HintingSink::new(&subfont.hint_state, &mut simplifying_adapter);
288 cs_eval.evaluate(&mut hinting_adapter)?;
289 hinting_adapter.finish();
290 } else {
291 let mut scaling_adapter =
292 ScalingSink26Dot6::new(&mut simplifying_adapter, subfont.scale);
293 cs_eval.evaluate(&mut scaling_adapter)?;
294 }
295 simplifying_adapter.finish();
296 Ok(())
297 }
298
299 fn parse_font_dict(&self, subfont_index: u32) -> Result<FontDict, Error> {
300 if self.top_dict.font_dicts.count() != 0 {
301 let font_dict_data = self.top_dict.font_dicts.get(subfont_index as usize)?;
304 FontDict::new(font_dict_data)
305 } else {
306 let range = self.top_dict.private_dict_range.clone();
311 Ok(FontDict {
312 private_dict_range: range.start as usize..range.end as usize,
313 font_matrix: None,
314 })
315 }
316 }
317}
318
319fn scale_for_hinting(scale: Option<Fixed>) -> Fixed {
323 Fixed::from_bits((scale.unwrap_or(Fixed::ONE).to_bits().saturating_add(32)) / 64)
324}
325
326struct CharstringEvaluator<'a> {
327 cff_data: &'a [u8],
328 charstrings: Index<'a>,
329 global_subrs: Index<'a>,
330 subrs: Option<Index<'a>>,
331 blend_state: Option<BlendState<'a>>,
332 charstring_data: &'a [u8],
333}
334
335impl CharstringEvaluator<'_> {
336 fn evaluate(self, sink: &mut impl CommandSink) -> Result<(), Error> {
337 charstring::evaluate(
338 self.cff_data,
339 self.charstrings,
340 self.global_subrs,
341 self.subrs,
342 self.blend_state,
343 self.charstring_data,
344 sink,
345 )
346 }
347}
348
349#[derive(Clone)]
357pub(crate) struct Subfont {
358 is_cff2: bool,
359 scale: Option<Fixed>,
360 scale_requested: bool,
364 subrs_offset: Option<usize>,
365 pub(crate) hint_state: HintState,
366 store_index: u16,
367 font_matrix: Option<[Fixed; 6]>,
368}
369
370impl Subfont {
371 pub fn subrs<'a>(&self, scaler: &Outlines<'a>) -> Result<Option<Index<'a>>, Error> {
373 if let Some(subrs_offset) = self.subrs_offset {
374 let offset_data = scaler.offset_data.as_bytes();
375 let index_data = offset_data.get(subrs_offset..).unwrap_or_default();
376 Ok(Some(Index::new(index_data, self.is_cff2)?))
377 } else {
378 Ok(None)
379 }
380 }
381
382 pub fn blend_state<'a>(
385 &self,
386 scaler: &Outlines<'a>,
387 coords: &'a [F2Dot14],
388 ) -> Result<Option<BlendState<'a>>, Error> {
389 if let Some(var_store) = scaler.top_dict.var_store.clone() {
390 Ok(Some(BlendState::new(var_store, coords, self.store_index)?))
391 } else {
392 Ok(None)
393 }
394 }
395}
396
397#[derive(Default)]
400struct PrivateDict {
401 hint_params: HintParams,
402 subrs_offset: Option<usize>,
403 store_index: u16,
404}
405
406impl PrivateDict {
407 fn new(
408 data: FontData,
409 range: Range<usize>,
410 blend_state: Option<BlendState<'_>>,
411 ) -> Result<Self, Error> {
412 let private_dict_data = data.read_array(range.clone())?;
413 let mut dict = Self::default();
414 for entry in dict::entries(private_dict_data, blend_state) {
415 use dict::Entry::*;
416 match entry? {
417 BlueValues(values) => dict.hint_params.blues = values,
418 FamilyBlues(values) => dict.hint_params.family_blues = values,
419 OtherBlues(values) => dict.hint_params.other_blues = values,
420 FamilyOtherBlues(values) => dict.hint_params.family_other_blues = values,
421 BlueScale(value) => dict.hint_params.blue_scale = value,
422 BlueShift(value) => dict.hint_params.blue_shift = value,
423 BlueFuzz(value) => dict.hint_params.blue_fuzz = value,
424 LanguageGroup(group) => dict.hint_params.language_group = group,
425 SubrsOffset(offset) => {
427 dict.subrs_offset = Some(
428 range
429 .start
430 .checked_add(offset)
431 .ok_or(ReadError::OutOfBounds)?,
432 )
433 }
434 VariationStoreIndex(index) => dict.store_index = index,
435 _ => {}
436 }
437 }
438 Ok(dict)
439 }
440}
441
442#[derive(Clone, Default)]
444struct FontDict {
445 private_dict_range: Range<usize>,
446 font_matrix: Option<([Fixed; 6], i32)>,
447}
448
449impl FontDict {
450 fn new(font_dict_data: &[u8]) -> Result<Self, Error> {
451 let mut range = None;
452 let mut font_matrix = None;
453 for entry in dict::entries(font_dict_data, None) {
454 match entry? {
455 dict::Entry::PrivateDictRange(r) => {
456 range = Some(r);
457 }
458 dict::Entry::FontMatrix(matrix, upem) => font_matrix = Some((matrix, upem)),
462 _ => {}
463 }
464 }
465 Ok(Self {
466 private_dict_range: range.ok_or(Error::MissingPrivateDict)?,
467 font_matrix,
468 })
469 }
470}
471
472#[derive(Clone, Default)]
475struct TopDict<'a> {
476 charstrings: Index<'a>,
477 font_dicts: Index<'a>,
478 fd_select: Option<FdSelect<'a>>,
479 private_dict_range: Range<u32>,
480 font_matrix: Option<([Fixed; 6], i32)>,
481 var_store: Option<ItemVariationStore<'a>>,
482}
483
484impl<'a> TopDict<'a> {
485 fn new(table_data: &'a [u8], top_dict_data: &'a [u8], is_cff2: bool) -> Result<Self, Error> {
486 let mut items = TopDict::default();
487 for entry in dict::entries(top_dict_data, None) {
488 match entry? {
489 dict::Entry::CharstringsOffset(offset) => {
490 items.charstrings =
491 Index::new(table_data.get(offset..).unwrap_or_default(), is_cff2)?;
492 }
493 dict::Entry::FdArrayOffset(offset) => {
494 items.font_dicts =
495 Index::new(table_data.get(offset..).unwrap_or_default(), is_cff2)?;
496 }
497 dict::Entry::FdSelectOffset(offset) => {
498 items.fd_select = Some(FdSelect::read(FontData::new(
499 table_data.get(offset..).unwrap_or_default(),
500 ))?);
501 }
502 dict::Entry::PrivateDictRange(range) => {
503 items.private_dict_range = range.start as u32..range.end as u32;
504 }
505 dict::Entry::FontMatrix(matrix, upem) => {
506 items.font_matrix = Some(normalize_font_matrix(matrix, upem));
508 }
509 dict::Entry::VariationStoreOffset(offset) if is_cff2 => {
510 let offset = offset.checked_add(2).ok_or(ReadError::OutOfBounds)?;
514 items.var_store = Some(ItemVariationStore::read(FontData::new(
515 table_data.get(offset..).unwrap_or_default(),
516 ))?);
517 }
518 _ => {}
519 }
520 }
521 Ok(items)
522 }
523}
524
525struct PenSink<'a, P>(&'a mut P);
528
529impl<'a, P> PenSink<'a, P> {
530 fn new(pen: &'a mut P) -> Self {
531 Self(pen)
532 }
533}
534
535impl<P> CommandSink for PenSink<'_, P>
536where
537 P: OutlinePen,
538{
539 fn move_to(&mut self, x: Fixed, y: Fixed) {
540 self.0.move_to(x.to_f32(), y.to_f32());
541 }
542
543 fn line_to(&mut self, x: Fixed, y: Fixed) {
544 self.0.line_to(x.to_f32(), y.to_f32());
545 }
546
547 fn curve_to(&mut self, cx0: Fixed, cy0: Fixed, cx1: Fixed, cy1: Fixed, x: Fixed, y: Fixed) {
548 self.0.curve_to(
549 cx0.to_f32(),
550 cy0.to_f32(),
551 cx1.to_f32(),
552 cy1.to_f32(),
553 x.to_f32(),
554 y.to_f32(),
555 );
556 }
557
558 fn close(&mut self) {
559 self.0.close();
560 }
561}
562
563fn transform(matrix: &[Fixed; 6], x: Fixed, y: Fixed) -> (Fixed, Fixed) {
564 (
565 matrix[0] * x + matrix[2] * y + matrix[4],
566 matrix[1] * x + matrix[3] * y + matrix[5],
567 )
568}
569
570struct HintedTransformingSink<'a, S> {
572 inner: &'a mut S,
573 matrix: [Fixed; 6],
574}
575
576impl<'a, S> HintedTransformingSink<'a, S> {
577 fn new(sink: &'a mut S, matrix: [Fixed; 6]) -> Self {
578 Self {
579 inner: sink,
580 matrix,
581 }
582 }
583
584 fn transform(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
585 let (x, y) = transform(
588 &self.matrix,
589 Fixed::from_bits(x.to_bits() >> 10),
590 Fixed::from_bits(y.to_bits() >> 10),
591 );
592 (
593 Fixed::from_bits(x.to_bits() << 10),
594 Fixed::from_bits(y.to_bits() << 10),
595 )
596 }
597}
598
599impl<S: CommandSink> CommandSink for HintedTransformingSink<'_, S> {
600 fn hstem(&mut self, y: Fixed, dy: Fixed) {
601 self.inner.hstem(y, dy);
602 }
603
604 fn vstem(&mut self, x: Fixed, dx: Fixed) {
605 self.inner.vstem(x, dx);
606 }
607
608 fn hint_mask(&mut self, mask: &[u8]) {
609 self.inner.hint_mask(mask);
610 }
611
612 fn counter_mask(&mut self, mask: &[u8]) {
613 self.inner.counter_mask(mask);
614 }
615
616 fn clear_hints(&mut self) {
617 self.inner.clear_hints();
618 }
619
620 fn move_to(&mut self, x: Fixed, y: Fixed) {
621 let (x, y) = self.transform(x, y);
622 self.inner.move_to(x, y);
623 }
624
625 fn line_to(&mut self, x: Fixed, y: Fixed) {
626 let (x, y) = self.transform(x, y);
627 self.inner.line_to(x, y);
628 }
629
630 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
631 let (cx1, cy1) = self.transform(cx1, cy1);
632 let (cx2, cy2) = self.transform(cx2, cy2);
633 let (x, y) = self.transform(x, y);
634 self.inner.curve_to(cx1, cy1, cx2, cy2, x, y);
635 }
636
637 fn close(&mut self) {
638 self.inner.close();
639 }
640}
641
642const ONE_OVER_64: Fixed = Fixed::from_bits(0x400);
644
645struct ScalingTransformingSink<'a, S> {
648 inner: &'a mut S,
649 matrix: [Fixed; 6],
650 scale: Option<Fixed>,
651}
652
653impl<'a, S> ScalingTransformingSink<'a, S> {
654 fn new(sink: &'a mut S, matrix: [Fixed; 6], scale: Option<Fixed>) -> Self {
655 Self {
656 inner: sink,
657 matrix,
658 scale,
659 }
660 }
661
662 fn transform(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
663 let ax = x * ONE_OVER_64;
673 let ay = y * ONE_OVER_64;
674 let bx = Fixed::from_bits(ax.to_bits() >> 10);
678 let by = Fixed::from_bits(ay.to_bits() >> 10);
679 let (cx, cy) = transform(&self.matrix, bx, by);
681 if let Some(scale) = self.scale {
682 let dx = cx * scale;
686 let dy = cy * scale;
687 (
689 Fixed::from_bits(dx.to_bits() << 10),
690 Fixed::from_bits(dy.to_bits() << 10),
691 )
692 } else {
693 (
696 Fixed::from_bits(cx.to_bits() << 16),
697 Fixed::from_bits(cy.to_bits() << 16),
698 )
699 }
700 }
701}
702
703impl<S: CommandSink> CommandSink for ScalingTransformingSink<'_, S> {
704 fn hstem(&mut self, y: Fixed, dy: Fixed) {
705 self.inner.hstem(y, dy);
706 }
707
708 fn vstem(&mut self, x: Fixed, dx: Fixed) {
709 self.inner.vstem(x, dx);
710 }
711
712 fn hint_mask(&mut self, mask: &[u8]) {
713 self.inner.hint_mask(mask);
714 }
715
716 fn counter_mask(&mut self, mask: &[u8]) {
717 self.inner.counter_mask(mask);
718 }
719
720 fn clear_hints(&mut self) {
721 self.inner.clear_hints();
722 }
723
724 fn move_to(&mut self, x: Fixed, y: Fixed) {
725 let (x, y) = self.transform(x, y);
726 self.inner.move_to(x, y);
727 }
728
729 fn line_to(&mut self, x: Fixed, y: Fixed) {
730 let (x, y) = self.transform(x, y);
731 self.inner.line_to(x, y);
732 }
733
734 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
735 let (cx1, cy1) = self.transform(cx1, cy1);
736 let (cx2, cy2) = self.transform(cx2, cy2);
737 let (x, y) = self.transform(x, y);
738 self.inner.curve_to(cx1, cy1, cx2, cy2, x, y);
739 }
740
741 fn close(&mut self) {
742 self.inner.close();
743 }
744}
745
746struct ScalingSink26Dot6<'a, S> {
752 inner: &'a mut S,
753 scale: Option<Fixed>,
754}
755
756impl<'a, S> ScalingSink26Dot6<'a, S> {
757 fn new(sink: &'a mut S, scale: Option<Fixed>) -> Self {
758 Self { scale, inner: sink }
759 }
760
761 fn scale(&self, coord: Fixed) -> Fixed {
762 let a = coord * ONE_OVER_64;
772 let b = Fixed::from_bits(a.to_bits() >> 10);
776 if let Some(scale) = self.scale {
777 let c = b * scale;
781 Fixed::from_bits(c.to_bits() << 10)
783 } else {
784 Fixed::from_bits(b.to_bits() << 16)
787 }
788 }
789}
790
791impl<S: CommandSink> CommandSink for ScalingSink26Dot6<'_, S> {
792 fn hstem(&mut self, y: Fixed, dy: Fixed) {
793 self.inner.hstem(y, dy);
794 }
795
796 fn vstem(&mut self, x: Fixed, dx: Fixed) {
797 self.inner.vstem(x, dx);
798 }
799
800 fn hint_mask(&mut self, mask: &[u8]) {
801 self.inner.hint_mask(mask);
802 }
803
804 fn counter_mask(&mut self, mask: &[u8]) {
805 self.inner.counter_mask(mask);
806 }
807
808 fn clear_hints(&mut self) {
809 self.inner.clear_hints();
810 }
811
812 fn move_to(&mut self, x: Fixed, y: Fixed) {
813 self.inner.move_to(self.scale(x), self.scale(y));
814 }
815
816 fn line_to(&mut self, x: Fixed, y: Fixed) {
817 self.inner.line_to(self.scale(x), self.scale(y));
818 }
819
820 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
821 self.inner.curve_to(
822 self.scale(cx1),
823 self.scale(cy1),
824 self.scale(cx2),
825 self.scale(cy2),
826 self.scale(x),
827 self.scale(y),
828 );
829 }
830
831 fn close(&mut self) {
832 self.inner.close();
833 }
834}
835
836#[derive(Copy, Clone)]
837enum PendingElement {
838 Move([Fixed; 2]),
839 Line([Fixed; 2]),
840 Curve([Fixed; 6]),
841}
842
843impl PendingElement {
844 fn target_point(&self) -> [Fixed; 2] {
845 match self {
846 Self::Move(xy) | Self::Line(xy) => *xy,
847 Self::Curve([.., x, y]) => [*x, *y],
848 }
849 }
850}
851
852struct NopFilteringSink<'a, S> {
861 is_open: bool,
862 start: Option<(Fixed, Fixed)>,
863 pending_element: Option<PendingElement>,
864 inner: &'a mut S,
865}
866
867impl<'a, S> NopFilteringSink<'a, S>
868where
869 S: CommandSink,
870{
871 fn new(inner: &'a mut S) -> Self {
872 Self {
873 is_open: false,
874 start: None,
875 pending_element: None,
876 inner,
877 }
878 }
879
880 fn flush_pending(&mut self, for_close: bool) {
881 if let Some(pending) = self.pending_element.take() {
882 match pending {
883 PendingElement::Move([x, y]) => {
884 if !for_close {
885 self.is_open = true;
886 self.inner.move_to(x, y);
887 self.start = Some((x, y));
888 }
889 }
890 PendingElement::Line([x, y]) => {
891 if !for_close || self.start != Some((x, y)) {
892 self.inner.line_to(x, y);
893 }
894 }
895 PendingElement::Curve([cx0, cy0, cx1, cy1, x, y]) => {
896 self.inner.curve_to(cx0, cy0, cx1, cy1, x, y);
897 }
898 }
899 }
900 }
901
902 pub fn finish(&mut self) {
903 self.close();
904 }
905}
906
907impl<S> CommandSink for NopFilteringSink<'_, S>
908where
909 S: CommandSink,
910{
911 fn hstem(&mut self, y: Fixed, dy: Fixed) {
912 self.inner.hstem(y, dy);
913 }
914
915 fn vstem(&mut self, x: Fixed, dx: Fixed) {
916 self.inner.vstem(x, dx);
917 }
918
919 fn hint_mask(&mut self, mask: &[u8]) {
920 self.inner.hint_mask(mask);
921 }
922
923 fn counter_mask(&mut self, mask: &[u8]) {
924 self.inner.counter_mask(mask);
925 }
926
927 fn clear_hints(&mut self) {
928 self.inner.clear_hints();
929 }
930
931 fn move_to(&mut self, x: Fixed, y: Fixed) {
932 self.pending_element = Some(PendingElement::Move([x, y]));
933 }
934
935 fn line_to(&mut self, x: Fixed, y: Fixed) {
936 if self
938 .pending_element
939 .map(|element| element.target_point() == [x, y])
940 .unwrap_or_default()
941 {
942 return;
943 }
944 self.flush_pending(false);
945 self.pending_element = Some(PendingElement::Line([x, y]));
946 }
947
948 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
949 self.flush_pending(false);
950 self.pending_element = Some(PendingElement::Curve([cx1, cy1, cx2, cy2, x, y]));
951 }
952
953 fn close(&mut self) {
954 self.flush_pending(true);
955 if self.is_open {
956 self.inner.close();
957 self.is_open = false;
958 }
959 }
960}
961
962fn matrix_mul_scaled(a: &[Fixed; 6], b: &[Fixed; 6], scaling: i32) -> [Fixed; 6] {
970 let val = Fixed::from_i32(scaling);
971 let xx = a[0].mul_div(b[0], val) + a[2].mul_div(b[1], val);
972 let yx = a[1].mul_div(b[0], val) + a[3].mul_div(b[1], val);
973 let xy = a[0].mul_div(b[2], val) + a[2].mul_div(b[3], val);
974 let yy = a[1].mul_div(b[2], val) + a[3].mul_div(b[3], val);
975 let x = b[4];
976 let y = b[5];
977 let dx = x.mul_div(a[0], val) + y.mul_div(a[2], val);
978 let dy = x.mul_div(a[1], val) + y.mul_div(a[3], val);
979 [xx, yx, xy, yy, dx, dy]
980}
981
982#[cfg(test)]
983mod tests {
984 use super::{super::pen::SvgPen, *};
985 use crate::{
986 outline::{HintingInstance, HintingOptions},
987 prelude::{LocationRef, Size},
988 MetadataProvider,
989 };
990 use dict::Blues;
991 use font_test_data::bebuffer::BeBuffer;
992 use raw::tables::cff2::Cff2;
993 use read_fonts::FontRef;
994
995 #[test]
996 fn unscaled_scaling_sink_produces_integers() {
997 let nothing = &mut ();
998 let sink = ScalingSink26Dot6::new(nothing, None);
999 for coord in [50.0, 50.1, 50.125, 50.5, 50.9] {
1000 assert_eq!(sink.scale(Fixed::from_f64(coord)).to_f32(), 50.0);
1001 }
1002 }
1003
1004 #[test]
1005 fn scaled_scaling_sink() {
1006 let ppem = 20.0;
1007 let upem = 1000.0;
1008 let scale = Fixed::from_bits((ppem * 64.) as i32) / Fixed::from_bits(upem as i32);
1010 let nothing = &mut ();
1011 let sink = ScalingSink26Dot6::new(nothing, Some(scale));
1012 let inputs = [
1013 (0.0, 0.0),
1015 (8.0, 0.15625),
1016 (16.0, 0.3125),
1017 (32.0, 0.640625),
1018 (72.0, 1.4375),
1019 (128.0, 2.5625),
1020 ];
1021 for (coord, expected) in inputs {
1022 assert_eq!(
1023 sink.scale(Fixed::from_f64(coord)).to_f32(),
1024 expected,
1025 "scaling coord {coord}"
1026 );
1027 }
1028 }
1029
1030 #[test]
1031 fn read_cff_static() {
1032 let font = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED).unwrap();
1033 let cff = Outlines::new(&font).unwrap();
1034 assert!(!cff.is_cff2());
1035 assert!(cff.top_dict.var_store.is_none());
1036 assert!(cff.top_dict.font_dicts.count() == 0);
1037 assert!(!cff.top_dict.private_dict_range.is_empty());
1038 assert!(cff.top_dict.fd_select.is_none());
1039 assert_eq!(cff.subfont_count(), 1);
1040 assert_eq!(cff.subfont_index(GlyphId::new(1)), 0);
1041 assert_eq!(cff.global_subrs.count(), 17);
1042 }
1043
1044 #[test]
1045 fn read_cff2_static() {
1046 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
1047 let cff = Outlines::new(&font).unwrap();
1048 assert!(cff.is_cff2());
1049 assert!(cff.top_dict.var_store.is_some());
1050 assert!(cff.top_dict.font_dicts.count() != 0);
1051 assert!(cff.top_dict.private_dict_range.is_empty());
1052 assert!(cff.top_dict.fd_select.is_none());
1053 assert_eq!(cff.subfont_count(), 1);
1054 assert_eq!(cff.subfont_index(GlyphId::new(1)), 0);
1055 assert_eq!(cff.global_subrs.count(), 0);
1056 }
1057
1058 #[test]
1059 fn read_example_cff2_table() {
1060 let cff2 = Cff2::read(FontData::new(font_test_data::cff2::EXAMPLE)).unwrap();
1061 let top_dict =
1062 TopDict::new(cff2.offset_data().as_bytes(), cff2.top_dict_data(), true).unwrap();
1063 assert!(top_dict.var_store.is_some());
1064 assert!(top_dict.font_dicts.count() != 0);
1065 assert!(top_dict.private_dict_range.is_empty());
1066 assert!(top_dict.fd_select.is_none());
1067 assert_eq!(cff2.global_subrs().count(), 0);
1068 }
1069
1070 #[test]
1071 fn cff2_variable_outlines_match_freetype() {
1072 compare_glyphs(
1073 font_test_data::CANTARELL_VF_TRIMMED,
1074 font_test_data::CANTARELL_VF_TRIMMED_GLYPHS,
1075 );
1076 }
1077
1078 #[test]
1079 fn cff_static_outlines_match_freetype() {
1080 compare_glyphs(
1081 font_test_data::NOTO_SERIF_DISPLAY_TRIMMED,
1082 font_test_data::NOTO_SERIF_DISPLAY_TRIMMED_GLYPHS,
1083 );
1084 }
1085
1086 #[test]
1087 fn unhinted_ends_with_close() {
1088 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
1089 let glyph = font.outline_glyphs().get(GlyphId::new(1)).unwrap();
1090 let mut svg = SvgPen::default();
1091 glyph.draw(Size::unscaled(), &mut svg).unwrap();
1092 assert!(svg.to_string().ends_with('Z'));
1093 }
1094
1095 #[test]
1096 fn hinted_ends_with_close() {
1097 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
1098 let glyphs = font.outline_glyphs();
1099 let hinter = HintingInstance::new(
1100 &glyphs,
1101 Size::unscaled(),
1102 LocationRef::default(),
1103 HintingOptions::default(),
1104 )
1105 .unwrap();
1106 let glyph = glyphs.get(GlyphId::new(1)).unwrap();
1107 let mut svg = SvgPen::default();
1108 glyph.draw(&hinter, &mut svg).unwrap();
1109 assert!(svg.to_string().ends_with('Z'));
1110 }
1111
1112 #[test]
1114 fn empty_private_dict() {
1115 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET).unwrap();
1116 let outlines = super::Outlines::new(&font).unwrap();
1117 assert!(outlines.top_dict.private_dict_range.is_empty());
1118 assert!(outlines
1119 .parse_font_dict(0)
1120 .unwrap()
1121 .private_dict_range
1122 .is_empty());
1123 }
1124
1125 #[test]
1128 fn subrs_offset_overflow() {
1129 let private_dict = BeBuffer::new()
1131 .push(0u32) .push(29u8) .push(-1i32) .push(19u8) .to_vec();
1136 assert!(
1138 PrivateDict::new(FontData::new(&private_dict), 4..private_dict.len(), None).is_err()
1139 );
1140 }
1141
1142 #[test]
1146 fn top_dict_ivs_offset_overflow() {
1147 let top_dict = BeBuffer::new()
1150 .push(29u8) .push(-1i32) .push(24u8) .to_vec();
1154 assert!(TopDict::new(&[], &top_dict, true).is_err());
1156 }
1157
1158 #[test]
1165 fn proper_scaling_when_factor_equals_fixed_one() {
1166 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET).unwrap();
1167 assert_eq!(font.head().unwrap().units_per_em(), 512);
1168 let glyphs = font.outline_glyphs();
1169 let glyph = glyphs.get(GlyphId::new(1)).unwrap();
1170 let mut svg = SvgPen::with_precision(6);
1171 glyph
1172 .draw((Size::new(8.0), LocationRef::default()), &mut svg)
1173 .unwrap();
1174 assert!(svg.starts_with("M6.328125,7.000000 L1.671875,7.000000"));
1176 }
1177
1178 fn compare_glyphs(font_data: &[u8], expected_outlines: &str) {
1185 use super::super::testing;
1186 let font = FontRef::new(font_data).unwrap();
1187 let expected_outlines = testing::parse_glyph_outlines(expected_outlines);
1188 let outlines = super::Outlines::new(&font).unwrap();
1189 let mut path = testing::Path::default();
1190 for expected_outline in &expected_outlines {
1191 if expected_outline.size == 0.0 && !expected_outline.coords.is_empty() {
1192 continue;
1193 }
1194 let size = (expected_outline.size != 0.0).then_some(expected_outline.size);
1195 path.elements.clear();
1196 let subfont = outlines
1197 .subfont(
1198 outlines.subfont_index(expected_outline.glyph_id),
1199 size,
1200 &expected_outline.coords,
1201 )
1202 .unwrap();
1203 outlines
1204 .draw(
1205 &subfont,
1206 expected_outline.glyph_id,
1207 &expected_outline.coords,
1208 false,
1209 &mut path,
1210 )
1211 .unwrap();
1212 if path.elements != expected_outline.path {
1213 panic!(
1214 "mismatch in glyph path for id {} (size: {}, coords: {:?}): path: {:?} expected_path: {:?}",
1215 expected_outline.glyph_id,
1216 expected_outline.size,
1217 expected_outline.coords,
1218 &path.elements,
1219 &expected_outline.path
1220 );
1221 }
1222 }
1223 }
1224
1225 #[test]
1227 fn capture_family_other_blues() {
1228 let private_dict_data = &font_test_data::cff2::EXAMPLE[0x4f..=0xc0];
1229 let store =
1230 ItemVariationStore::read(FontData::new(&font_test_data::cff2::EXAMPLE[18..])).unwrap();
1231 let coords = &[F2Dot14::from_f32(0.0)];
1232 let blend_state = BlendState::new(store, coords, 0).unwrap();
1233 let private_dict = PrivateDict::new(
1234 FontData::new(private_dict_data),
1235 0..private_dict_data.len(),
1236 Some(blend_state),
1237 )
1238 .unwrap();
1239 assert_eq!(
1240 private_dict.hint_params.family_other_blues,
1241 Blues::new([-249.0, -239.0].map(Fixed::from_f64).into_iter())
1242 )
1243 }
1244
1245 #[test]
1246 fn implied_seac() {
1247 let font = FontRef::new(font_test_data::CHARSTRING_PATH_OPS).unwrap();
1248 let glyphs = font.outline_glyphs();
1249 let gid = GlyphId::new(3);
1250 assert_eq!(font.glyph_names().get(gid).unwrap(), "Scaron");
1251 let glyph = glyphs.get(gid).unwrap();
1252 let mut pen = SvgPen::new();
1253 glyph
1254 .draw((Size::unscaled(), LocationRef::default()), &mut pen)
1255 .unwrap();
1256 assert_eq!(pen.to_string().chars().filter(|ch| *ch == 'Z').count(), 2);
1261 }
1262
1263 #[test]
1264 fn implied_seac_clears_hints() {
1265 let font = FontRef::new(font_test_data::CHARSTRING_PATH_OPS).unwrap();
1266 let outlines = Outlines::from_cff(&font, 1000).unwrap();
1267 let subfont = outlines.subfont(0, Some(16.0), &[]).unwrap();
1268 let cff_data = outlines.offset_data.as_bytes();
1269 let charstrings = outlines.top_dict.charstrings.clone();
1270 let charstring_data = charstrings.get(3).unwrap();
1271 let subrs = subfont.subrs(&outlines).unwrap();
1272 let blend_state = None;
1273 let cs_eval = CharstringEvaluator {
1274 cff_data,
1275 charstrings,
1276 global_subrs: outlines.global_subrs.clone(),
1277 subrs,
1278 blend_state,
1279 charstring_data,
1280 };
1281 struct ClearHintsCountingSink(u32);
1282 impl CommandSink for ClearHintsCountingSink {
1283 fn move_to(&mut self, _: Fixed, _: Fixed) {}
1284 fn line_to(&mut self, _: Fixed, _: Fixed) {}
1285 fn curve_to(&mut self, _: Fixed, _: Fixed, _: Fixed, _: Fixed, _: Fixed, _: Fixed) {}
1286 fn close(&mut self) {}
1287 fn clear_hints(&mut self) {
1288 self.0 += 1;
1289 }
1290 }
1291 let mut sink = ClearHintsCountingSink(0);
1292 cs_eval.evaluate(&mut sink).unwrap();
1293 assert_eq!(sink.0, 2);
1296 }
1297
1298 const TRANSFORM: [Fixed; 6] = [
1299 Fixed::ONE,
1300 Fixed::ZERO,
1301 Fixed::from_bits(10945),
1303 Fixed::ONE,
1304 Fixed::ZERO,
1305 Fixed::ZERO,
1306 ];
1307
1308 #[test]
1309 fn hinted_transform_sink() {
1310 let input = [(383i32, 117i32), (450, 20), (555, -34), (683, -34)]
1313 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1314 let expected = [(403, 117i32), (453, 20), (549, -34), (677, -34)]
1315 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1316 let mut dummy = ();
1317 let sink = HintedTransformingSink::new(&mut dummy, TRANSFORM);
1318 let transformed = input.map(|(x, y)| sink.transform(x, y));
1319 assert_eq!(transformed, expected);
1320 }
1321
1322 #[test]
1323 fn unhinted_scaled_transform_sink() {
1324 let input = [(150i32, 46i32), (176, 8), (217, -13), (267, -13)]
1327 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1328 let expected = [(404, 118i32), (453, 20), (550, -33), (678, -33)]
1329 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1330 let mut dummy = ();
1331 let sink =
1332 ScalingTransformingSink::new(&mut dummy, TRANSFORM, Some(Fixed::from_bits(167772)));
1333 let transformed = input.map(|(x, y)| sink.transform(x, y));
1334 assert_eq!(transformed, expected);
1335 }
1336
1337 #[test]
1338 fn unhinted_unscaled_transform_sink() {
1339 let input = [(150i32, 46i32), (176, 8), (217, -13), (267, -13)]
1342 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1343 let expected = [(158, 46i32), (177, 8), (215, -13), (265, -13)]
1344 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1345 let mut dummy = ();
1346 let sink = ScalingTransformingSink::new(&mut dummy, TRANSFORM, None);
1347 let transformed = input.map(|(x, y)| sink.transform(x, y));
1348 assert_eq!(transformed, expected);
1349 }
1350
1351 #[test]
1352 fn fixed_matrix_mul() {
1353 let a = [0.5, 0.75, -1.0, 2.0, 0.0, 0.0].map(Fixed::from_f64);
1354 let b = [1.5, -1.0, 0.25, -1.0, 1.0, 2.0].map(Fixed::from_f64);
1355 let expected = [1.75, -0.875, 1.125, -1.8125, -1.5, 4.75].map(Fixed::from_f64);
1356 let result = matrix_mul_scaled(&a, &b, 1);
1357 assert_eq!(result, expected);
1358 }
1359
1360 #[test]
1362 fn nested_font_matrices() {
1363 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET_MATRIX).unwrap();
1365 let outlines = Outlines::from_cff(&font, 512).unwrap();
1366 let (top_matrix, top_upem) = outlines.top_dict.font_matrix.unwrap();
1368 let expected_top_matrix = [65536, 0, 5604, 65536, 0, 0].map(Fixed::from_bits);
1369 assert_eq!(top_matrix, expected_top_matrix);
1370 assert_eq!(top_upem, 512);
1371 let (sub_matrix, sub_upem) = outlines.parse_font_dict(0).unwrap().font_matrix.unwrap();
1373 let expected_sub_matrix = [327680, 0, 0, 327680, 0, 0].map(Fixed::from_bits);
1374 assert_eq!(sub_matrix, expected_sub_matrix);
1375 assert_eq!(sub_upem, 10);
1376 let subfont = outlines.subfont(0, Some(24.0), &[]).unwrap();
1378 let expected_combined_matrix = [65536, 0, 5604, 65536, 0, 0].map(Fixed::from_bits);
1379 assert_eq!(subfont.font_matrix.unwrap(), expected_combined_matrix);
1380 assert_eq!(subfont.scale.unwrap().to_bits(), 98304);
1382 }
1383
1384 #[test]
1388 fn subfont_hint_scale_overflow() {
1389 let _ = scale_for_hinting(Some(Fixed::from_bits(i32::MAX)));
1391 }
1392}