1mod harfbuzz;
6
7use app_units::Au;
8use euclid::default::Point2D;
9pub(crate) use harfbuzz::Shaper;
10use read_fonts::types::Tag;
11use rustc_hash::FxHashMap;
12use style::computed_values::font_variant_position::T as FontVariantPosition;
13use style::properties::generated::font_face::Descriptors as FontFaceRuleDescriptors;
14use style::values::computed::{FontVariantEastAsian, FontVariantLigatures, FontVariantNumeric};
15
16use crate::{
17 AFRC, CALT, CLIG, DLIG, FRAC, FWID, GlyphId, HLIG, JP04, JP78, JP83, JP90, KERN, LIGA, LNUM,
18 ONUM, ORDN, PNUM, PWID, RUBY, SMPL, SUBS, SUPS, ShapingFlags, ShapingOptions, TNUM, TRAD, ZERO,
19};
20
21fn unicode_script_to_iso15924_tag(script: unicode_script::Script) -> u32 {
24 let bytes: [u8; 4] = match script {
25 unicode_script::Script::Unknown => *b"Zzzz",
26 _ => {
27 let short_name = script.short_name();
28 short_name.as_bytes().try_into().unwrap()
29 },
30 };
31
32 u32::from_be_bytes(bytes)
33}
34
35#[derive(Debug)]
36pub(crate) struct ShapedGlyph {
37 pub glyph_id: GlyphId,
39 pub string_byte_offset: usize,
43 pub advance: Au,
45 pub offset: Option<Point2D<Au>>,
47}
48
49pub(crate) trait GlyphShapingResult {
52 fn len(&self) -> usize;
54 fn is_rtl(&self) -> bool;
56 fn iter(&self) -> impl Iterator<Item = ShapedGlyph>;
58}
59
60pub(crate) fn compute_used_font_features(
65 options: &ShapingOptions,
66 font_face_rule: Option<&FontFaceRuleDescriptors>,
67) -> impl Iterator<Item = (Tag, u32)> {
68 let mut features = FxHashMap::default();
69
70 let mut add_feature = |tag, value| {
71 features.entry(tag).insert_entry(value);
72 };
73
74 add_feature(LIGA, 1);
77 add_feature(CLIG, 1);
78
79 if let Some(font_feature_settings) =
82 font_face_rule.and_then(|rule| rule.font_feature_settings.as_ref())
83 {
84 for feature_setting in font_feature_settings.0.iter() {
85 add_feature(
86 Tag::from_u32(feature_setting.tag.0),
87 feature_setting.value.resolve().expect(
88 "The value is enforced to be resolvable at parse time \
89 (see FontFeatureSettings::parse_for_font_face_rule).",
90 ) as u32,
91 )
92 }
93 }
94
95 if options.ligatures == FontVariantLigatures::NONE {
99 add_feature(LIGA, 0);
100 add_feature(CLIG, 0);
101 add_feature(DLIG, 0);
102 add_feature(HLIG, 0);
103 add_feature(CALT, 0);
104 } else {
105 if options
106 .ligatures
107 .contains(FontVariantLigatures::COMMON_LIGATURES)
108 {
109 add_feature(LIGA, 1);
110 add_feature(CLIG, 1);
111 } else if options
112 .ligatures
113 .contains(FontVariantLigatures::NO_COMMON_LIGATURES)
114 {
115 add_feature(LIGA, 0);
116 add_feature(CLIG, 0);
117 }
118
119 if options
120 .ligatures
121 .contains(FontVariantLigatures::DISCRETIONARY_LIGATURES)
122 {
123 add_feature(DLIG, 1);
124 } else if options
125 .ligatures
126 .contains(FontVariantLigatures::NO_DISCRETIONARY_LIGATURES)
127 {
128 add_feature(DLIG, 0);
129 }
130
131 if options
132 .ligatures
133 .contains(FontVariantLigatures::HISTORICAL_LIGATURES)
134 {
135 add_feature(HLIG, 1);
136 } else if options
137 .ligatures
138 .contains(FontVariantLigatures::NO_HISTORICAL_LIGATURES)
139 {
140 add_feature(HLIG, 0);
141 }
142
143 if options.ligatures.contains(FontVariantLigatures::CONTEXTUAL) {
144 add_feature(CALT, 1);
145 } else if options
146 .ligatures
147 .contains(FontVariantLigatures::NO_CONTEXTUAL)
148 {
149 add_feature(CALT, 0);
150 }
151 }
152
153 if options.numeric != FontVariantNumeric::NORMAL {
154 if options.numeric.contains(FontVariantNumeric::LINING_NUMS) {
155 add_feature(LNUM, 1);
156 } else if options.numeric.contains(FontVariantNumeric::OLDSTYLE_NUMS) {
157 add_feature(ONUM, 1);
158 }
159 if options
160 .numeric
161 .contains(FontVariantNumeric::PROPORTIONAL_NUMS)
162 {
163 add_feature(PNUM, 1);
164 } else if options.numeric.contains(FontVariantNumeric::TABULAR_NUMS) {
165 add_feature(TNUM, 1);
166 }
167 if options
168 .numeric
169 .contains(FontVariantNumeric::DIAGONAL_FRACTIONS)
170 {
171 add_feature(FRAC, 1);
172 } else if options
173 .numeric
174 .contains(FontVariantNumeric::STACKED_FRACTIONS)
175 {
176 add_feature(AFRC, 1);
177 }
178 if options.numeric.contains(FontVariantNumeric::ORDINAL) {
179 add_feature(ORDN, 1);
180 }
181 if options.numeric.contains(FontVariantNumeric::SLASHED_ZERO) {
182 add_feature(ZERO, 1);
183 }
184 }
185
186 if options.east_asian != FontVariantEastAsian::NORMAL {
187 if options.east_asian.contains(FontVariantEastAsian::JIS78) {
188 add_feature(JP78, 1);
189 } else if options.east_asian.contains(FontVariantEastAsian::JIS83) {
190 add_feature(JP83, 1);
191 } else if options.east_asian.contains(FontVariantEastAsian::JIS90) {
192 add_feature(JP90, 1);
193 } else if options.east_asian.contains(FontVariantEastAsian::JIS04) {
194 add_feature(JP04, 1);
195 } else if options
196 .east_asian
197 .contains(FontVariantEastAsian::SIMPLIFIED)
198 {
199 add_feature(SMPL, 1);
200 } else if options
201 .east_asian
202 .contains(FontVariantEastAsian::TRADITIONAL)
203 {
204 add_feature(TRAD, 1);
205 }
206
207 if options
208 .east_asian
209 .contains(FontVariantEastAsian::FULL_WIDTH)
210 {
211 add_feature(FWID, 1);
212 } else if options
213 .east_asian
214 .contains(FontVariantEastAsian::PROPORTIONAL_WIDTH)
215 {
216 add_feature(PWID, 1);
217 }
218
219 if options.east_asian.contains(FontVariantEastAsian::RUBY) {
220 add_feature(RUBY, 1);
221 }
222 }
223
224 match options.position {
225 FontVariantPosition::Normal => {},
226 FontVariantPosition::Sub => add_feature(SUBS, 1),
227 FontVariantPosition::Super => add_feature(SUPS, 1),
228 }
229
230 if options
231 .flags
232 .contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
233 {
234 add_feature(KERN, 0);
235 }
236
237 for feature_setting in options.feature_settings.0.iter() {
239 add_feature(
240 Tag::from_u32(feature_setting.tag.0),
241 feature_setting.value as u32,
242 )
243 }
244
245 features.into_iter()
246}