skrifa/scaler/
postscript.rs1use super::{DrawnMetrics, ScaleError};
7use crate::{
8 font::Font,
9 outline::cff::{scale_for_hinting, HintParams, HintState, HintedTransformingSink, HintingSink},
10};
11use alloc::vec::Vec;
12use raw::model::Error;
13use raw::{
14 model::glyph::outline::{cff::CffGlyph, PathSink},
15 ps::{
16 cff::{CffFontRef, Subfont},
17 charstring::NopFilterSink,
18 FontMatrix, HintingParams, Transform,
19 },
20 types::{F2Dot14, Fixed},
21};
22
23const CACHED_SUBFONTS: u16 = 8;
27
28#[derive(Default)]
30pub(super) struct PostScriptHinter {
31 subfonts: Vec<Option<HintedSubfont>>,
34 ppem: f32,
35}
36
37struct HintedSubfont {
39 subfont: Subfont,
40 transform: Transform,
41 hint_state: HintState,
42}
43
44impl HintedSubfont {
45 fn new(cff: &CffFontRef<'_>, index: u16, coords: &[F2Dot14], ppem: f32) -> Result<Self, Error> {
47 let (subfont, params) = cff.subfont_hinted(index, coords)?;
48 let transform = cff.transform(&subfont, Some(ppem));
49 let hint_state = HintState::new(&hint_params(params), scale_for_hinting(transform.scale));
50 Ok(Self {
51 subfont,
52 transform,
53 hint_state,
54 })
55 }
56}
57
58impl PostScriptHinter {
59 pub(super) fn reconfigure(&mut self, cff: &CffFontRef<'_>, coords: &[F2Dot14], ppem: f32) {
61 self.ppem = ppem;
62 self.subfonts.clear();
63 self.subfonts.extend(
64 (0..cff.num_subfonts().min(CACHED_SUBFONTS))
65 .map(|index| HintedSubfont::new(cff, index, coords, ppem).ok()),
66 );
67 }
68
69 pub(super) fn draw(
70 &self,
71 font: &Font,
72 outline: &CffGlyph<'_>,
73 sink: &mut dyn PathSink,
74 ) -> Result<DrawnMetrics, ScaleError> {
75 let cff = outline.cff_font();
76 let glyph = outline.glyph();
77 let coords = outline.normalized_coords();
78 let index = u16::try_from(outline.subfont_index()).map_err(|_| Error::Malformed)?;
79 let read;
80 let hinted = match self.subfonts.get(usize::from(index)) {
81 Some(hinted) => hinted.as_ref().ok_or(Error::Malformed)?,
82 None => {
83 read = HintedSubfont::new(cff, index, coords, self.ppem)?;
84 &read
85 }
86 };
87 let mut sink = sink;
88 let mut filter = NopFilterSink::new(&mut sink);
89 let width = if hinted.transform.matrix != FontMatrix::IDENTITY {
90 let mut transform = HintedTransformingSink::new(&mut filter, hinted.transform.matrix);
92 let mut hinting = HintingSink::new(&hinted.hint_state, &mut transform);
93 cff.evaluate_charstring(&hinted.subfont, glyph, coords, &mut hinting)?
94 } else {
95 let mut hinting = HintingSink::new(&hinted.hint_state, &mut filter);
96 cff.evaluate_charstring(&hinted.subfont, glyph, coords, &mut hinting)?
97 };
98 let advance_width = width
99 .or_else(|| {
102 let advance = font.glyph_metrics().h_advance_exact(glyph).round();
103 Some(Fixed::from_i32(advance.to_i32()))
104 })
105 .map(|width| hinted.transform.transform_h_metric(width).round().to_f32())
108 .filter(|width| *width >= 0.0);
111 let mut metrics = DrawnMetrics::default();
112 metrics.h_advance = advance_width;
113 Ok(metrics)
114 }
115}
116
117fn hint_params(params: HintingParams) -> HintParams {
121 HintParams {
122 blues: params.blues,
123 family_blues: params.family_blues,
124 other_blues: params.other_blues,
125 family_other_blues: params.family_other_blues,
126 blue_scale: params.blue_scale,
127 blue_shift: params.blue_shift,
128 blue_fuzz: params.blue_fuzz,
129 language_group: params.language_group,
130 }
131}