#![warn(missing_docs)] #![allow(dead_code)] //! Interface to Harfbuzz's WASM exports //! //! This crate is designed to make it easier to write a //! WASM shaper for your font using Rust. It binds the //! functions exported by Harfbuzz into the WASM runtime, //! and wraps them in an ergonomic interface using Rust //! structures. For example, here is a basic shaping engine: //! //! //! ```rust //! #[wasm_bindgen] //! pub fn shape(font_ref: u32, buf_ref: u32) -> i32 { //! let font = Font::from_ref(font_ref); //! let mut buffer = GlyphBuffer::from_ref(buf_ref); //! for mut item in buffer.glyphs.iter_mut() { //! // Map character to glyph //! item.codepoint = font.get_glyph(codepoint, 0); //! // Set advance width //! item.h_advance = font.get_glyph_h_advance(item.codepoint); //! } //! 1 //! } //! ``` use std::ffi::{c_int, CStr, CString};
#[cfg(feature = "kurbo")] use kurbo::BezPath;
// We don't use #[wasm_bindgen] here because that makes // assumptions about Javascript calling conventions. We // really do just want to import some C symbols and run // them in unsafe-land! extern"C" {
fn face_get_upem(face: u32) -> u32;
fn font_get_face(font: u32) -> u32;
fn font_get_glyph(font: u32, unicode: u32, uvs: u32) -> u32;
fn font_get_scale(font: u32, x_scale: *mut i32, y_scale: *mut i32);
fn font_get_glyph_extents(font: u32, glyph: u32, extents: *mut CGlyphExtents) -> bool;
fn font_glyph_to_string(font: u32, glyph: u32, str: *const u8, len: u32);
fn font_get_glyph_h_advance(font: u32, glyph: u32) -> i32;
fn font_get_glyph_v_advance(font: u32, glyph: u32) -> i32;
fn font_copy_glyph_outline(font: u32, glyph: u32, outline: *mut CGlyphOutline) -> bool;
fn face_copy_table(font: u32, tag: u32, blob: *mut Blob) -> bool;
fn buffer_copy_contents(buffer: u32, cbuffer: *mut CBufferContents) -> bool;
fn buffer_set_contents(buffer: u32, cbuffer: &CBufferContents) -> bool;
fn debugprint(s: *const u8);
fn shape_with(
font: u32,
buffer: u32,
features: u32,
num_features: u32,
shaper: *const u8,
) -> i32;
}
/// An opaque reference to a font at a given size and /// variation. It is equivalent to the `hb_font_t` pointer /// in Harfbuzz. #[derive(Debug)] pubstruct Font(u32);
impl Font { /// Initialize a `Font` struct from the reference provided /// by Harfbuzz to the `shape` function. pub fn from_ref(ptr: u32) -> Self { Self(ptr)
} /// Call the given Harfbuzz shaper on a buffer reference. /// /// For example, `font.shape_with(buffer_ref, "ot")` will /// run standard OpenType shaping, allowing you to modify /// the buffer contents after glyph mapping, substitution /// and positioning has taken place. pub fn shape_with(&self, buffer_ref: u32, shaper: &str) { let c_shaper = CString::new(shaper).unwrap(); unsafe {
shape_with(self.0, buffer_ref, 0, 0, c_shaper.as_ptr() as *const u8);
}
}
/// Return the font face object that this font belongs to. pub fn get_face(&self) -> Face {
Face(unsafe { font_get_face(self.0) })
}
/// Map a Unicode codepoint to a glyph ID. /// /// The `uvs` parameter specifies a Unicode Variation /// Selector codepoint which is used in conjunction with /// [format 14 cmap tables](https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences) /// to provide alternate glyph mappings for characters with /// Unicode Variation Sequences. Generally you will pass /// `0`. pub fn get_glyph(&self, unicode: u32, uvs: u32) -> u32 { unsafe { font_get_glyph(self.0, unicode, uvs) }
}
/// Get the extents for a given glyph ID, in its design position. pub fn get_glyph_extents(&self, glyph: u32) -> CGlyphExtents { letmut extents = CGlyphExtents::default(); unsafe {
font_get_glyph_extents(self.0, glyph, &mut extents);
}
extents
}
/// Get the default advance width for a given glyph ID. pub fn get_glyph_h_advance(&self, glyph: u32) -> i32 { unsafe { font_get_glyph_h_advance(self.0, glyph) }
}
/// Get the default vertical advance for a given glyph ID.
fn get_glyph_v_advance(&self, glyph: u32) -> i32 { unsafe { font_get_glyph_v_advance(self.0, glyph) }
}
/// Get the name of a glyph. /// /// If no names are provided by the font, names of the form /// `gidXXX` are constructed. pub fn get_glyph_name(&self, glyph: u32) -> String { letmut s = [1u8; 32]; unsafe {
font_glyph_to_string(self.0, glyph, s.as_mut_ptr(), 32);
} unsafe { CStr::from_ptr(s.as_ptr() as *const _) }
.to_str()
.unwrap()
.to_string()
}
/// Get the X and Y scale factor applied to this font. /// /// This should be divided by the units per em value to /// provide a scale factor mapping from design units to /// user units. (See [`Face::get_upem`].) pub fn get_scale(&self) -> (i32, i32) { letmut x_scale: i32 = 0; letmut y_scale: i32 = 0; unsafe {
font_get_scale( self.0,
&mut x_scale as *mut c_int,
&mut y_scale as *mut c_int,
)
};
(x_scale, y_scale)
}
#[cfg(feature = "kurbo")] /// Get the outline of a glyph as a vector of bezier paths pub fn get_outline(&self, glyph: u32) -> Vec<BezPath> { letmut outline = CGlyphOutline {
n_points: 0,
points: std::ptr::null_mut(),
n_contours: 0,
contours: std::ptr::null_mut(),
}; let end_pts_of_contours: &[usize] = unsafe {
font_copy_glyph_outline(self.0, glyph, &mut outline);
std::slice::from_raw_parts(outline.contours, outline.n_contours as usize)
}; let points: &[CGlyphOutlinePoint] = unsafe { std::slice::from_raw_parts(outline.points, outline.n_points as usize) }; letmut results: Vec<BezPath> = vec![]; letmut start_pt: usize = 0;
for end_pt in end_pts_of_contours { let this_contour = &points[start_pt..*end_pt];
start_pt = *end_pt; letmut path = BezPath::new(); letmut ix = 0; while ix < this_contour.len() { let point = &this_contour[ix]; match point.pointtype {
PointType::MoveTo => path.move_to((point.x as f64, point.y as f64)),
PointType::LineTo => path.line_to((point.x as f64, point.y as f64)),
PointType::QuadraticTo => {
ix += 1; let end_pt = &this_contour[ix];
path.quad_to(
(point.x as f64, point.y as f64),
(end_pt.x as f64, end_pt.y as f64),
);
}
PointType::CubicTo => {
ix += 1; let mid_pt = &this_contour[ix];
ix += 1; let end_pt = &this_contour[ix];
path.curve_to(
(point.x as f64, point.y as f64),
(mid_pt.x as f64, mid_pt.y as f64),
(end_pt.x as f64, end_pt.y as f64),
);
}
}
ix += 1;
}
path.close_path();
results.push(path);
}
results
}
}
/// An opaque reference to a font face, equivalent to the `hb_face_t` pointer /// in Harfbuzz. /// /// This is generally returned from [`Font::get_face`]. #[derive(Debug)] pubstruct Face(u32);
impl Face { /// Get a blob containing the contents of the given binary font table. pub fn reference_table(&self, tag: &str) -> Blob { letmut tag_u: u32 = 0; letmut chars = tag.chars();
tag_u |= (chars.next().unwrap() as u32) << 24;
tag_u |= (chars.next().unwrap() as u32) << 16;
tag_u |= (chars.next().unwrap() as u32) << 8;
tag_u |= chars.next().unwrap() as u32; letmut blob = Blob {
data: std::ptr::null_mut(),
length: 0,
}; unsafe {
face_copy_table(self.0, tag_u, &mut blob);
}
blob
}
/// Get the face's design units per em. pub fn get_upem(&self) -> u32 { unsafe { face_get_upem(self.0) }
}
}
/// Trait implemented by custom structs representing buffer items pubtrait BufferItem { /// Construct an item in your preferred representation out of the info and position data provided by Harfbuzz.
fn from_c(info: CGlyphInfo, position: CGlyphPosition) -> Self; /// Return info and position data to Harfbuzz.
fn to_c(self) -> (CGlyphInfo, CGlyphPosition);
}
/// Generic representation of a Harfbuzz buffer item. /// /// By making this generic, we allow you to implement your own /// representations of buffer items; for example, in your shaper, /// you may want certain fields to keep track of the glyph's name, /// extents, or shape, so you would want a custom struct to represent /// buffer items. If you don't care about any of them, use the /// supplied `GlyphBuffer` struct. #[derive(Debug)] pubstruct Buffer<T: BufferItem> {
_ptr: u32, /// Glyphs in the buffer pub glyphs: Vec<T>,
}
impl<T: BufferItem> Buffer<T> { /// Construct a buffer from the pointer Harfbuzz provides to the WASM. /// /// The `Buffer` struct implements Drop, meaning that when the shaping /// function is finished, the buffer contents are sent back to Harfbuzz. pub fn from_ref(ptr: u32) -> Self { letmut c_contents = CBufferContents {
info: std::ptr::null_mut(),
position: std::ptr::null_mut(),
length: 0,
};
impl<T: BufferItem> Drop for Buffer<T> {
fn drop(&mutself) { letmut positions: Vec<CGlyphPosition>; letmut infos: Vec<CGlyphInfo>; let glyphs = std::mem::take(&mutself.glyphs);
(infos, positions) = glyphs.into_iter().map(|g| g.to_c()).unzip(); let c_contents = CBufferContents {
length: positions.len() as u32,
info: infos[..].as_mut_ptr(),
position: positions[..].as_mut_ptr(),
}; unsafe { if !buffer_set_contents(self._ptr, &c_contents) {
panic!("Couldn't set buffer contents");
}
}
}
}
/// Some data provided by Harfbuzz. #[derive(Debug)] #[repr(C)] pubstruct Blob { /// Length of the blob in bytes pub length: u32, /// A raw pointer to the contents pub data: *mut u8,
}
/// Glyph information in a buffer item provided by Harfbuzz /// /// You'll only need to interact with this if you're writing /// your own buffer item structure. #[repr(C)] #[derive(Debug, Clone)] pubstruct CGlyphInfo { pub codepoint: u32, pub mask: u32, pub cluster: u32, pub var1: u32, pub var2: u32,
}
/// Glyph positioning information in a buffer item provided by Harfbuzz /// /// You'll only need to interact with this if you're writing /// your own buffer item structure. #[derive(Debug, Clone)] #[repr(C)] pubstruct CGlyphPosition { pub x_advance: i32, pub y_advance: i32, pub x_offset: i32, pub y_offset: i32, pub var: u32,
}
/// Glyph extents #[derive(Debug, Clone, Default)] #[repr(C)] pubstruct CGlyphExtents { /// The scaled left side bearing of the glyph pub x_bearing: i32, /// The scaled coordinate of the top of the glyph pub y_bearing: i32, /// The width of the glyph pub width: i32, /// The height of the glyph pub height: i32,
}
/// Ergonomic representation of a Harfbuzz buffer item /// /// Harfbuzz buffers are normally split into two arrays, /// one representing glyph information and the other /// representing glyph positioning. In Rust, this would /// require lots of zipping and unzipping, so we zip them /// together into a single structure for you. #[derive(Debug, Clone, Copy)] pubstruct Glyph { /// The Unicode codepoint or glyph ID of the item pub codepoint: u32, /// The index of the cluster in the input text where this came from pub cluster: u32, /// The horizontal advance of the glyph pub x_advance: i32, /// The vertical advance of the glyph pub y_advance: i32, /// The horizontal offset of the glyph pub x_offset: i32, /// The vertical offset of the glyph pub y_offset: i32, /// You can use this for whatever you like pub flags: u32,
} impl BufferItem for Glyph {
fn from_c(info: CGlyphInfo, pos: CGlyphPosition) -> Self { Self {
codepoint: info.codepoint,
cluster: info.cluster,
x_advance: pos.x_advance,
y_advance: pos.y_advance,
x_offset: pos.x_offset,
y_offset: pos.y_offset,
flags: 0,
}
}
fn to_c(self) -> (CGlyphInfo, CGlyphPosition) { let info = CGlyphInfo {
codepoint: self.codepoint,
cluster: self.cluster,
mask: 0,
var1: 0,
var2: 0,
}; let pos = CGlyphPosition {
x_advance: self.x_advance,
y_advance: self.y_advance,
x_offset: self.x_offset,
y_offset: self.y_offset,
var: 0,
};
(info, pos)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.