1mod hint;
4
5use super::{GlyphHMetrics, OutlinePen};
6use hint::{HintParams, HintState, HintingSink};
7use raw::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 private_dict_range = self.private_dict_range(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 = PrivateDict::new(self.offset_data, private_dict_range, blend_state)?;
154 let scale = match size {
155 Some(ppem) if self.units_per_em > 0 => {
156 Some(
159 Fixed::from_bits((ppem * 64.) as i32)
160 / Fixed::from_bits(self.units_per_em as i32),
161 )
162 }
163 _ => None,
164 };
165 let hint_scale = Fixed::from_bits((scale.unwrap_or(Fixed::ONE).to_bits() + 32) / 64);
168 let hint_state = HintState::new(&private_dict.hint_params, hint_scale);
169 Ok(Subfont {
170 is_cff2: self.is_cff2(),
171 scale,
172 subrs_offset: private_dict.subrs_offset,
173 hint_state,
174 store_index: private_dict.store_index,
175 })
176 }
177
178 pub fn draw(
190 &self,
191 subfont: &Subfont,
192 glyph_id: GlyphId,
193 coords: &[F2Dot14],
194 hint: bool,
195 pen: &mut impl OutlinePen,
196 ) -> Result<(), Error> {
197 let cff_data = self.offset_data.as_bytes();
198 let charstrings = self.top_dict.charstrings.clone();
199 let charstring_data = charstrings.get(glyph_id.to_u32() as usize)?;
200 let subrs = subfont.subrs(self)?;
201 let blend_state = subfont.blend_state(self, coords)?;
202 let cs_eval = CharstringEvaluator {
203 cff_data,
204 charstrings,
205 global_subrs: self.global_subrs.clone(),
206 subrs,
207 blend_state,
208 charstring_data,
209 };
210 let is_scaled = subfont.scale.is_some();
211 let mut pen_sink = PenSink::new(pen);
212 let mut simplifying_adapter = NopFilteringSink::new(&mut pen_sink);
213 if let Some(matrix) = self.top_dict.font_matrix {
214 if hint && is_scaled {
216 let mut transform_sink =
217 HintedTransformingSink::new(&mut simplifying_adapter, matrix);
218 let mut hinting_adapter =
219 HintingSink::new(&subfont.hint_state, &mut transform_sink);
220 cs_eval.evaluate(&mut hinting_adapter)?;
221 hinting_adapter.finish();
222 } else {
223 let mut transform_sink =
224 ScalingTransformingSink::new(&mut simplifying_adapter, matrix, subfont.scale);
225 cs_eval.evaluate(&mut transform_sink)?;
226 }
227 } else {
228 if hint && subfont.scale.is_some() {
230 let mut hinting_adapter =
231 HintingSink::new(&subfont.hint_state, &mut simplifying_adapter);
232 cs_eval.evaluate(&mut hinting_adapter)?;
233 hinting_adapter.finish();
234 } else {
235 let mut scaling_adapter =
236 ScalingSink26Dot6::new(&mut simplifying_adapter, subfont.scale);
237 cs_eval.evaluate(&mut scaling_adapter)?;
238 }
239 }
240 simplifying_adapter.finish();
241 Ok(())
242 }
243
244 fn private_dict_range(&self, subfont_index: u32) -> Result<Range<usize>, Error> {
245 if self.top_dict.font_dicts.count() != 0 {
246 let font_dict_data = self.top_dict.font_dicts.get(subfont_index as usize)?;
249 let mut range = None;
250 for entry in dict::entries(font_dict_data, None) {
251 if let dict::Entry::PrivateDictRange(r) = entry? {
252 range = Some(r);
253 break;
254 }
255 }
256 range
257 } else {
258 let range = self.top_dict.private_dict_range.clone();
263 Some(range.start as usize..range.end as usize)
264 }
265 .ok_or(Error::MissingPrivateDict)
266 }
267}
268
269struct CharstringEvaluator<'a> {
270 cff_data: &'a [u8],
271 charstrings: Index<'a>,
272 global_subrs: Index<'a>,
273 subrs: Option<Index<'a>>,
274 blend_state: Option<BlendState<'a>>,
275 charstring_data: &'a [u8],
276}
277
278impl CharstringEvaluator<'_> {
279 fn evaluate(self, sink: &mut impl CommandSink) -> Result<(), Error> {
280 charstring::evaluate(
281 self.cff_data,
282 self.charstrings,
283 self.global_subrs,
284 self.subrs,
285 self.blend_state,
286 self.charstring_data,
287 sink,
288 )
289 }
290}
291
292#[derive(Clone)]
300pub(crate) struct Subfont {
301 is_cff2: bool,
302 scale: Option<Fixed>,
303 subrs_offset: Option<usize>,
304 pub(crate) hint_state: HintState,
305 store_index: u16,
306}
307
308impl Subfont {
309 pub fn subrs<'a>(&self, scaler: &Outlines<'a>) -> Result<Option<Index<'a>>, Error> {
311 if let Some(subrs_offset) = self.subrs_offset {
312 let offset_data = scaler.offset_data.as_bytes();
313 let index_data = offset_data.get(subrs_offset..).unwrap_or_default();
314 Ok(Some(Index::new(index_data, self.is_cff2)?))
315 } else {
316 Ok(None)
317 }
318 }
319
320 pub fn blend_state<'a>(
323 &self,
324 scaler: &Outlines<'a>,
325 coords: &'a [F2Dot14],
326 ) -> Result<Option<BlendState<'a>>, Error> {
327 if let Some(var_store) = scaler.top_dict.var_store.clone() {
328 Ok(Some(BlendState::new(var_store, coords, self.store_index)?))
329 } else {
330 Ok(None)
331 }
332 }
333}
334
335#[derive(Default)]
338struct PrivateDict {
339 hint_params: HintParams,
340 subrs_offset: Option<usize>,
341 store_index: u16,
342}
343
344impl PrivateDict {
345 fn new(
346 data: FontData,
347 range: Range<usize>,
348 blend_state: Option<BlendState<'_>>,
349 ) -> Result<Self, Error> {
350 let private_dict_data = data.read_array(range.clone())?;
351 let mut dict = Self::default();
352 for entry in dict::entries(private_dict_data, blend_state) {
353 use dict::Entry::*;
354 match entry? {
355 BlueValues(values) => dict.hint_params.blues = values,
356 FamilyBlues(values) => dict.hint_params.family_blues = values,
357 OtherBlues(values) => dict.hint_params.other_blues = values,
358 FamilyOtherBlues(values) => dict.hint_params.family_other_blues = values,
359 BlueScale(value) => dict.hint_params.blue_scale = value,
360 BlueShift(value) => dict.hint_params.blue_shift = value,
361 BlueFuzz(value) => dict.hint_params.blue_fuzz = value,
362 LanguageGroup(group) => dict.hint_params.language_group = group,
363 SubrsOffset(offset) => {
365 dict.subrs_offset = Some(
366 range
367 .start
368 .checked_add(offset)
369 .ok_or(ReadError::OutOfBounds)?,
370 )
371 }
372 VariationStoreIndex(index) => dict.store_index = index,
373 _ => {}
374 }
375 }
376 Ok(dict)
377 }
378}
379
380#[derive(Clone, Default)]
383struct TopDict<'a> {
384 charstrings: Index<'a>,
385 font_dicts: Index<'a>,
386 fd_select: Option<FdSelect<'a>>,
387 private_dict_range: Range<u32>,
388 font_matrix: Option<[Fixed; 6]>,
389 var_store: Option<ItemVariationStore<'a>>,
390}
391
392impl<'a> TopDict<'a> {
393 fn new(table_data: &'a [u8], top_dict_data: &'a [u8], is_cff2: bool) -> Result<Self, Error> {
394 let mut items = TopDict::default();
395 for entry in dict::entries(top_dict_data, None) {
396 match entry? {
397 dict::Entry::CharstringsOffset(offset) => {
398 items.charstrings =
399 Index::new(table_data.get(offset..).unwrap_or_default(), is_cff2)?;
400 }
401 dict::Entry::FdArrayOffset(offset) => {
402 items.font_dicts =
403 Index::new(table_data.get(offset..).unwrap_or_default(), is_cff2)?;
404 }
405 dict::Entry::FdSelectOffset(offset) => {
406 items.fd_select = Some(FdSelect::read(FontData::new(
407 table_data.get(offset..).unwrap_or_default(),
408 ))?);
409 }
410 dict::Entry::PrivateDictRange(range) => {
411 items.private_dict_range = range.start as u32..range.end as u32;
412 }
413 dict::Entry::FontMatrix(matrix) => {
414 items.font_matrix = Some(matrix);
415 }
416 dict::Entry::VariationStoreOffset(offset) if is_cff2 => {
417 let offset = offset.checked_add(2).ok_or(ReadError::OutOfBounds)?;
421 items.var_store = Some(ItemVariationStore::read(FontData::new(
422 table_data.get(offset..).unwrap_or_default(),
423 ))?);
424 }
425 _ => {}
426 }
427 }
428 Ok(items)
429 }
430}
431
432struct PenSink<'a, P>(&'a mut P);
435
436impl<'a, P> PenSink<'a, P> {
437 fn new(pen: &'a mut P) -> Self {
438 Self(pen)
439 }
440}
441
442impl<P> CommandSink for PenSink<'_, P>
443where
444 P: OutlinePen,
445{
446 fn move_to(&mut self, x: Fixed, y: Fixed) {
447 self.0.move_to(x.to_f32(), y.to_f32());
448 }
449
450 fn line_to(&mut self, x: Fixed, y: Fixed) {
451 self.0.line_to(x.to_f32(), y.to_f32());
452 }
453
454 fn curve_to(&mut self, cx0: Fixed, cy0: Fixed, cx1: Fixed, cy1: Fixed, x: Fixed, y: Fixed) {
455 self.0.curve_to(
456 cx0.to_f32(),
457 cy0.to_f32(),
458 cx1.to_f32(),
459 cy1.to_f32(),
460 x.to_f32(),
461 y.to_f32(),
462 );
463 }
464
465 fn close(&mut self) {
466 self.0.close();
467 }
468}
469
470fn transform(matrix: &[Fixed; 6], x: Fixed, y: Fixed) -> (Fixed, Fixed) {
471 (
472 matrix[0] * x + matrix[2] * y + matrix[4],
473 matrix[1] * x + matrix[3] * y + matrix[5],
474 )
475}
476
477struct HintedTransformingSink<'a, S> {
479 inner: &'a mut S,
480 matrix: [Fixed; 6],
481}
482
483impl<'a, S> HintedTransformingSink<'a, S> {
484 fn new(sink: &'a mut S, matrix: [Fixed; 6]) -> Self {
485 Self {
486 inner: sink,
487 matrix,
488 }
489 }
490
491 fn transform(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
492 let (x, y) = transform(
495 &self.matrix,
496 Fixed::from_bits(x.to_bits() >> 10),
497 Fixed::from_bits(y.to_bits() >> 10),
498 );
499 (
500 Fixed::from_bits(x.to_bits() << 10),
501 Fixed::from_bits(y.to_bits() << 10),
502 )
503 }
504}
505
506impl<S: CommandSink> CommandSink for HintedTransformingSink<'_, S> {
507 fn hstem(&mut self, y: Fixed, dy: Fixed) {
508 self.inner.hstem(y, dy);
509 }
510
511 fn vstem(&mut self, x: Fixed, dx: Fixed) {
512 self.inner.vstem(x, dx);
513 }
514
515 fn hint_mask(&mut self, mask: &[u8]) {
516 self.inner.hint_mask(mask);
517 }
518
519 fn counter_mask(&mut self, mask: &[u8]) {
520 self.inner.counter_mask(mask);
521 }
522
523 fn move_to(&mut self, x: Fixed, y: Fixed) {
524 let (x, y) = self.transform(x, y);
525 self.inner.move_to(x, y);
526 }
527
528 fn line_to(&mut self, x: Fixed, y: Fixed) {
529 let (x, y) = self.transform(x, y);
530 self.inner.line_to(x, y);
531 }
532
533 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
534 let (cx1, cy1) = self.transform(cx1, cy1);
535 let (cx2, cy2) = self.transform(cx2, cy2);
536 let (x, y) = self.transform(x, y);
537 self.inner.curve_to(cx1, cy1, cx2, cy2, x, y);
538 }
539
540 fn close(&mut self) {
541 self.inner.close();
542 }
543}
544
545const ONE_OVER_64: Fixed = Fixed::from_bits(0x400);
547
548struct ScalingTransformingSink<'a, S> {
551 inner: &'a mut S,
552 matrix: [Fixed; 6],
553 scale: Option<Fixed>,
554}
555
556impl<'a, S> ScalingTransformingSink<'a, S> {
557 fn new(sink: &'a mut S, matrix: [Fixed; 6], scale: Option<Fixed>) -> Self {
558 Self {
559 inner: sink,
560 matrix,
561 scale,
562 }
563 }
564
565 fn transform(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
566 let ax = x * ONE_OVER_64;
576 let ay = y * ONE_OVER_64;
577 let bx = Fixed::from_bits(ax.to_bits() >> 10);
581 let by = Fixed::from_bits(ay.to_bits() >> 10);
582 let (cx, cy) = transform(&self.matrix, bx, by);
584 if let Some(scale) = self.scale {
585 let dx = cx * scale;
589 let dy = cy * scale;
590 (
592 Fixed::from_bits(dx.to_bits() << 10),
593 Fixed::from_bits(dy.to_bits() << 10),
594 )
595 } else {
596 (
599 Fixed::from_bits(cx.to_bits() << 16),
600 Fixed::from_bits(cy.to_bits() << 16),
601 )
602 }
603 }
604}
605
606impl<S: CommandSink> CommandSink for ScalingTransformingSink<'_, S> {
607 fn hstem(&mut self, y: Fixed, dy: Fixed) {
608 self.inner.hstem(y, dy);
609 }
610
611 fn vstem(&mut self, x: Fixed, dx: Fixed) {
612 self.inner.vstem(x, dx);
613 }
614
615 fn hint_mask(&mut self, mask: &[u8]) {
616 self.inner.hint_mask(mask);
617 }
618
619 fn counter_mask(&mut self, mask: &[u8]) {
620 self.inner.counter_mask(mask);
621 }
622
623 fn move_to(&mut self, x: Fixed, y: Fixed) {
624 let (x, y) = self.transform(x, y);
625 self.inner.move_to(x, y);
626 }
627
628 fn line_to(&mut self, x: Fixed, y: Fixed) {
629 let (x, y) = self.transform(x, y);
630 self.inner.line_to(x, y);
631 }
632
633 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
634 let (cx1, cy1) = self.transform(cx1, cy1);
635 let (cx2, cy2) = self.transform(cx2, cy2);
636 let (x, y) = self.transform(x, y);
637 self.inner.curve_to(cx1, cy1, cx2, cy2, x, y);
638 }
639
640 fn close(&mut self) {
641 self.inner.close();
642 }
643}
644
645struct ScalingSink26Dot6<'a, S> {
651 inner: &'a mut S,
652 scale: Option<Fixed>,
653}
654
655impl<'a, S> ScalingSink26Dot6<'a, S> {
656 fn new(sink: &'a mut S, scale: Option<Fixed>) -> Self {
657 Self { scale, inner: sink }
658 }
659
660 fn scale(&self, coord: Fixed) -> Fixed {
661 let a = coord * ONE_OVER_64;
671 let b = Fixed::from_bits(a.to_bits() >> 10);
675 if let Some(scale) = self.scale {
676 let c = b * scale;
680 Fixed::from_bits(c.to_bits() << 10)
682 } else {
683 Fixed::from_bits(b.to_bits() << 16)
686 }
687 }
688}
689
690impl<S: CommandSink> CommandSink for ScalingSink26Dot6<'_, S> {
691 fn hstem(&mut self, y: Fixed, dy: Fixed) {
692 self.inner.hstem(y, dy);
693 }
694
695 fn vstem(&mut self, x: Fixed, dx: Fixed) {
696 self.inner.vstem(x, dx);
697 }
698
699 fn hint_mask(&mut self, mask: &[u8]) {
700 self.inner.hint_mask(mask);
701 }
702
703 fn counter_mask(&mut self, mask: &[u8]) {
704 self.inner.counter_mask(mask);
705 }
706
707 fn move_to(&mut self, x: Fixed, y: Fixed) {
708 self.inner.move_to(self.scale(x), self.scale(y));
709 }
710
711 fn line_to(&mut self, x: Fixed, y: Fixed) {
712 self.inner.line_to(self.scale(x), self.scale(y));
713 }
714
715 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
716 self.inner.curve_to(
717 self.scale(cx1),
718 self.scale(cy1),
719 self.scale(cx2),
720 self.scale(cy2),
721 self.scale(x),
722 self.scale(y),
723 );
724 }
725
726 fn close(&mut self) {
727 self.inner.close();
728 }
729}
730
731#[derive(Copy, Clone)]
732enum PendingElement {
733 Move([Fixed; 2]),
734 Line([Fixed; 2]),
735 Curve([Fixed; 6]),
736}
737
738impl PendingElement {
739 fn target_point(&self) -> [Fixed; 2] {
740 match self {
741 Self::Move(xy) | Self::Line(xy) => *xy,
742 Self::Curve([.., x, y]) => [*x, *y],
743 }
744 }
745}
746
747struct NopFilteringSink<'a, S> {
756 is_open: bool,
757 start: Option<(Fixed, Fixed)>,
758 pending_element: Option<PendingElement>,
759 inner: &'a mut S,
760}
761
762impl<'a, S> NopFilteringSink<'a, S>
763where
764 S: CommandSink,
765{
766 fn new(inner: &'a mut S) -> Self {
767 Self {
768 is_open: false,
769 start: None,
770 pending_element: None,
771 inner,
772 }
773 }
774
775 fn flush_pending(&mut self, for_close: bool) {
776 if let Some(pending) = self.pending_element.take() {
777 match pending {
778 PendingElement::Move([x, y]) => {
779 if !for_close {
780 self.is_open = true;
781 self.inner.move_to(x, y);
782 self.start = Some((x, y));
783 }
784 }
785 PendingElement::Line([x, y]) => {
786 if !for_close || self.start != Some((x, y)) {
787 self.inner.line_to(x, y);
788 }
789 }
790 PendingElement::Curve([cx0, cy0, cx1, cy1, x, y]) => {
791 self.inner.curve_to(cx0, cy0, cx1, cy1, x, y);
792 }
793 }
794 }
795 }
796
797 pub fn finish(&mut self) {
798 self.close();
799 }
800}
801
802impl<S> CommandSink for NopFilteringSink<'_, S>
803where
804 S: CommandSink,
805{
806 fn hstem(&mut self, y: Fixed, dy: Fixed) {
807 self.inner.hstem(y, dy);
808 }
809
810 fn vstem(&mut self, x: Fixed, dx: Fixed) {
811 self.inner.vstem(x, dx);
812 }
813
814 fn hint_mask(&mut self, mask: &[u8]) {
815 self.inner.hint_mask(mask);
816 }
817
818 fn counter_mask(&mut self, mask: &[u8]) {
819 self.inner.counter_mask(mask);
820 }
821
822 fn move_to(&mut self, x: Fixed, y: Fixed) {
823 self.pending_element = Some(PendingElement::Move([x, y]));
824 }
825
826 fn line_to(&mut self, x: Fixed, y: Fixed) {
827 if self
829 .pending_element
830 .map(|element| element.target_point() == [x, y])
831 .unwrap_or_default()
832 {
833 return;
834 }
835 self.flush_pending(false);
836 self.pending_element = Some(PendingElement::Line([x, y]));
837 }
838
839 fn curve_to(&mut self, cx1: Fixed, cy1: Fixed, cx2: Fixed, cy2: Fixed, x: Fixed, y: Fixed) {
840 self.flush_pending(false);
841 self.pending_element = Some(PendingElement::Curve([cx1, cy1, cx2, cy2, x, y]));
842 }
843
844 fn close(&mut self) {
845 self.flush_pending(true);
846 if self.is_open {
847 self.inner.close();
848 self.is_open = false;
849 }
850 }
851}
852
853#[cfg(test)]
854mod tests {
855 use super::{super::pen::SvgPen, *};
856 use crate::{
857 outline::{HintingInstance, HintingOptions},
858 prelude::{LocationRef, Size},
859 MetadataProvider,
860 };
861 use dict::Blues;
862 use font_test_data::bebuffer::BeBuffer;
863 use raw::tables::cff2::Cff2;
864 use read_fonts::FontRef;
865
866 #[test]
867 fn unscaled_scaling_sink_produces_integers() {
868 let nothing = &mut ();
869 let sink = ScalingSink26Dot6::new(nothing, None);
870 for coord in [50.0, 50.1, 50.125, 50.5, 50.9] {
871 assert_eq!(sink.scale(Fixed::from_f64(coord)).to_f32(), 50.0);
872 }
873 }
874
875 #[test]
876 fn scaled_scaling_sink() {
877 let ppem = 20.0;
878 let upem = 1000.0;
879 let scale = Fixed::from_bits((ppem * 64.) as i32) / Fixed::from_bits(upem as i32);
881 let nothing = &mut ();
882 let sink = ScalingSink26Dot6::new(nothing, Some(scale));
883 let inputs = [
884 (0.0, 0.0),
886 (8.0, 0.15625),
887 (16.0, 0.3125),
888 (32.0, 0.640625),
889 (72.0, 1.4375),
890 (128.0, 2.5625),
891 ];
892 for (coord, expected) in inputs {
893 assert_eq!(
894 sink.scale(Fixed::from_f64(coord)).to_f32(),
895 expected,
896 "scaling coord {coord}"
897 );
898 }
899 }
900
901 #[test]
902 fn read_cff_static() {
903 let font = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED).unwrap();
904 let cff = Outlines::new(&font).unwrap();
905 assert!(!cff.is_cff2());
906 assert!(cff.top_dict.var_store.is_none());
907 assert!(cff.top_dict.font_dicts.count() == 0);
908 assert!(!cff.top_dict.private_dict_range.is_empty());
909 assert!(cff.top_dict.fd_select.is_none());
910 assert_eq!(cff.subfont_count(), 1);
911 assert_eq!(cff.subfont_index(GlyphId::new(1)), 0);
912 assert_eq!(cff.global_subrs.count(), 17);
913 }
914
915 #[test]
916 fn read_cff2_static() {
917 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
918 let cff = Outlines::new(&font).unwrap();
919 assert!(cff.is_cff2());
920 assert!(cff.top_dict.var_store.is_some());
921 assert!(cff.top_dict.font_dicts.count() != 0);
922 assert!(cff.top_dict.private_dict_range.is_empty());
923 assert!(cff.top_dict.fd_select.is_none());
924 assert_eq!(cff.subfont_count(), 1);
925 assert_eq!(cff.subfont_index(GlyphId::new(1)), 0);
926 assert_eq!(cff.global_subrs.count(), 0);
927 }
928
929 #[test]
930 fn read_example_cff2_table() {
931 let cff2 = Cff2::read(FontData::new(font_test_data::cff2::EXAMPLE)).unwrap();
932 let top_dict =
933 TopDict::new(cff2.offset_data().as_bytes(), cff2.top_dict_data(), true).unwrap();
934 assert!(top_dict.var_store.is_some());
935 assert!(top_dict.font_dicts.count() != 0);
936 assert!(top_dict.private_dict_range.is_empty());
937 assert!(top_dict.fd_select.is_none());
938 assert_eq!(cff2.global_subrs().count(), 0);
939 }
940
941 #[test]
942 fn cff2_variable_outlines_match_freetype() {
943 compare_glyphs(
944 font_test_data::CANTARELL_VF_TRIMMED,
945 font_test_data::CANTARELL_VF_TRIMMED_GLYPHS,
946 );
947 }
948
949 #[test]
950 fn cff_static_outlines_match_freetype() {
951 compare_glyphs(
952 font_test_data::NOTO_SERIF_DISPLAY_TRIMMED,
953 font_test_data::NOTO_SERIF_DISPLAY_TRIMMED_GLYPHS,
954 );
955 }
956
957 #[test]
958 fn unhinted_ends_with_close() {
959 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
960 let glyph = font.outline_glyphs().get(GlyphId::new(1)).unwrap();
961 let mut svg = SvgPen::default();
962 glyph.draw(Size::unscaled(), &mut svg).unwrap();
963 assert!(svg.to_string().ends_with('Z'));
964 }
965
966 #[test]
967 fn hinted_ends_with_close() {
968 let font = FontRef::new(font_test_data::CANTARELL_VF_TRIMMED).unwrap();
969 let glyphs = font.outline_glyphs();
970 let hinter = HintingInstance::new(
971 &glyphs,
972 Size::unscaled(),
973 LocationRef::default(),
974 HintingOptions::default(),
975 )
976 .unwrap();
977 let glyph = glyphs.get(GlyphId::new(1)).unwrap();
978 let mut svg = SvgPen::default();
979 glyph.draw(&hinter, &mut svg).unwrap();
980 assert!(svg.to_string().ends_with('Z'));
981 }
982
983 #[test]
985 fn empty_private_dict() {
986 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET).unwrap();
987 let outlines = super::Outlines::new(&font).unwrap();
988 assert!(outlines.top_dict.private_dict_range.is_empty());
989 assert!(outlines.private_dict_range(0).unwrap().is_empty());
990 }
991
992 #[test]
995 fn subrs_offset_overflow() {
996 let private_dict = BeBuffer::new()
998 .push(0u32) .push(29u8) .push(-1i32) .push(19u8) .to_vec();
1003 assert!(
1005 PrivateDict::new(FontData::new(&private_dict), 4..private_dict.len(), None).is_err()
1006 );
1007 }
1008
1009 #[test]
1013 fn top_dict_ivs_offset_overflow() {
1014 let top_dict = BeBuffer::new()
1017 .push(29u8) .push(-1i32) .push(24u8) .to_vec();
1021 assert!(TopDict::new(&[], &top_dict, true).is_err());
1023 }
1024
1025 #[test]
1032 fn proper_scaling_when_factor_equals_fixed_one() {
1033 let font = FontRef::new(font_test_data::MATERIAL_ICONS_SUBSET).unwrap();
1034 assert_eq!(font.head().unwrap().units_per_em(), 512);
1035 let glyphs = font.outline_glyphs();
1036 let glyph = glyphs.get(GlyphId::new(1)).unwrap();
1037 let mut svg = SvgPen::with_precision(6);
1038 glyph
1039 .draw((Size::new(8.0), LocationRef::default()), &mut svg)
1040 .unwrap();
1041 assert!(svg.starts_with("M6.328125,7.000000 L1.671875,7.000000"));
1043 }
1044
1045 fn compare_glyphs(font_data: &[u8], expected_outlines: &str) {
1052 use super::super::testing;
1053 let font = FontRef::new(font_data).unwrap();
1054 let expected_outlines = testing::parse_glyph_outlines(expected_outlines);
1055 let outlines = super::Outlines::new(&font).unwrap();
1056 let mut path = testing::Path::default();
1057 for expected_outline in &expected_outlines {
1058 if expected_outline.size == 0.0 && !expected_outline.coords.is_empty() {
1059 continue;
1060 }
1061 let size = (expected_outline.size != 0.0).then_some(expected_outline.size);
1062 path.elements.clear();
1063 let subfont = outlines
1064 .subfont(
1065 outlines.subfont_index(expected_outline.glyph_id),
1066 size,
1067 &expected_outline.coords,
1068 )
1069 .unwrap();
1070 outlines
1071 .draw(
1072 &subfont,
1073 expected_outline.glyph_id,
1074 &expected_outline.coords,
1075 false,
1076 &mut path,
1077 )
1078 .unwrap();
1079 if path.elements != expected_outline.path {
1080 panic!(
1081 "mismatch in glyph path for id {} (size: {}, coords: {:?}): path: {:?} expected_path: {:?}",
1082 expected_outline.glyph_id,
1083 expected_outline.size,
1084 expected_outline.coords,
1085 &path.elements,
1086 &expected_outline.path
1087 );
1088 }
1089 }
1090 }
1091
1092 #[test]
1094 fn capture_family_other_blues() {
1095 let private_dict_data = &font_test_data::cff2::EXAMPLE[0x4f..=0xc0];
1096 let store =
1097 ItemVariationStore::read(FontData::new(&font_test_data::cff2::EXAMPLE[18..])).unwrap();
1098 let coords = &[F2Dot14::from_f32(0.0)];
1099 let blend_state = BlendState::new(store, coords, 0).unwrap();
1100 let private_dict = PrivateDict::new(
1101 FontData::new(private_dict_data),
1102 0..private_dict_data.len(),
1103 Some(blend_state),
1104 )
1105 .unwrap();
1106 assert_eq!(
1107 private_dict.hint_params.family_other_blues,
1108 Blues::new([-249.0, -239.0].map(Fixed::from_f64).into_iter())
1109 )
1110 }
1111
1112 #[test]
1113 fn implied_seac() {
1114 let font = FontRef::new(font_test_data::CHARSTRING_PATH_OPS).unwrap();
1115 let glyphs = font.outline_glyphs();
1116 let gid = GlyphId::new(3);
1117 assert_eq!(font.glyph_names().get(gid).unwrap(), "Scaron");
1118 let glyph = glyphs.get(gid).unwrap();
1119 let mut pen = SvgPen::new();
1120 glyph
1121 .draw((Size::unscaled(), LocationRef::default()), &mut pen)
1122 .unwrap();
1123 assert_eq!(pen.to_string().chars().filter(|ch| *ch == 'Z').count(), 2);
1128 }
1129
1130 const TRANSFORM: [Fixed; 6] = [
1131 Fixed::ONE,
1132 Fixed::ZERO,
1133 Fixed::from_bits(10945),
1135 Fixed::ONE,
1136 Fixed::ZERO,
1137 Fixed::ZERO,
1138 ];
1139
1140 #[test]
1141 fn hinted_transform_sink() {
1142 let input = [(383i32, 117i32), (450, 20), (555, -34), (683, -34)]
1145 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1146 let expected = [(403, 117i32), (453, 20), (549, -34), (677, -34)]
1147 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1148 let mut dummy = ();
1149 let sink = HintedTransformingSink::new(&mut dummy, TRANSFORM);
1150 let transformed = input.map(|(x, y)| sink.transform(x, y));
1151 assert_eq!(transformed, expected);
1152 }
1153
1154 #[test]
1155 fn unhinted_scaled_transform_sink() {
1156 let input = [(150i32, 46i32), (176, 8), (217, -13), (267, -13)]
1159 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1160 let expected = [(404, 118i32), (453, 20), (550, -33), (678, -33)]
1161 .map(|(x, y)| (Fixed::from_bits(x << 10), Fixed::from_bits(y << 10)));
1162 let mut dummy = ();
1163 let sink =
1164 ScalingTransformingSink::new(&mut dummy, TRANSFORM, Some(Fixed::from_bits(167772)));
1165 let transformed = input.map(|(x, y)| sink.transform(x, y));
1166 assert_eq!(transformed, expected);
1167 }
1168
1169 #[test]
1170 fn unhinted_unscaled_transform_sink() {
1171 let input = [(150i32, 46i32), (176, 8), (217, -13), (267, -13)]
1174 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1175 let expected = [(158, 46i32), (177, 8), (215, -13), (265, -13)]
1176 .map(|(x, y)| (Fixed::from_bits(x << 16), Fixed::from_bits(y << 16)));
1177 let mut dummy = ();
1178 let sink = ScalingTransformingSink::new(&mut dummy, TRANSFORM, None);
1179 let transformed = input.map(|(x, y)| sink.transform(x, y));
1180 assert_eq!(transformed, expected);
1181 }
1182}