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::font_face::FontFaceRule;
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
60fn compute_used_font_features(
65 options: &ShapingOptions,
66 font_face_rule: Option<&FontFaceRule>,
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.descriptors.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.value() as u32,
88 )
89 }
90 }
91
92 if options.ligatures == FontVariantLigatures::NONE {
96 add_feature(LIGA, 0);
97 add_feature(CLIG, 0);
98 add_feature(DLIG, 0);
99 add_feature(HLIG, 0);
100 add_feature(CALT, 0);
101 } else {
102 if options
103 .ligatures
104 .contains(FontVariantLigatures::COMMON_LIGATURES)
105 {
106 add_feature(LIGA, 1);
107 add_feature(CLIG, 1);
108 } else if options
109 .ligatures
110 .contains(FontVariantLigatures::NO_COMMON_LIGATURES)
111 {
112 add_feature(LIGA, 0);
113 add_feature(CLIG, 0);
114 }
115
116 if options
117 .ligatures
118 .contains(FontVariantLigatures::DISCRETIONARY_LIGATURES)
119 {
120 add_feature(DLIG, 1);
121 } else if options
122 .ligatures
123 .contains(FontVariantLigatures::NO_DISCRETIONARY_LIGATURES)
124 {
125 add_feature(DLIG, 0);
126 }
127
128 if options
129 .ligatures
130 .contains(FontVariantLigatures::HISTORICAL_LIGATURES)
131 {
132 add_feature(HLIG, 1);
133 } else if options
134 .ligatures
135 .contains(FontVariantLigatures::NO_HISTORICAL_LIGATURES)
136 {
137 add_feature(HLIG, 0);
138 }
139
140 if options.ligatures.contains(FontVariantLigatures::CONTEXTUAL) {
141 add_feature(CALT, 1);
142 } else if options
143 .ligatures
144 .contains(FontVariantLigatures::NO_CONTEXTUAL)
145 {
146 add_feature(CALT, 0);
147 }
148 }
149
150 if options.numeric != FontVariantNumeric::NORMAL {
151 if options.numeric.contains(FontVariantNumeric::LINING_NUMS) {
152 add_feature(LNUM, 1);
153 } else if options.numeric.contains(FontVariantNumeric::OLDSTYLE_NUMS) {
154 add_feature(ONUM, 1);
155 }
156 if options
157 .numeric
158 .contains(FontVariantNumeric::PROPORTIONAL_NUMS)
159 {
160 add_feature(PNUM, 1);
161 } else if options.numeric.contains(FontVariantNumeric::TABULAR_NUMS) {
162 add_feature(TNUM, 1);
163 }
164 if options
165 .numeric
166 .contains(FontVariantNumeric::DIAGONAL_FRACTIONS)
167 {
168 add_feature(FRAC, 1);
169 } else if options
170 .numeric
171 .contains(FontVariantNumeric::STACKED_FRACTIONS)
172 {
173 add_feature(AFRC, 1);
174 }
175 if options.numeric.contains(FontVariantNumeric::ORDINAL) {
176 add_feature(ORDN, 1);
177 }
178 if options.numeric.contains(FontVariantNumeric::SLASHED_ZERO) {
179 add_feature(ZERO, 1);
180 }
181 }
182
183 if options.east_asian != FontVariantEastAsian::NORMAL {
184 if options.east_asian.contains(FontVariantEastAsian::JIS78) {
185 add_feature(JP78, 1);
186 } else if options.east_asian.contains(FontVariantEastAsian::JIS83) {
187 add_feature(JP83, 1);
188 } else if options.east_asian.contains(FontVariantEastAsian::JIS90) {
189 add_feature(JP90, 1);
190 } else if options.east_asian.contains(FontVariantEastAsian::JIS04) {
191 add_feature(JP04, 1);
192 } else if options
193 .east_asian
194 .contains(FontVariantEastAsian::SIMPLIFIED)
195 {
196 add_feature(SMPL, 1);
197 } else if options
198 .east_asian
199 .contains(FontVariantEastAsian::TRADITIONAL)
200 {
201 add_feature(TRAD, 1);
202 }
203
204 if options
205 .east_asian
206 .contains(FontVariantEastAsian::FULL_WIDTH)
207 {
208 add_feature(FWID, 1);
209 } else if options
210 .east_asian
211 .contains(FontVariantEastAsian::PROPORTIONAL_WIDTH)
212 {
213 add_feature(PWID, 1);
214 }
215
216 if options.east_asian.contains(FontVariantEastAsian::RUBY) {
217 add_feature(RUBY, 1);
218 }
219 }
220
221 match options.position {
222 FontVariantPosition::Normal => {},
223 FontVariantPosition::Sub => add_feature(SUBS, 1),
224 FontVariantPosition::Super => add_feature(SUPS, 1),
225 }
226
227 if options
228 .flags
229 .contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
230 {
231 add_feature(KERN, 0);
232 }
233
234 for feature_setting in options.feature_settings.0.iter() {
236 add_feature(
237 Tag::from_u32(feature_setting.tag.0),
238 feature_setting.value as u32,
239 )
240 }
241
242 features.into_iter()
243}