1#![allow(unsafe_code)]
6
7use std::os::raw::{c_char, c_int, c_uint, c_void};
8use std::sync::LazyLock;
9use std::{char, ptr};
10
11use app_units::Au;
12use euclid::default::Point2D;
13use harfbuzz_sys::{
16 HB_DIRECTION_LTR, HB_DIRECTION_RTL, HB_MEMORY_MODE_READONLY, HB_OT_LAYOUT_BASELINE_TAG_HANGING,
17 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT, HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
18 hb_blob_create, hb_blob_t, hb_bool_t, hb_buffer_add_utf8, hb_buffer_create, hb_buffer_destroy,
19 hb_buffer_get_glyph_infos, hb_buffer_get_glyph_positions, hb_buffer_get_length,
20 hb_buffer_set_direction, hb_buffer_set_script, hb_buffer_t, hb_codepoint_t,
21 hb_face_create_for_tables, hb_face_destroy, hb_face_t, hb_feature_t, hb_font_create,
22 hb_font_destroy, hb_font_funcs_create, hb_font_funcs_set_glyph_h_advance_func,
23 hb_font_funcs_set_nominal_glyph_func, hb_font_funcs_t, hb_font_set_funcs, hb_font_set_ppem,
24 hb_font_set_scale, hb_font_set_variations, hb_font_t, hb_glyph_info_t, hb_glyph_position_t,
25 hb_ot_layout_get_baseline, hb_position_t, hb_script_from_iso15924_tag, hb_shape, hb_tag_t,
26 hb_variation_t,
27};
28use num_traits::Zero;
29use read_fonts::types::Tag;
30
31use super::{HarfBuzzShapedGlyphData, ShapedGlyphEntry, unicode_script_to_iso15924_tag};
32use crate::platform::font::FontTable;
33use crate::{
34 BASE, Font, FontBaseline, FontTableMethods, GlyphId, GlyphStore, KERN, LIGA, ShapingFlags,
35 ShapingOptions, fixed_to_float, float_to_fixed,
36};
37
38const HB_OT_TAG_DEFAULT_SCRIPT: hb_tag_t = u32::from_be_bytes(Tag::new(b"DFLT").to_be_bytes());
39const HB_OT_TAG_DEFAULT_LANGUAGE: hb_tag_t = u32::from_be_bytes(Tag::new(b"dflt").to_be_bytes());
40
41pub(crate) struct ShapedGlyphData {
42 count: usize,
43 buffer: *mut hb_buffer_t,
44 glyph_infos: *mut hb_glyph_info_t,
45 pos_infos: *mut hb_glyph_position_t,
46}
47
48impl ShapedGlyphData {
49 unsafe fn new(buffer: *mut hb_buffer_t) -> ShapedGlyphData {
57 let mut glyph_count = 0;
58 let glyph_infos = unsafe { hb_buffer_get_glyph_infos(buffer, &mut glyph_count) };
59 assert!(!glyph_infos.is_null());
60 let mut pos_count = 0;
61 let pos_infos = unsafe { hb_buffer_get_glyph_positions(buffer, &mut pos_count) };
62 assert!(!pos_infos.is_null());
63 assert_eq!(glyph_count, pos_count);
64
65 ShapedGlyphData {
66 count: glyph_count as usize,
67 buffer,
68 glyph_infos,
69 pos_infos,
70 }
71 }
72}
73
74impl Drop for ShapedGlyphData {
75 fn drop(&mut self) {
76 unsafe { hb_buffer_destroy(self.buffer) }
77 }
78}
79
80impl HarfBuzzShapedGlyphData for ShapedGlyphData {
81 #[inline]
82 fn len(&self) -> usize {
83 self.count
84 }
85
86 #[inline(always)]
87 fn byte_offset_of_glyph(&self, i: usize) -> u32 {
88 assert!(i < self.count);
89
90 unsafe {
91 let glyph_info_i = self.glyph_infos.add(i);
92 (*glyph_info_i).cluster
93 }
94 }
95
96 fn entry_for_glyph(&self, i: usize, y_pos: &mut Au) -> ShapedGlyphEntry {
98 assert!(i < self.count);
99
100 unsafe {
101 let glyph_info_i = self.glyph_infos.add(i);
102 let pos_info_i = self.pos_infos.add(i);
103 let x_offset = Shaper::fixed_to_float((*pos_info_i).x_offset);
104 let y_offset = Shaper::fixed_to_float((*pos_info_i).y_offset);
105 let x_advance = Shaper::fixed_to_float((*pos_info_i).x_advance);
106 let y_advance = Shaper::fixed_to_float((*pos_info_i).y_advance);
107
108 let x_offset = Au::from_f64_px(x_offset);
109 let y_offset = Au::from_f64_px(y_offset);
110 let x_advance = Au::from_f64_px(x_advance);
111 let y_advance = Au::from_f64_px(y_advance);
112
113 let offset = if x_offset.is_zero() && y_offset.is_zero() && y_advance.is_zero() {
114 None
115 } else {
116 if y_advance > Au::zero() {
118 *y_pos -= y_advance;
119 }
120
121 Some(Point2D::new(x_offset, *y_pos - y_offset))
122 };
123
124 ShapedGlyphEntry {
125 codepoint: (*glyph_info_i).codepoint as GlyphId,
126 advance: x_advance,
127 offset,
128 }
129 }
130 }
131}
132
133#[derive(Debug)]
134pub(crate) struct Shaper {
135 hb_face: *mut hb_face_t,
136 hb_font: *mut hb_font_t,
137 font: *const Font,
138}
139
140unsafe impl Sync for Shaper {}
144unsafe impl Send for Shaper {}
145
146impl Drop for Shaper {
147 fn drop(&mut self) {
148 unsafe {
149 assert!(!self.hb_face.is_null());
150 hb_face_destroy(self.hb_face);
151
152 assert!(!self.hb_font.is_null());
153 hb_font_destroy(self.hb_font);
154 }
155 }
156}
157
158impl Shaper {
159 pub(crate) fn new(font: &Font) -> Shaper {
160 unsafe {
161 let hb_face: *mut hb_face_t = hb_face_create_for_tables(
162 Some(font_table_func),
163 font as *const Font as *mut c_void,
164 None,
165 );
166 let hb_font: *mut hb_font_t = hb_font_create(hb_face);
167
168 let pt_size = font.descriptor.pt_size.to_f64_px();
170 hb_font_set_ppem(hb_font, pt_size as c_uint, pt_size as c_uint);
171
172 hb_font_set_scale(
174 hb_font,
175 Shaper::float_to_fixed(pt_size) as c_int,
176 Shaper::float_to_fixed(pt_size) as c_int,
177 );
178
179 hb_font_set_funcs(
181 hb_font,
182 HB_FONT_FUNCS.0,
183 font as *const Font as *mut c_void,
184 None,
185 );
186
187 if servo_config::pref!(layout_variable_fonts_enabled) {
188 let variations = &font.variations();
189 if !variations.is_empty() {
190 let variations: Vec<_> = variations
191 .iter()
192 .map(|variation| hb_variation_t {
193 tag: variation.tag,
194
195 value: variation.value,
196 })
197 .collect();
198
199 hb_font_set_variations(hb_font, variations.as_ptr(), variations.len() as u32);
200 }
201 }
202
203 Shaper {
204 hb_face,
205 hb_font,
206 font,
207 }
208 }
209 }
210
211 fn shaped_glyph_data(&self, text: &str, options: &ShapingOptions) -> ShapedGlyphData {
213 unsafe {
214 let hb_buffer: *mut hb_buffer_t = hb_buffer_create();
215 hb_buffer_set_direction(
216 hb_buffer,
217 if options.flags.contains(ShapingFlags::RTL_FLAG) {
218 HB_DIRECTION_RTL
219 } else {
220 HB_DIRECTION_LTR
221 },
222 );
223
224 let script =
225 hb_script_from_iso15924_tag(unicode_script_to_iso15924_tag(options.script));
226 hb_buffer_set_script(hb_buffer, script);
227
228 hb_buffer_add_utf8(
229 hb_buffer,
230 text.as_ptr() as *const c_char,
231 text.len() as c_int,
232 0,
233 text.len() as c_int,
234 );
235
236 let mut features = Vec::new();
237 if options
238 .flags
239 .contains(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG)
240 {
241 features.push(hb_feature_t {
242 tag: u32::from_be_bytes(LIGA.to_be_bytes()),
243 value: 0,
244 start: 0,
245 end: hb_buffer_get_length(hb_buffer),
246 })
247 }
248 if options
249 .flags
250 .contains(ShapingFlags::DISABLE_KERNING_SHAPING_FLAG)
251 {
252 features.push(hb_feature_t {
253 tag: u32::from_be_bytes(KERN.to_be_bytes()),
254 value: 0,
255 start: 0,
256 end: hb_buffer_get_length(hb_buffer),
257 })
258 }
259
260 hb_shape(
261 self.hb_font,
262 hb_buffer,
263 features.as_mut_ptr(),
264 features.len() as u32,
265 );
266
267 ShapedGlyphData::new(hb_buffer)
268 }
269 }
270
271 fn font(&self) -> &Font {
272 unsafe { &(*self.font) }
273 }
274
275 pub(crate) fn shape_text(&self, text: &str, options: &ShapingOptions, glyphs: &mut GlyphStore) {
276 let glyph_data = self.shaped_glyph_data(text, options);
277 let font = self.font();
278 super::shape_text_harfbuzz(&glyph_data, font, text, options, glyphs);
279 }
280
281 pub(crate) fn baseline(&self) -> Option<FontBaseline> {
282 unsafe { (*self.font).table_for_tag(BASE)? };
283
284 let mut hanging_baseline = 0;
285 let mut alphabetic_baseline = 0;
286 let mut ideographic_baseline = 0;
287
288 unsafe {
289 hb_ot_layout_get_baseline(
290 self.hb_font,
291 HB_OT_LAYOUT_BASELINE_TAG_ROMAN,
292 HB_DIRECTION_LTR,
293 HB_OT_TAG_DEFAULT_SCRIPT,
294 HB_OT_TAG_DEFAULT_LANGUAGE,
295 &mut alphabetic_baseline as *mut _,
296 );
297
298 hb_ot_layout_get_baseline(
299 self.hb_font,
300 HB_OT_LAYOUT_BASELINE_TAG_HANGING,
301 HB_DIRECTION_LTR,
302 HB_OT_TAG_DEFAULT_SCRIPT,
303 HB_OT_TAG_DEFAULT_LANGUAGE,
304 &mut hanging_baseline as *mut _,
305 );
306
307 hb_ot_layout_get_baseline(
308 self.hb_font,
309 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
310 HB_DIRECTION_LTR,
311 HB_OT_TAG_DEFAULT_SCRIPT,
312 HB_OT_TAG_DEFAULT_LANGUAGE,
313 &mut ideographic_baseline as *mut _,
314 );
315 }
316
317 Some(FontBaseline {
318 ideographic_baseline: Shaper::fixed_to_float(ideographic_baseline) as f32,
319 alphabetic_baseline: Shaper::fixed_to_float(alphabetic_baseline) as f32,
320 hanging_baseline: Shaper::fixed_to_float(hanging_baseline) as f32,
321 })
322 }
323
324 fn float_to_fixed(f: f64) -> i32 {
325 float_to_fixed(16, f)
326 }
327
328 fn fixed_to_float(i: hb_position_t) -> f64 {
329 fixed_to_float(16, i)
330 }
331}
332
333struct FontFuncs(*mut hb_font_funcs_t);
335
336unsafe impl Sync for FontFuncs {}
337unsafe impl Send for FontFuncs {}
338
339static HB_FONT_FUNCS: LazyLock<FontFuncs> = LazyLock::new(|| unsafe {
340 let hb_funcs = hb_font_funcs_create();
341 hb_font_funcs_set_nominal_glyph_func(hb_funcs, Some(glyph_func), ptr::null_mut(), None);
342 hb_font_funcs_set_glyph_h_advance_func(
343 hb_funcs,
344 Some(glyph_h_advance_func),
345 ptr::null_mut(),
346 None,
347 );
348
349 FontFuncs(hb_funcs)
350});
351
352extern "C" fn glyph_func(
353 _: *mut hb_font_t,
354 font_data: *mut c_void,
355 unicode: hb_codepoint_t,
356 glyph: *mut hb_codepoint_t,
357 _: *mut c_void,
358) -> hb_bool_t {
359 let font: *const Font = font_data as *const Font;
360 assert!(!font.is_null());
361
362 match unsafe { (*font).glyph_index(char::from_u32(unicode).unwrap()) } {
363 Some(g) => {
364 unsafe { *glyph = g as hb_codepoint_t };
365 true as hb_bool_t
366 },
367 None => false as hb_bool_t,
368 }
369}
370
371extern "C" fn glyph_h_advance_func(
372 _: *mut hb_font_t,
373 font_data: *mut c_void,
374 glyph: hb_codepoint_t,
375 _: *mut c_void,
376) -> hb_position_t {
377 let font: *mut Font = font_data as *mut Font;
378 assert!(!font.is_null());
379
380 let advance = unsafe { (*font).glyph_h_advance(glyph as GlyphId) };
381 Shaper::float_to_fixed(advance)
382}
383
384extern "C" fn font_table_func(
386 _: *mut hb_face_t,
387 tag: hb_tag_t,
388 user_data: *mut c_void,
389) -> *mut hb_blob_t {
390 let font = user_data as *const Font;
392 assert!(!font.is_null());
393
394 let Some(font_table) = (unsafe { (*font).table_for_tag(Tag::from_u32(tag)) }) else {
396 return ptr::null_mut();
397 };
398
399 let font_table_ptr = Box::into_raw(Box::new(font_table));
403
404 let buf = unsafe { (*font_table_ptr).buffer() };
405 let blob = unsafe {
407 hb_blob_create(
408 buf.as_ptr() as *const c_char,
409 buf.len() as c_uint,
410 HB_MEMORY_MODE_READONLY,
411 font_table_ptr as *mut c_void,
412 Some(destroy_blob_func),
413 )
414 };
415
416 assert!(!blob.is_null());
417 blob
418}
419
420extern "C" fn destroy_blob_func(font_table_ptr: *mut c_void) {
421 unsafe {
422 drop(Box::from_raw(font_table_ptr as *mut FontTable));
423 }
424}