Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lib.rs   Sprache: unbekannt

 
#![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)]
pub struct 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 {
        let mut 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 {
        let mut 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) {
        let mut x_scale: i32 = 0;
        let mut 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> {
        let mut 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) };
        let mut results: Vec<BezPath> = vec![];
        let mut start_pt: usize = 0;
        for end_pt in end_pts_of_contours {
            let this_contour = &points[start_pt..*end_pt];
            start_pt = *end_pt;
            let mut path = BezPath::new();
            let mut 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)]
pub struct Face(u32);

impl Face {
    /// Get a blob containing the contents of the given binary font table.
    pub fn reference_table(&self, tag: &str) -> Blob {
        let mut tag_u: u32 = 0;
        let mut 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;
        let mut 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
pub trait 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)]
pub struct 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 {
        let mut c_contents = CBufferContents {
            info: std::ptr::null_mut(),
            position: std::ptr::null_mut(),
            length: 0,
        };

        unsafe {
            buffer_copy_contents(ptr, &mut c_contents) || panic!("Couldn't copy buffer contents")
        };
        let positions: Vec<CGlyphPosition> = unsafe {
            std::slice::from_raw_parts(c_contents.position, c_contents.length as usize).to_vec()
        };
        let infos: Vec<CGlyphInfo> = unsafe {
            std::slice::from_raw_parts(c_contents.info, c_contents.length as usize).to_vec()
        };
        Buffer {
            glyphs: infos
                .into_iter()
                .zip(positions.into_iter())
                .map(|(i, p)| T::from_c(i, p))
                .collect(),
            _ptr: ptr,
        }
    }
}

impl<T: BufferItem> Drop for Buffer<T> {
    fn drop(&mut self) {
        let mut positions: Vec<CGlyphPosition>;
        let mut infos: Vec<CGlyphInfo>;
        let glyphs = std::mem::take(&mut self.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)]
pub struct 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)]
pub struct 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)]
pub struct 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)]
pub struct 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,
}

#[derive(Debug)]
#[repr(C)]
struct CBufferContents {
    length: u32,
    info: *mut CGlyphInfo,
    position: *mut CGlyphPosition,
}

/// 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)]
pub struct 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)
    }
}

#[repr(C)]
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Debug)]
enum PointType {
    MoveTo,
    LineTo,
    QuadraticTo,
    CubicTo,
}

#[repr(C)]
#[derive(Clone, Debug)]
struct CGlyphOutlinePoint {
    x: f32,
    y: f32,
    pointtype: PointType,
}

#[repr(C)]
struct CGlyphOutline {
    n_points: usize,
    points: *mut CGlyphOutlinePoint,
    n_contours: usize,
    contours: *mut usize,
}

/// Our default buffer item struct. See also [`Glyph`].
pub type GlyphBuffer = Buffer<Glyph>;

/// Write a string to the Harfbuzz debug log.
pub fn debug(s: &str) {
    let c_s = CString::new(s).unwrap();
    unsafe {
        debugprint(c_s.as_ptr() as *const u8);
    };
}

[ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge