skrifa/outline/autohint/
instance.rs
1use crate::{attribute::Style, prelude::Size, MetadataProvider};
4
5use super::{
6 super::{
7 pen::PathStyle, AdjustedMetrics, DrawError, OutlineGlyph, OutlineGlyphCollection,
8 OutlinePen, Target,
9 },
10 metrics::{fixed_mul, pix_round, Scale, UnscaledStyleMetricsSet},
11 outline::Outline,
12 shape::{Shaper, ShaperMode},
13 style::GlyphStyleMap,
14};
15use alloc::sync::Arc;
16use raw::{
17 types::{F26Dot6, F2Dot14},
18 FontRef, TableProvider,
19};
20
21const SHAPER_MODE: ShaperMode = if cfg!(feature = "autohint_shaping") {
24 ShaperMode::BestEffort
25} else {
26 ShaperMode::Nominal
27};
28
29#[derive(Clone, Debug)]
34pub struct GlyphStyles(Arc<GlyphStyleMap>);
35
36impl GlyphStyles {
37 pub fn new(outlines: &OutlineGlyphCollection) -> Self {
39 if let Some(font) = outlines.font() {
40 let glyph_count = font
41 .maxp()
42 .map(|maxp| maxp.num_glyphs() as u32)
43 .unwrap_or_default();
44 let shaper = Shaper::new(font, SHAPER_MODE);
45 Self(Arc::new(GlyphStyleMap::new(glyph_count, &shaper)))
46 } else {
47 Self(Default::default())
48 }
49 }
50}
51
52#[derive(Clone)]
53pub(crate) struct Instance {
54 styles: GlyphStyles,
55 metrics: UnscaledStyleMetricsSet,
56 target: Target,
57 is_fixed_width: bool,
58 style: Style,
59}
60
61impl Instance {
62 pub fn new(
63 font: &FontRef,
64 outlines: &OutlineGlyphCollection,
65 coords: &[F2Dot14],
66 target: Target,
67 styles: Option<GlyphStyles>,
68 lazy_metrics: bool,
69 ) -> Self {
70 let styles = styles.unwrap_or_else(|| GlyphStyles::new(outlines));
71 #[cfg(feature = "std")]
72 let metrics = if lazy_metrics {
73 UnscaledStyleMetricsSet::lazy(&styles.0)
74 } else {
75 UnscaledStyleMetricsSet::precomputed(font, coords, SHAPER_MODE, &styles.0)
76 };
77 #[cfg(not(feature = "std"))]
78 let metrics = UnscaledStyleMetricsSet::precomputed(font, coords, SHAPER_MODE, &styles.0);
79 let is_fixed_width = font
80 .post()
81 .map(|post| post.is_fixed_pitch() != 0)
82 .unwrap_or_default();
83 let style = font.attributes().style;
84 Self {
85 styles,
86 metrics,
87 target,
88 is_fixed_width,
89 style,
90 }
91 }
92
93 pub fn draw(
94 &self,
95 size: Size,
96 coords: &[F2Dot14],
97 glyph: &OutlineGlyph,
98 path_style: PathStyle,
99 pen: &mut impl OutlinePen,
100 ) -> Result<AdjustedMetrics, DrawError> {
101 let font = glyph.font();
102 let glyph_id = glyph.glyph_id();
103 let style = self
104 .styles
105 .0
106 .style(glyph_id)
107 .ok_or(DrawError::GlyphNotFound(glyph_id))?;
108 let metrics = self
109 .metrics
110 .get(font, coords, SHAPER_MODE, &self.styles.0, glyph_id)
111 .ok_or(DrawError::GlyphNotFound(glyph_id))?;
112 let units_per_em = glyph.units_per_em() as i32;
113 let scale = Scale::new(
114 size.ppem().unwrap_or(units_per_em as f32),
115 units_per_em,
116 self.style,
117 self.target,
118 metrics.style_class().script.group,
119 );
120 let mut outline = Outline::default();
121 outline.fill(glyph, coords)?;
122 let hinted_metrics = super::hint::hint_outline(&mut outline, &metrics, &scale, Some(style));
123 let h_advance = outline.advance;
124 let mut pp1x = 0;
125 let mut pp2x = fixed_mul(h_advance, hinted_metrics.x_scale);
126 let is_light = self.target.is_light() || self.target.preserve_linear_metrics();
127 if !is_light {
129 if let (true, Some(edge_metrics)) = (
130 scale.flags & Scale::NO_ADVANCE == 0,
131 hinted_metrics.edge_metrics,
132 ) {
133 let old_rsb = pp2x - edge_metrics.right_opos;
134 let old_lsb = edge_metrics.left_opos;
135 let new_lsb = edge_metrics.left_pos;
136 let mut pp1x_uh = new_lsb - old_lsb;
137 let mut pp2x_uh = edge_metrics.right_pos + old_rsb;
138 if old_lsb < 24 {
139 pp1x_uh -= 8;
140 }
141 if old_rsb < 24 {
142 pp2x_uh += 8;
143 }
144 pp1x = pix_round(pp1x_uh);
145 pp2x = pix_round(pp2x_uh);
146 if pp1x >= new_lsb && old_lsb > 0 {
147 pp1x -= 64;
148 }
149 if pp2x <= edge_metrics.right_pos && old_rsb > 0 {
150 pp2x += 64;
151 }
152 } else {
153 pp1x = pix_round(pp1x);
154 pp2x = pix_round(pp2x);
155 }
156 } else {
157 pp1x = pix_round(pp1x);
158 pp2x = pix_round(pp2x);
159 }
160 if pp1x != 0 {
161 for point in &mut outline.points {
162 point.x -= pp1x;
163 }
164 }
165 let advance = if !is_light
166 && (self.is_fixed_width || (metrics.digits_have_same_width && style.is_digit()))
167 {
168 fixed_mul(h_advance, scale.x_scale)
169 } else if h_advance != 0 {
170 pp2x - pp1x
171 } else {
172 0
173 };
174 outline.to_path(path_style, pen)?;
175 Ok(AdjustedMetrics {
176 has_overlaps: glyph.has_overlaps().unwrap_or_default(),
177 lsb: None,
178 advance_width: Some(F26Dot6::from_bits(pix_round(advance)).to_f32()),
179 })
180 }
181}