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 = Fixed::from_bits((scale.unwrap_or(Fixed::ONE).to_bits() + 32) / 64);
226 let hint_state = HintState::new(&private_dict.hint_params, hint_scale);
227 Ok(Subfont {
228 is_cff2: self.is_cff2(),
229 scale,
230 scale_requested,
231 subrs_offset: private_dict.subrs_offset,
232 hint_state,
233 store_index: private_dict.store_index,
234 font_matrix,
235 })
236 }
237
238 pub fn draw(
250 &self,
251 subfont: &Subfont,
252 glyph_id: GlyphId,
253 coords: &[F2Dot14],
254 hint: bool,
255 pen: &mut impl OutlinePen,
256 ) -> Result<(), Error> {
257 let cff_data = self.offset_data.as_bytes();
258 let charstrings = self.top_dict.charstrings.clone();
259 let charstring_data = charstrings.get(glyph_id.to_u32() as usize)?;
260 let subrs = subfont.subrs(self)?;
261 let blend_state = subfont.blend_state(self, coords)?;
262 let cs_eval = CharstringEvaluator {
263 cff_data,
264 charstrings,
265 global_subrs: self.global_subrs.clone(),
266 subrs,
267 blend_state,
268 charstring_data,
269 };
270 let apply_hinting = hint && subfont.scale_requested;
272 let mut pen_sink = PenSink::new(pen);
273 let mut simplifying_adapter = NopFilteringSink::new(&mut pen_sink);
274 if let Some(matrix) = subfont.font_matrix {
275 if apply_hinting {
276 let mut transform_sink =
277 HintedTransformingSink::new(&mut simplifying_adapter, matrix);
278 let mut hinting_adapter =
279 HintingSink::new(&subfont.hint_state, &mut transform_sink);
280 cs_eval.evaluate(&mut hinting_adapter)?;
281 hinting_adapter.finish();
282 } else {
283 let mut transform_sink =
284 ScalingTransformingSink::new(&mut simplifying_adapter, matrix, subfont.scale);
285 cs_eval.evaluate(&mut transform_sink)?;
286 }
287 } else if apply_hinting {
288 let mut hinting_adapter =
289 HintingSink::new(&subfont.hint_state, &mut simplifying_adapter);
290 cs_eval.evaluate(&mut hinting_adapter)?;
291 hinting_adapter.finish();
292 } else {
293 let mut scaling_adapter =
294 ScalingSink26Dot6::new(&mut simplifying_adapter, subfont.scale);
295 cs_eval.evaluate(&mut scaling_adapter)?;
296 }
297 simplifying_adapter.finish();
298 Ok(())
299 }
300
301 fn parse_font_dict(&self, subfont_index: u32) -> Result<FontDict, Error> {
302 if self.top_dict.font_dicts.count() != 0 {
303 let font_dict_data = self.top_dict.font_dicts.get(subfont_index as usize)?;
306 FontDict::new(font_dict_data)
307 } else {
308 let range = self.top_dict.private_dict_range.clone();
313 Ok(FontDict {
314 private_dict_range: range.start as usize..range.end as usize,
315 font_matrix: None,
316 })
317 }
318 }
319}
320
321struct CharstringEvaluator<'a> {
322 cff_data: &'a [u8],
323 charstrings: Index<'a>,
324 global_subrs: Index<'a>,
325 subrs: Option<Index<'a>>,
326 blend_state: Option<BlendState<'a>>,
327 charstring_data: &'a [u8],
328}
329
330impl CharstringEvaluator<'_> {
331 fn evaluate(self, sink: &mut impl CommandSink) -> Result<(), Error> {
332 charstring::evaluate(
333 self.cff_data,
334 self.charstrings,
335 self.global_subrs,
336 self.subrs,
337 self.blend_state,
338 self.charstring_data,
339 sink,
340 )
341 }
342}
343
344#[derive(Clone)]
352pub(crate) struct Subfont {
353 is_cff2: bool,
354 scale: Option<Fixed>,
355 scale_requested: bool,
359 subrs_offset: Option<usize>,
360 pub(crate) hint_state: HintState,
361 store_index: u16,
362 font_matrix: Option<[Fixed; 6]>,
363}
364
365impl Subfont {
366 pub fn subrs<'a>(&self, scaler: &Outlines<'a>) -> Result<Option<Index<'a>>, Error> {
368 if let Some(subrs_offset) = self.subrs_offset {
369 let offset_data = scaler.offset_data.as_bytes();
370 let index_data = offset_data.get(subrs_offset..).unwrap_or_default();
371 Ok(Some(Index::new(index_data, self.is_cff2)?))
372 } else {
373 Ok(None)
374 }
375 }
376
377 pub fn blend_state<'a>(
380 &self,
381 scaler: &Outlines<'a>,
382 coords: &'a [F2Dot14],
383 ) -> Result<Option<BlendState<'a>>, Error> {
384 if let Some(var_store) = scaler.top_dict.var_store.clone() {
385 Ok(Some(BlendState::new(var_store, coords, self.store_index)?))
386 } else {
387 Ok(None)
388 }
389 }
390}
391
392#[derive(Default)]
395struct PrivateDict {
396 hint_params: HintParams,
397 subrs_offset: Option<usize>,
398 store_index: u16,
399}
400
401impl PrivateDict {
402 fn new(
403 data: FontData,
404 range: Range<usize>,
405 blend_state: Option<BlendState<'_>>,
406 ) -> Result<Self, Error> {
407 let private_dict_data = data.read_array(range.clone())?;
408 let mut dict = Self::default();
409 for entry in dict::entries(private_dict_data, blend_state) {
410 use dict::Entry::*;
411 match entry? {
412 BlueValues(values) => dict.hint_params.blues = values,
413 FamilyBlues(values) => dict.hint_params.family_blues = values,
414 OtherBlues(values) => dict.hint_params.other_blues = values,
415 FamilyOtherBlues(values) => dict.hint_params.family_other_blues = values,
416 BlueScale(value) => dict.hint_params.blue_scale = value,
417 BlueShift(value) => dict.hint_params.blue_shift = value,
418 BlueFuzz(value) => dict.hint_params.blue_fuzz = value,
419 LanguageGroup(group) => dict.hint_params.language_group = group,
420 SubrsOffset(offset) => {
422 dict.subrs_offset = Some(
423 range
424 .start
425 .checked_add(offset)
426 .ok_or(ReadError::OutOfBounds)?,
427 )
428 }
429 VariationStoreIndex(index) => dict.store_index = index,
430 _ => {}
431 }
432 }
433 Ok(dict)
434 }
435}
436
437#[derive(Clone, Default)]
439struct FontDict {
440 private_dict_range: Range<usize>,
441 font_matrix: Option<([Fixed; 6], i32)>,
442}
443
444impl FontDict {
445 fn new(font_dict_data: &[u8]) -> Result<Self, Error> {
446 let mut range = None;
447 let mut font_matrix = None;
448 for entry in dict::entries(font_dict_data, None) {
449 match entry? {
450 dict::Entry::PrivateDictRange(r) => {
451 range = Some(r);
452 }
453 dict::Entry::FontMatrix(matrix, upem) => font_matrix = Some((matrix, upem)),
457 _ => {}
458 }
459 }
460 Ok(Self {
461 private_dict_range: range.ok_or(Error::MissingPrivateDict)?,
462 font_matrix,
463 })
464 }
465}
466
467#[derive(Clone, Default)]
470struct TopDict<'a> {
471 charstrings: Index<'a>,
472 font_dicts: Index<'a>,
473 fd_select: Option<FdSelect<'a>>,
474 private_dict_range: Range<u32>,
475 font_matrix: Option<([Fixed; 6], i32)>,
476 var_store: Option<ItemVariationStore<'a>>,
477}
478
479impl<'a> TopDict<'a> {
480 fn new(table_data: &'a [u8], top_dict_data: &'a [u8], is_cff2: bool) -> Result<Self, Error> {
481 let mut items = TopDict::default();
482 for entry in dict::entries(top_dict_data, None) {
483 match entry? {
484 dict::Entry::CharstringsOffset(offset) => {
485 items.charstrings =
486 Index::new(table_data.get(offset..).unwrap_or_default(), is_cff2)?;
487 }
488 dict::Entry::FdArrayOffset(offset) => {
489 items.font_dicts =
490 Index::new(table_data.get(offset..).unwrap_or_default(), is_cff2)?;
491 }
492 dict::Entry::FdSelectOffset(offset) => {
493 items.fd_select = Some(FdSelect::read(FontData::new(
494 table_data.get(offset..).unwrap_or_default(),
495 ))?);
496 }
497 dict::Entry::PrivateDictRange(range) => {
498 items.private_dict_range = range.start as u32..range.end as u32;
499 }
500 dict::Entry::FontMatrix(matrix, upem) => {
501 items.font_matrix = Some(normalize_font_matrix(matrix, upem));
503 }
504 dict::Entry::VariationStoreOffset(offset) if is_cff2 => {
505 let offset = offset.checked_add(2).ok_or(ReadError::OutOfBounds)?;
509 items.var_store = Some(ItemVariationStore::read(FontData::new(
510 table_data.get(offset..).unwrap_or_default(),
511 ))?);
512 }
513 _ => {}
514 }
515 }
516 Ok(items)
517 }
518}
519
520struct PenSink<'a, P>(&'a mut P);
523
524impl<'a, P> PenSink<'a, P> {
525 fn new(pen: &'a mut P) -> Self {
526 Self(pen)
527 }
528}
529
530impl<P> CommandSink for PenSink<'_, P>
531where
532 P: OutlinePen,
533{
534 fn move_to(&mut self, x: Fixed, y: Fixed) {
535 self.0.move_to(x.to_f32(), y.to_f32());
536 }
537
538 fn line_to(&mut self, x: Fixed, y: Fixed) {
539 self.0.line_to(x.to_f32(), y.to_f32());
540 }
541
542 fn curve_to(&mut self, cx0: Fixed, cy0: Fixed, cx1: Fixed, cy1: Fixed, x: Fixed, y: Fixed) {
543 self.0.curve_to(
544 cx0.to_f32(),
545 cy0.to_f32(),
546 cx1.to_f32(),
547 cy1.to_f32(),
548 x.to_f32(),
549 y.to_f32(),
550 );
551 }
552
553 fn close(&mut self) {
554 self.0.close();
555 }
556}
557
558fn transform(matrix: &[Fixed; 6], x: Fixed, y: Fixed) -> (Fixed, Fixed) {
559 (
560 matrix[0] * x + matrix[2] * y + matrix[4],
561 matrix[1] * x + matrix[3] * y + matrix[5],
562 )
563}
564
565struct HintedTransformingSink<'a, S> {
567 inner: &'a mut S,
568 matrix: [Fixed; 6],
569}
570
571impl<'a, S> HintedTransformingSink<'a, S> {
572 fn new(sink: &'a mut S, matrix: [Fixed; 6]) -> Self {
573 Self {
574 inner: sink,
575 matrix,
576 }
577 }
578
579 fn transform(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
580 let (x, y) = transform(
583 &self.matrix,
584 Fixed::from_bits(x.to_bits() >> 10),
585 Fixed::from_bits(y.to_bits() >> 10),
586 );
587 (
588 Fixed::from_bits(x.to_bits() << 10),
589 Fixed::from_bits(y.to_bits() << 10),
590 )
591 }
592}
593
594impl<S: CommandSink> CommandSink for HintedTransformingSink<'_, S> {
595 fn hstem(&mut self, y: Fixed, dy: Fixed) {
596 self.inner.hstem(y, dy);
597 }
598
599 fn vstem(&mut self, x: Fixed, dx: Fixed) {
600 self.inner.vstem(x, dx);
601 }
602
603 fn hint_mask(&mut self, mask: &[u8]) {
604 self.inner.hint_mask(mask);
605 }
606
607 fn counter_mask(&mut self, mask: &[u8]) {
608 self.inner.counter_mask(mask);
609 }
610
611 fn move_to(&mut self, x: Fixed, y: Fixed) {
612 let (x, y) = self.transform(x, y);
613 self.inner.move_to(x, y);
614 }
615
616 fn line_to(&mut self, x: Fixed, y: Fixed) {
617 let (x, y) = self.transform(x, y);
618 self.inner.line_to(x, y);
619 }
620
621 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
622 let (cx1, cy1) = self.transform(cx1, cy1);
623 let (cx2, cy2) = self.transform(cx2, cy2);
624 let (x, y) = self.transform(x, y);
625 self.inner.curve_to(cx1, cy1, cx2, cy2, x, y);
626 }
627
628 fn close(&mut self) {
629 self.inner.close();
630 }
631}
632
633const ONE_OVER_64: Fixed = Fixed::from_bits(0x400);
635
636struct ScalingTransformingSink<'a, S> {
639 inner: &'a mut S,
640 matrix: [Fixed; 6],
641 scale: Option<Fixed>,
642}
643
644impl<'a, S> ScalingTransformingSink<'a, S> {
645 fn new(sink: &'a mut S, matrix: [Fixed; 6], scale: Option<Fixed>) -> Self {
646 Self {
647 inner: sink,
648 matrix,
649 scale,
650 }
651 }
652
653 fn transform(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
654 let ax = x * ONE_OVER_64;
664 let ay = y * ONE_OVER_64;
665 let bx = Fixed::from_bits(ax.to_bits() >> 10);
669 let by = Fixed::from_bits(ay.to_bits() >> 10);
670 let (cx, cy) = transform(&self.matrix, bx, by);
672 if let Some(scale) = self.scale {
673 let dx = cx * scale;
677 let dy = cy * scale;
678 (
680 Fixed::from_bits(dx.to_bits() << 10),
681 Fixed::from_bits(dy.to_bits() << 10),
682 )
683 } else {
684 (
687 Fixed::from_bits(cx.to_bits() << 16),
688 Fixed::from_bits(cy.to_bits() << 16),
689 )
690 }
691 }
692}
693
694impl<S: CommandSink> CommandSink for ScalingTransformingSink<'_, S> {
695 fn hstem(&mut self, y: Fixed, dy: Fixed) {
696 self.inner.hstem(y, dy);
697 }
698
699 fn vstem(&mut self, x: Fixed, dx: Fixed) {
700 self.inner.vstem(x, dx);
701 }
702
703 fn hint_mask(&mut self, mask: &[u8]) {
704 self.inner.hint_mask(mask);
705 }
706
707 fn counter_mask(&mut self, mask: &[u8]) {
708 self.inner.counter_mask(mask);
709 }
710
711 fn move_to(&mut self, x: Fixed, y: Fixed) {
712 let (x, y) = self.transform(x, y);
713 self.inner.move_to(x, y);
714 }
715
716 fn line_to(&mut self, x: Fixed, y: Fixed) {
717 let (x, y) = self.transform(x, y);
718 self.inner.line_to(x, y);
719 }
720
721 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
722 let (cx1, cy1) = self.transform(cx1, cy1);
723 let (cx2, cy2) = self.transform(cx2, cy2);
724 let (x, y) = self.transform(x, y);
725 self.inner.curve_to(cx1, cy1, cx2, cy2, x, y);
726 }
727
728 fn close(&mut self) {
729 self.inner.close();
730 }
731}
732
733struct ScalingSink26Dot6<'a, S> {
739 inner: &'a mut S,
740 scale: Option<Fixed>,
741}
742
743impl<'a, S> ScalingSink26Dot6<'a, S> {
744 fn new(sink: &'a mut S, scale: Option<Fixed>) -> Self {
745 Self { scale, inner: sink }
746 }
747
748 fn scale(&self, coord: Fixed) -> Fixed {
749 let a = coord * ONE_OVER_64;
759 let b = Fixed::from_bits(a.to_bits() >> 10);
763 if let Some(scale) = self.scale {
764 let c = b * scale;
768 Fixed::from_bits(c.to_bits() << 10)
770 } else {
771 Fixed::from_bits(b.to_bits() << 16)
774 }
775 }
776}
777
778impl<S: CommandSink> CommandSink for ScalingSink26Dot6<'_, S> {
779 fn hstem(&mut self, y: Fixed, dy: Fixed) {
780 self.inner.hstem(y, dy);
781 }
782
783 fn vstem(&mut self, x: Fixed, dx: Fixed) {
784 self.inner.vstem(x, dx);
785 }
786
787 fn hint_mask(&mut self, mask: &[u8]) {
788 self.inner.hint_mask(mask);
789 }
790
791 fn counter_mask(&mut self, mask: &[u8]) {
792 self.inner.counter_mask(mask);
793 }
794
795 fn move_to(&mut self, x: Fixed, y: Fixed) {
796 self.inner.move_to(self.scale(x), self.scale(y));
797 }
798
799 fn line_to(&mut self, x: Fixed, y: Fixed) {
800 self.inner.line_to(self.scale(x), self.scale(y));
801 }
802
803 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
804 self.inner.curve_to(
805 self.scale(cx1),
806 self.scale(cy1),
807 self.scale(cx2),
808 self.scale(cy2),
809 self.scale(x),
810 self.scale(y),
811 );
812 }
813
814 fn close(&mut self) {
815 self.inner.close();
816 }
817}
818
819#[derive(Copy, Clone)]
820enum PendingElement {
821 Move([Fixed; 2]),
822 Line([Fixed; 2]),
823 Curve([Fixed; 6]),
824}
825
826impl PendingElement {
827 fn target_point(&self) -> [Fixed; 2] {
828 match self {
829 Self::Move(xy) | Self::Line(xy) => *xy,
830 Self::Curve([.., x, y]) => [*x, *y],
831 }
832 }
833}
834
835struct NopFilteringSink<'a, S> {
844 is_open: bool,
845 start: Option<(Fixed, Fixed)>,
846 pending_element: Option<PendingElement>,
847 inner: &'a mut S,
848}
849
850impl<'a, S> NopFilteringSink<'a, S>
851where
852 S: CommandSink,
853{
854 fn new(inner: &'a mut S) -> Self {
855 Self {
856 is_open: false,
857 start: None,
858 pending_element: None,
859 inner,
860 }
861 }
862
863 fn flush_pending(&mut self, for_close: bool) {
864 if let Some(pending) = self.pending_element.take() {
865 match pending {
866 PendingElement::Move([x, y]) => {
867 if !for_close {
868 self.is_open = true;
869 self.inner.move_to(x, y);
870 self.start = Some((x, y));
871 }
872 }
873 PendingElement::Line([x, y]) => {
874 if !for_close || self.start != Some((x, y)) {
875 self.inner.line_to(x, y);
876 }
877 }
878 PendingElement::Curve([cx0, cy0, cx1, cy1, x, y]) => {
879 self.inner.curve_to(cx0, cy0, cx1, cy1, x, y);
880 }
881 }
882 }
883 }
884
885 pub fn finish(&mut self) {
886 self.close();
887 }
888}
889
890impl<S> CommandSink for NopFilteringSink<'_, S>
891where
892 S: CommandSink,
893{
894 fn hstem(&mut self, y: Fixed, dy: Fixed) {
895 self.inner.hstem(y, dy);
896 }
897
898 fn vstem(&mut self, x: Fixed, dx: Fixed) {
899 self.inner.vstem(x, dx);
900 }
901
902 fn hint_mask(&mut self, mask: &[u8]) {
903 self.inner.hint_mask(mask);
904 }
905
906 fn counter_mask(&mut self, mask: &[u8]) {
907 self.inner.counter_mask(mask);
908 }
909
910 fn move_to(&mut self, x: Fixed, y: Fixed) {
911 self.pending_element = Some(PendingElement::Move([x, y]));
912 }
913
914 fn line_to(&mut self, x: Fixed, y: Fixed) {
915 if self
917 .pending_element
918 .map(|element| element.target_point() == [x, y])
919 .unwrap_or_default()
920 {
921 return;
922 }
923 self.flush_pending(false);
924 self.pending_element = Some(PendingElement::Line([x, y]));
925 }
926
927 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
928 self.flush_pending(false);
929 self.pending_element = Some(PendingElement::Curve([cx1, cy1, cx2, cy2, x, y]));
930 }
931
932 fn close(&mut self) {
933 self.flush_pending(true);
934 if self.is_open {
935 self.inner.close();
936 self.is_open = false;
937 }
938 }
939}
940
941fn matrix_mul_scaled(a: &[Fixed; 6], b: &[Fixed; 6], scaling: i32) -> [Fixed; 6] {
949 let val = Fixed::from_i32(scaling);
950 let xx = a[0].mul_div(b[0], val) + a[2].mul_div(b[1], val);
951 let yx = a[1].mul_div(b[0], val) + a[3].mul_div(b[1], val);
952 let xy = a[0].mul_div(b[2], val) + a[2].mul_div(b[3], val);
953 let yy = a[1].mul_div(b[2], val) + a[3].mul_div(b[3], val);
954 let x = b[4];
955 let y = b[5];
956 let dx = x.mul_div(a[0], val) + y.mul_div(a[2], val);
957 let dy = x.mul_div(a[1], val) + y.mul_div(a[3], val);
958 [xx, yx, xy, yy, dx, dy]
959}
960
961#[cfg(test)]
962mod tests {
963 use super::{super::pen::SvgPen, *};
964 use crate::{
965 outline::{HintingInstance, HintingOptions},
966 prelude::{LocationRef, Size},
967 MetadataProvider,
968 };
969 use dict::Blues;
970 use font_test_data::bebuffer::BeBuffer;
971 use raw::tables::cff2::Cff2;
972 use read_fonts::FontRef;
973
974 #[test]
975 fn unscaled_scaling_sink_produces_integers() {
976 let nothing = &mut ();
977 let sink = ScalingSink26Dot6::new(nothing, None);
978 for coord in [50.0, 50.1, 50.125, 50.5, 50.9] {
979 assert_eq!(sink.scale(Fixed::from_f64(coord)).to_f32(), 50.0);
980 }
981 }
982
983 #[test]
984 fn scaled_scaling_sink() {
985 let ppem = 20.0;
986 let upem = 1000.0;
987 let scale = Fixed::from_bits((ppem * 64.) as i32) / Fixed::from_bits(upem as i32);
989 let nothing = &mut ();
990 let sink = ScalingSink26Dot6::new(nothing, Some(scale));
991 let inputs = [
992 (0.0, 0.0),
994 (8.0, 0.15625),
995 (16.0, 0.3125),
996 (32.0, 0.640625),
997 (72.0, 1.4375),
998 (128.0, 2.5625),
999 ];
1000 for (coord, expected) in inputs {
1001 assert_eq!(
1002 sink.scale(Fixed::from_f64(coord)).to_f32(),
1003 expected,
1004 "scaling coord {coord}"
1005 );
1006 }
1007 }
1008
1009 #[test]
1010 fn read_cff_static() {
1011 let font = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED).unwrap();
1012 let cff = Outlines::new(&font).unwrap();
1013 assert!(!cff.is_cff2());
1014 assert!(cff.top_dict.var_store.is_none());
1015 assert!(cff.top_dict.font_dicts.count() == 0);
1016 assert!(!cff.top_dict.private_dict_range.is_empty());
1017 assert!(cff.top_dict.fd_select.is_none());
1018 assert_eq!(cff.subfont_count(), 1);
1019 assert_eq!(cff.subfont_index(GlyphId::new(1)), 0);
1020 assert_eq!(cff.global_subrs.count(), 17);
1021 }
1022
1023 #[test]
1024 fn read_cff2_static() {
1025 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
1026 let cff = Outlines::new(&font).unwrap();
1027 assert!(cff.is_cff2());
1028 assert!(cff.top_dict.var_store.is_some());
1029 assert!(cff.top_dict.font_dicts.count() != 0);
1030 assert!(cff.top_dict.private_dict_range.is_empty());
1031 assert!(cff.top_dict.fd_select.is_none());
1032 assert_eq!(cff.subfont_count(), 1);
1033 assert_eq!(cff.subfont_index(GlyphId::new(1)), 0);
1034 assert_eq!(cff.global_subrs.count(), 0);
1035 }
1036
1037 #[test]
1038 fn read_example_cff2_table() {
1039 let cff2 = Cff2::read(FontData::new(font_test_data::cff2::EXAMPLE)).unwrap();
1040 let top_dict =
1041 TopDict::new(cff2.offset_data().as_bytes(), cff2.top_dict_data(), true).unwrap();
1042 assert!(top_dict.var_store.is_some());
1043 assert!(top_dict.font_dicts.count() != 0);
1044 assert!(top_dict.private_dict_range.is_empty());
1045 assert!(top_dict.fd_select.is_none());
1046 assert_eq!(cff2.global_subrs().count(), 0);
1047 }
1048
1049 #[test]
1050 fn cff2_variable_outlines_match_freetype() {
1051 compare_glyphs(
1052 font_test_data::CANTARELL_VF_TRIMMED,
1053 font_test_data::CANTARELL_VF_TRIMMED_GLYPHS,
1054 );
1055 }
1056
1057 #[test]
1058 fn cff_static_outlines_match_freetype() {
1059 compare_glyphs(
1060 font_test_data::NOTO_SERIF_DISPLAY_TRIMMED,
1061 font_test_data::NOTO_SERIF_DISPLAY_TRIMMED_GLYPHS,
1062 );
1063 }
1064
1065 #[test]
1066 fn unhinted_ends_with_close() {
1067 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
1068 let glyph = font.outline_glyphs().get(GlyphId::new(1)).unwrap();
1069 let mut svg = SvgPen::default();
1070 glyph.draw(Size::unscaled(), &mut svg).unwrap();
1071 assert!(svg.to_string().ends_with('Z'));
1072 }
1073
1074 #[test]
1075 fn hinted_ends_with_close() {
1076 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
1077 let glyphs = font.outline_glyphs();
1078 let hinter = HintingInstance::new(
1079 &glyphs,
1080 Size::unscaled(),
1081 LocationRef::default(),
1082 HintingOptions::default(),
1083 )
1084 .unwrap();
1085 let glyph = glyphs.get(GlyphId::new(1)).unwrap();
1086 let mut svg = SvgPen::default();
1087 glyph.draw(&hinter, &mut svg).unwrap();
1088 assert!(svg.to_string().ends_with('Z'));
1089 }
1090
1091 #[test]
1093 fn empty_private_dict() {
1094 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET).unwrap();
1095 let outlines = super::Outlines::new(&font).unwrap();
1096 assert!(outlines.top_dict.private_dict_range.is_empty());
1097 assert!(outlines
1098 .parse_font_dict(0)
1099 .unwrap()
1100 .private_dict_range
1101 .is_empty());
1102 }
1103
1104 #[test]
1107 fn subrs_offset_overflow() {
1108 let private_dict = BeBuffer::new()
1110 .push(0u32) .push(29u8) .push(-1i32) .push(19u8) .to_vec();
1115 assert!(
1117 PrivateDict::new(FontData::new(&private_dict), 4..private_dict.len(), None).is_err()
1118 );
1119 }
1120
1121 #[test]
1125 fn top_dict_ivs_offset_overflow() {
1126 let top_dict = BeBuffer::new()
1129 .push(29u8) .push(-1i32) .push(24u8) .to_vec();
1133 assert!(TopDict::new(&[], &top_dict, true).is_err());
1135 }
1136
1137 #[test]
1144 fn proper_scaling_when_factor_equals_fixed_one() {
1145 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET).unwrap();
1146 assert_eq!(font.head().unwrap().units_per_em(), 512);
1147 let glyphs = font.outline_glyphs();
1148 let glyph = glyphs.get(GlyphId::new(1)).unwrap();
1149 let mut svg = SvgPen::with_precision(6);
1150 glyph
1151 .draw((Size::new(8.0), LocationRef::default()), &mut svg)
1152 .unwrap();
1153 assert!(svg.starts_with("M6.328125,7.000000 L1.671875,7.000000"));
1155 }
1156
1157 fn compare_glyphs(font_data: &[u8], expected_outlines: &str) {
1164 use super::super::testing;
1165 let font = FontRef::new(font_data).unwrap();
1166 let expected_outlines = testing::parse_glyph_outlines(expected_outlines);
1167 let outlines = super::Outlines::new(&font).unwrap();
1168 let mut path = testing::Path::default();
1169 for expected_outline in &expected_outlines {
1170 if expected_outline.size == 0.0 && !expected_outline.coords.is_empty() {
1171 continue;
1172 }
1173 let size = (expected_outline.size != 0.0).then_some(expected_outline.size);
1174 path.elements.clear();
1175 let subfont = outlines
1176 .subfont(
1177 outlines.subfont_index(expected_outline.glyph_id),
1178 size,
1179 &expected_outline.coords,
1180 )
1181 .unwrap();
1182 outlines
1183 .draw(
1184 &subfont,
1185 expected_outline.glyph_id,
1186 &expected_outline.coords,
1187 false,
1188 &mut path,
1189 )
1190 .unwrap();
1191 if path.elements != expected_outline.path {
1192 panic!(
1193 "mismatch in glyph path for id {} (size: {}, coords: {:?}): path: {:?} expected_path: {:?}",
1194 expected_outline.glyph_id,
1195 expected_outline.size,
1196 expected_outline.coords,
1197 &path.elements,
1198 &expected_outline.path
1199 );
1200 }
1201 }
1202 }
1203
1204 #[test]
1206 fn capture_family_other_blues() {
1207 let private_dict_data = &font_test_data::cff2::EXAMPLE[0x4f..=0xc0];
1208 let store =
1209 ItemVariationStore::read(FontData::new(&font_test_data::cff2::EXAMPLE[18..])).unwrap();
1210 let coords = &[F2Dot14::from_f32(0.0)];
1211 let blend_state = BlendState::new(store, coords, 0).unwrap();
1212 let private_dict = PrivateDict::new(
1213 FontData::new(private_dict_data),
1214 0..private_dict_data.len(),
1215 Some(blend_state),
1216 )
1217 .unwrap();
1218 assert_eq!(
1219 private_dict.hint_params.family_other_blues,
1220 Blues::new([-249.0, -239.0].map(Fixed::from_f64).into_iter())
1221 )
1222 }
1223
1224 #[test]
1225 fn implied_seac() {
1226 let font = FontRef::new(font_test_data::CHARSTRING_PATH_OPS).unwrap();
1227 let glyphs = font.outline_glyphs();
1228 let gid = GlyphId::new(3);
1229 assert_eq!(font.glyph_names().get(gid).unwrap(), "Scaron");
1230 let glyph = glyphs.get(gid).unwrap();
1231 let mut pen = SvgPen::new();
1232 glyph
1233 .draw((Size::unscaled(), LocationRef::default()), &mut pen)
1234 .unwrap();
1235 assert_eq!(pen.to_string().chars().filter(|ch| *ch == 'Z').count(), 2);
1240 }
1241
1242 const TRANSFORM: [Fixed; 6] = [
1243 Fixed::ONE,
1244 Fixed::ZERO,
1245 Fixed::from_bits(10945),
1247 Fixed::ONE,
1248 Fixed::ZERO,
1249 Fixed::ZERO,
1250 ];
1251
1252 #[test]
1253 fn hinted_transform_sink() {
1254 let input = [(383i32, 117i32), (450, 20), (555, -34), (683, -34)]
1257 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1258 let expected = [(403, 117i32), (453, 20), (549, -34), (677, -34)]
1259 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1260 let mut dummy = ();
1261 let sink = HintedTransformingSink::new(&mut dummy, TRANSFORM);
1262 let transformed = input.map(|(x, y)| sink.transform(x, y));
1263 assert_eq!(transformed, expected);
1264 }
1265
1266 #[test]
1267 fn unhinted_scaled_transform_sink() {
1268 let input = [(150i32, 46i32), (176, 8), (217, -13), (267, -13)]
1271 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1272 let expected = [(404, 118i32), (453, 20), (550, -33), (678, -33)]
1273 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1274 let mut dummy = ();
1275 let sink =
1276 ScalingTransformingSink::new(&mut dummy, TRANSFORM, Some(Fixed::from_bits(167772)));
1277 let transformed = input.map(|(x, y)| sink.transform(x, y));
1278 assert_eq!(transformed, expected);
1279 }
1280
1281 #[test]
1282 fn unhinted_unscaled_transform_sink() {
1283 let input = [(150i32, 46i32), (176, 8), (217, -13), (267, -13)]
1286 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1287 let expected = [(158, 46i32), (177, 8), (215, -13), (265, -13)]
1288 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1289 let mut dummy = ();
1290 let sink = ScalingTransformingSink::new(&mut dummy, TRANSFORM, None);
1291 let transformed = input.map(|(x, y)| sink.transform(x, y));
1292 assert_eq!(transformed, expected);
1293 }
1294
1295 #[test]
1296 fn fixed_matrix_mul() {
1297 let a = [0.5, 0.75, -1.0, 2.0, 0.0, 0.0].map(Fixed::from_f64);
1298 let b = [1.5, -1.0, 0.25, -1.0, 1.0, 2.0].map(Fixed::from_f64);
1299 let expected = [1.75, -0.875, 1.125, -1.8125, -1.5, 4.75].map(Fixed::from_f64);
1300 let result = matrix_mul_scaled(&a, &b, 1);
1301 assert_eq!(result, expected);
1302 }
1303
1304 #[test]
1306 fn nested_font_matrices() {
1307 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET_MATRIX).unwrap();
1309 let outlines = Outlines::from_cff(&font, 512).unwrap();
1310 let (top_matrix, top_upem) = outlines.top_dict.font_matrix.unwrap();
1312 let expected_top_matrix = [65536, 0, 5604, 65536, 0, 0].map(Fixed::from_bits);
1313 assert_eq!(top_matrix, expected_top_matrix);
1314 assert_eq!(top_upem, 512);
1315 let (sub_matrix, sub_upem) = outlines.parse_font_dict(0).unwrap().font_matrix.unwrap();
1317 let expected_sub_matrix = [327680, 0, 0, 327680, 0, 0].map(Fixed::from_bits);
1318 assert_eq!(sub_matrix, expected_sub_matrix);
1319 assert_eq!(sub_upem, 10);
1320 let subfont = outlines.subfont(0, Some(24.0), &[]).unwrap();
1322 let expected_combined_matrix = [65536, 0, 5604, 65536, 0, 0].map(Fixed::from_bits);
1323 assert_eq!(subfont.font_matrix.unwrap(), expected_combined_matrix);
1324 assert_eq!(subfont.scale.unwrap().to_bits(), 98304);
1326 }
1327}