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