xref: /aosp_15_r20/external/harfbuzz_ng/src/wasm/rust/harfbuzz-wasm/src/lib.rs (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
1 #![warn(missing_docs)]
2 #![allow(dead_code)]
3 //! Interface to Harfbuzz's WASM exports
4 //!
5 //! This crate is designed to make it easier to write a
6 //! WASM shaper for your font using Rust. It binds the
7 //! functions exported by Harfbuzz into the WASM runtime,
8 //! and wraps them in an ergonomic interface using Rust
9 //! structures. For example, here is a basic shaping engine:
10 //!
11 //!
12 //! ```rust
13 //! #[wasm_bindgen]
14 //! pub fn shape(font_ref: u32, buf_ref: u32) -> i32 {
15 //!     let font = Font::from_ref(font_ref);
16 //!     let mut buffer = GlyphBuffer::from_ref(buf_ref);
17 //!     for mut item in buffer.glyphs.iter_mut() {
18 //!         // Map character to glyph
19 //!         item.codepoint = font.get_glyph(codepoint, 0);
20 //!         // Set advance width
21 //!         item.h_advance = font.get_glyph_h_advance(item.codepoint);
22 //!     }
23 //!     1
24 //! }
25 //! ```
26 use std::ffi::{c_int, CStr, CString};
27 
28 #[cfg(feature = "kurbo")]
29 use kurbo::BezPath;
30 
31 // We don't use #[wasm_bindgen] here because that makes
32 // assumptions about Javascript calling conventions. We
33 // really do just want to import some C symbols and run
34 // them in unsafe-land!
35 extern "C" {
face_get_upem(face: u32) -> u3236     fn face_get_upem(face: u32) -> u32;
font_get_face(font: u32) -> u3237     fn font_get_face(font: u32) -> u32;
font_get_glyph(font: u32, unicode: u32, uvs: u32) -> u3238     fn font_get_glyph(font: u32, unicode: u32, uvs: u32) -> u32;
font_get_scale(font: u32, x_scale: *mut i32, y_scale: *mut i32)39     fn font_get_scale(font: u32, x_scale: *mut i32, y_scale: *mut i32);
font_get_glyph_extents(font: u32, glyph: u32, extents: *mut CGlyphExtents) -> bool40     fn font_get_glyph_extents(font: u32, glyph: u32, extents: *mut CGlyphExtents) -> bool;
font_glyph_to_string(font: u32, glyph: u32, str: *const u8, len: u32)41     fn font_glyph_to_string(font: u32, glyph: u32, str: *const u8, len: u32);
font_get_glyph_h_advance(font: u32, glyph: u32) -> i3242     fn font_get_glyph_h_advance(font: u32, glyph: u32) -> i32;
font_get_glyph_v_advance(font: u32, glyph: u32) -> i3243     fn font_get_glyph_v_advance(font: u32, glyph: u32) -> i32;
font_copy_glyph_outline(font: u32, glyph: u32, outline: *mut CGlyphOutline) -> bool44     fn font_copy_glyph_outline(font: u32, glyph: u32, outline: *mut CGlyphOutline) -> bool;
face_copy_table(font: u32, tag: u32, blob: *mut Blob) -> bool45     fn face_copy_table(font: u32, tag: u32, blob: *mut Blob) -> bool;
buffer_copy_contents(buffer: u32, cbuffer: *mut CBufferContents) -> bool46     fn buffer_copy_contents(buffer: u32, cbuffer: *mut CBufferContents) -> bool;
buffer_set_contents(buffer: u32, cbuffer: &CBufferContents) -> bool47     fn buffer_set_contents(buffer: u32, cbuffer: &CBufferContents) -> bool;
debugprint(s: *const u8)48     fn debugprint(s: *const u8);
shape_with( font: u32, buffer: u32, features: u32, num_features: u32, shaper: *const u8, ) -> i3249     fn shape_with(
50         font: u32,
51         buffer: u32,
52         features: u32,
53         num_features: u32,
54         shaper: *const u8,
55     ) -> i32;
56 }
57 
58 /// An opaque reference to a font at a given size and
59 /// variation. It is equivalent to the `hb_font_t` pointer
60 /// in Harfbuzz.
61 #[derive(Debug)]
62 pub struct Font(u32);
63 
64 impl Font {
65     /// Initialize a `Font` struct from the reference provided
66     /// by Harfbuzz to the `shape` function.
from_ref(ptr: u32) -> Self67     pub fn from_ref(ptr: u32) -> Self {
68         Self(ptr)
69     }
70     /// Call the given Harfbuzz shaper on a buffer reference.
71     ///
72     /// For example, `font.shape_with(buffer_ref, "ot")` will
73     /// run standard OpenType shaping, allowing you to modify
74     /// the buffer contents after glyph mapping, substitution
75     /// and positioning has taken place.
shape_with(&self, buffer_ref: u32, shaper: &str)76     pub fn shape_with(&self, buffer_ref: u32, shaper: &str) {
77         let c_shaper = CString::new(shaper).unwrap();
78         unsafe {
79             shape_with(self.0, buffer_ref, 0, 0, c_shaper.as_ptr() as *const u8);
80         }
81     }
82 
83     /// Return the font face object that this font belongs to.
get_face(&self) -> Face84     pub fn get_face(&self) -> Face {
85         Face(unsafe { font_get_face(self.0) })
86     }
87 
88     /// Map a Unicode codepoint to a glyph ID.
89     ///
90     /// The `uvs` parameter specifies a Unicode Variation
91     /// Selector codepoint which is used in conjunction with
92     /// [format 14 cmap tables](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences)
93     /// to provide alternate glyph mappings for characters with
94     /// Unicode Variation Sequences. Generally you will pass
95     /// `0`.
get_glyph(&self, unicode: u32, uvs: u32) -> u3296     pub fn get_glyph(&self, unicode: u32, uvs: u32) -> u32 {
97         unsafe { font_get_glyph(self.0, unicode, uvs) }
98     }
99 
100     /// Get the extents for a given glyph ID, in its design position.
get_glyph_extents(&self, glyph: u32) -> CGlyphExtents101     pub fn get_glyph_extents(&self, glyph: u32) -> CGlyphExtents {
102         let mut extents = CGlyphExtents::default();
103         unsafe {
104             font_get_glyph_extents(self.0, glyph, &mut extents);
105         }
106         extents
107     }
108 
109     /// Get the default advance width for a given glyph ID.
get_glyph_h_advance(&self, glyph: u32) -> i32110     pub fn get_glyph_h_advance(&self, glyph: u32) -> i32 {
111         unsafe { font_get_glyph_h_advance(self.0, glyph) }
112     }
113 
114     /// Get the default vertical advance for a given glyph ID.
get_glyph_v_advance(&self, glyph: u32) -> i32115     fn get_glyph_v_advance(&self, glyph: u32) -> i32 {
116         unsafe { font_get_glyph_v_advance(self.0, glyph) }
117     }
118 
119     /// Get the name of a glyph.
120     ///
121     /// If no names are provided by the font, names of the form
122     /// `gidXXX` are constructed.
get_glyph_name(&self, glyph: u32) -> String123     pub fn get_glyph_name(&self, glyph: u32) -> String {
124         let mut s = [1u8; 32];
125         unsafe {
126             font_glyph_to_string(self.0, glyph, s.as_mut_ptr(), 32);
127         }
128         unsafe { CStr::from_ptr(s.as_ptr() as *const _) }
129             .to_str()
130             .unwrap()
131             .to_string()
132     }
133 
134     /// Get the X and Y scale factor applied to this font.
135     ///
136     /// This should be divided by the units per em value to
137     /// provide a scale factor mapping from design units to
138     /// user units. (See [`Face::get_upem`].)
get_scale(&self) -> (i32, i32)139     pub fn get_scale(&self) -> (i32, i32) {
140         let mut x_scale: i32 = 0;
141         let mut y_scale: i32 = 0;
142         unsafe {
143             font_get_scale(
144                 self.0,
145                 &mut x_scale as *mut c_int,
146                 &mut y_scale as *mut c_int,
147             )
148         };
149         (x_scale, y_scale)
150     }
151 
152     #[cfg(feature = "kurbo")]
153     /// Get the outline of a glyph as a vector of bezier paths
get_outline(&self, glyph: u32) -> Vec<BezPath>154     pub fn get_outline(&self, glyph: u32) -> Vec<BezPath> {
155         let mut outline = CGlyphOutline {
156             n_points: 0,
157             points: std::ptr::null_mut(),
158             n_contours: 0,
159             contours: std::ptr::null_mut(),
160         };
161         let end_pts_of_contours: &[usize] = unsafe {
162             font_copy_glyph_outline(self.0, glyph, &mut outline);
163             std::slice::from_raw_parts(outline.contours, outline.n_contours as usize)
164         };
165         let points: &[CGlyphOutlinePoint] =
166             unsafe { std::slice::from_raw_parts(outline.points, outline.n_points as usize) };
167         let mut results: Vec<BezPath> = vec![];
168         let mut start_pt: usize = 0;
169         for end_pt in end_pts_of_contours {
170             let this_contour = &points[start_pt..*end_pt];
171             start_pt = *end_pt;
172             let mut path = BezPath::new();
173             let mut ix = 0;
174             while ix < this_contour.len() {
175                 let point = &this_contour[ix];
176                 match point.pointtype {
177                     PointType::MoveTo => path.move_to((point.x as f64, point.y as f64)),
178                     PointType::LineTo => path.line_to((point.x as f64, point.y as f64)),
179                     PointType::QuadraticTo => {
180                         ix += 1;
181                         let end_pt = &this_contour[ix];
182                         path.quad_to(
183                             (point.x as f64, point.y as f64),
184                             (end_pt.x as f64, end_pt.y as f64),
185                         );
186                     }
187                     PointType::CubicTo => {
188                         ix += 1;
189                         let mid_pt = &this_contour[ix];
190                         ix += 1;
191                         let end_pt = &this_contour[ix];
192                         path.curve_to(
193                             (point.x as f64, point.y as f64),
194                             (mid_pt.x as f64, mid_pt.y as f64),
195                             (end_pt.x as f64, end_pt.y as f64),
196                         );
197                     }
198                 }
199                 ix += 1;
200             }
201             path.close_path();
202             results.push(path);
203         }
204         results
205     }
206 }
207 
208 /// An opaque reference to a font face, equivalent to the `hb_face_t` pointer
209 /// in Harfbuzz.
210 ///
211 /// This is generally returned from [`Font::get_face`].
212 #[derive(Debug)]
213 pub struct Face(u32);
214 
215 impl Face {
216     /// Get a blob containing the contents of the given binary font table.
reference_table(&self, tag: &str) -> Blob217     pub fn reference_table(&self, tag: &str) -> Blob {
218         let mut tag_u: u32 = 0;
219         let mut chars = tag.chars();
220         tag_u |= (chars.next().unwrap() as u32) << 24;
221         tag_u |= (chars.next().unwrap() as u32) << 16;
222         tag_u |= (chars.next().unwrap() as u32) << 8;
223         tag_u |= chars.next().unwrap() as u32;
224         let mut blob = Blob {
225             data: std::ptr::null_mut(),
226             length: 0,
227         };
228         unsafe {
229             face_copy_table(self.0, tag_u, &mut blob);
230         }
231         blob
232     }
233 
234     /// Get the face's design units per em.
get_upem(&self) -> u32235     pub fn get_upem(&self) -> u32 {
236         unsafe { face_get_upem(self.0) }
237     }
238 }
239 
240 /// Trait implemented by custom structs representing buffer items
241 pub trait BufferItem {
242     /// Construct an item in your preferred representation out of the info and position data provided by Harfbuzz.
from_c(info: CGlyphInfo, position: CGlyphPosition) -> Self243     fn from_c(info: CGlyphInfo, position: CGlyphPosition) -> Self;
244     /// Return info and position data to Harfbuzz.
to_c(self) -> (CGlyphInfo, CGlyphPosition)245     fn to_c(self) -> (CGlyphInfo, CGlyphPosition);
246 }
247 
248 /// Generic representation of a Harfbuzz buffer item.
249 ///
250 /// By making this generic, we allow you to implement your own
251 /// representations of buffer items; for example, in your shaper,
252 /// you may want certain fields to keep track of the glyph's name,
253 /// extents, or shape, so you would want a custom struct to represent
254 /// buffer items. If you don't care about any of them, use the
255 /// supplied `GlyphBuffer` struct.
256 #[derive(Debug)]
257 pub struct Buffer<T: BufferItem> {
258     _ptr: u32,
259     /// Glyphs in the buffer
260     pub glyphs: Vec<T>,
261 }
262 
263 impl<T: BufferItem> Buffer<T> {
264     /// Construct a buffer from the pointer Harfbuzz provides to the WASM.
265     ///
266     /// The `Buffer` struct implements Drop, meaning that when the shaping
267     /// function is finished, the buffer contents are sent back to Harfbuzz.
from_ref(ptr: u32) -> Self268     pub fn from_ref(ptr: u32) -> Self {
269         let mut c_contents = CBufferContents {
270             info: std::ptr::null_mut(),
271             position: std::ptr::null_mut(),
272             length: 0,
273         };
274 
275         unsafe {
276             buffer_copy_contents(ptr, &mut c_contents) || panic!("Couldn't copy buffer contents")
277         };
278         let positions: Vec<CGlyphPosition> = unsafe {
279             std::slice::from_raw_parts(c_contents.position, c_contents.length as usize).to_vec()
280         };
281         let infos: Vec<CGlyphInfo> = unsafe {
282             std::slice::from_raw_parts(c_contents.info, c_contents.length as usize).to_vec()
283         };
284         Buffer {
285             glyphs: infos
286                 .into_iter()
287                 .zip(positions.into_iter())
288                 .map(|(i, p)| T::from_c(i, p))
289                 .collect(),
290             _ptr: ptr,
291         }
292     }
293 }
294 
295 impl<T: BufferItem> Drop for Buffer<T> {
drop(&mut self)296     fn drop(&mut self) {
297         let mut positions: Vec<CGlyphPosition>;
298         let mut infos: Vec<CGlyphInfo>;
299         let glyphs = std::mem::take(&mut self.glyphs);
300         (infos, positions) = glyphs.into_iter().map(|g| g.to_c()).unzip();
301         let c_contents = CBufferContents {
302             length: positions.len() as u32,
303             info: infos[..].as_mut_ptr(),
304             position: positions[..].as_mut_ptr(),
305         };
306         unsafe {
307             if !buffer_set_contents(self._ptr, &c_contents) {
308                 panic!("Couldn't set buffer contents");
309             }
310         }
311     }
312 }
313 
314 /// Some data provided by Harfbuzz.
315 #[derive(Debug)]
316 #[repr(C)]
317 pub struct Blob {
318     /// Length of the blob in bytes
319     pub length: u32,
320     /// A raw pointer to the contents
321     pub data: *mut u8,
322 }
323 
324 /// Glyph information in a buffer item provided by Harfbuzz
325 ///
326 /// You'll only need to interact with this if you're writing
327 /// your own buffer item structure.
328 #[repr(C)]
329 #[derive(Debug, Clone)]
330 pub struct CGlyphInfo {
331     pub codepoint: u32,
332     pub mask: u32,
333     pub cluster: u32,
334     pub var1: u32,
335     pub var2: u32,
336 }
337 
338 /// Glyph positioning information in a buffer item provided by Harfbuzz
339 ///
340 /// You'll only need to interact with this if you're writing
341 /// your own buffer item structure.
342 #[derive(Debug, Clone)]
343 #[repr(C)]
344 pub struct CGlyphPosition {
345     pub x_advance: i32,
346     pub y_advance: i32,
347     pub x_offset: i32,
348     pub y_offset: i32,
349     pub var: u32,
350 }
351 
352 /// Glyph extents
353 #[derive(Debug, Clone, Default)]
354 #[repr(C)]
355 pub struct CGlyphExtents {
356     /// The scaled left side bearing of the glyph
357     pub x_bearing: i32,
358     /// The scaled coordinate of the top of the glyph
359     pub y_bearing: i32,
360     /// The width of the glyph
361     pub width: i32,
362     /// The height of the glyph
363     pub height: i32,
364 }
365 
366 #[derive(Debug)]
367 #[repr(C)]
368 struct CBufferContents {
369     length: u32,
370     info: *mut CGlyphInfo,
371     position: *mut CGlyphPosition,
372 }
373 
374 /// Ergonomic representation of a Harfbuzz buffer item
375 ///
376 /// Harfbuzz buffers are normally split into two arrays,
377 /// one representing glyph information and the other
378 /// representing glyph positioning. In Rust, this would
379 /// require lots of zipping and unzipping, so we zip them
380 /// together into a single structure for you.
381 #[derive(Debug, Clone, Copy)]
382 pub struct Glyph {
383     /// The Unicode codepoint or glyph ID of the item
384     pub codepoint: u32,
385     /// The index of the cluster in the input text where this came from
386     pub cluster: u32,
387     /// The horizontal advance of the glyph
388     pub x_advance: i32,
389     /// The vertical advance of the glyph
390     pub y_advance: i32,
391     /// The horizontal offset of the glyph
392     pub x_offset: i32,
393     /// The vertical offset of the glyph
394     pub y_offset: i32,
395     /// You can use this for whatever you like
396     pub flags: u32,
397 }
398 impl BufferItem for Glyph {
from_c(info: CGlyphInfo, pos: CGlyphPosition) -> Self399     fn from_c(info: CGlyphInfo, pos: CGlyphPosition) -> Self {
400         Self {
401             codepoint: info.codepoint,
402             cluster: info.cluster,
403             x_advance: pos.x_advance,
404             y_advance: pos.y_advance,
405             x_offset: pos.x_offset,
406             y_offset: pos.y_offset,
407             flags: 0,
408         }
409     }
to_c(self) -> (CGlyphInfo, CGlyphPosition)410     fn to_c(self) -> (CGlyphInfo, CGlyphPosition) {
411         let info = CGlyphInfo {
412             codepoint: self.codepoint,
413             cluster: self.cluster,
414             mask: 0,
415             var1: 0,
416             var2: 0,
417         };
418         let pos = CGlyphPosition {
419             x_advance: self.x_advance,
420             y_advance: self.y_advance,
421             x_offset: self.x_offset,
422             y_offset: self.y_offset,
423             var: 0,
424         };
425         (info, pos)
426     }
427 }
428 
429 #[repr(C)]
430 #[allow(clippy::enum_variant_names)]
431 #[derive(Clone, Debug)]
432 enum PointType {
433     MoveTo,
434     LineTo,
435     QuadraticTo,
436     CubicTo,
437 }
438 
439 #[repr(C)]
440 #[derive(Clone, Debug)]
441 struct CGlyphOutlinePoint {
442     x: f32,
443     y: f32,
444     pointtype: PointType,
445 }
446 
447 #[repr(C)]
448 struct CGlyphOutline {
449     n_points: usize,
450     points: *mut CGlyphOutlinePoint,
451     n_contours: usize,
452     contours: *mut usize,
453 }
454 
455 /// Our default buffer item struct. See also [`Glyph`].
456 pub type GlyphBuffer = Buffer<Glyph>;
457 
458 /// Write a string to the Harfbuzz debug log.
debug(s: &str)459 pub fn debug(s: &str) {
460     let c_s = CString::new(s).unwrap();
461     unsafe {
462         debugprint(c_s.as_ptr() as *const u8);
463     };
464 }
465