// Copyright 2023 The Servo Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::sys::*; use crate::traits::*; use crate::Error; use alloc::boxed::Box; use core::ffi::c_void; /// Converts from `hb_codepoint_t`, assumed to be valid, to `char`. #[inline] fn hb_codepoint_t_to_char(input: hb_codepoint_t) -> char { unsafe { char::from_u32_unchecked(input) } } /// A builder for [`UnicodeFuncs`]. /// /// Not all of the functions need to be provided. If a function is missing, /// the one from the parent will be called, either empty (returning invalid /// placeholder data) or default (using the built-in Harfbuzz properties). #[non_exhaustive] #[derive(Debug)] pub struct UnicodeFuncsBuilder { raw: *mut hb_unicode_funcs_t, } impl UnicodeFuncsBuilder { /// Creates a new, empty builder. pub fn new_with_empty_parent() -> Result { let parent = unsafe { hb_unicode_funcs_get_empty() }; // The HarfBuzz refcounting convention is that "create" // sets refcount to one, not zero. // https://harfbuzz.github.io/object-model-lifecycle.html let ufuncs = unsafe { hb_unicode_funcs_create(parent) }; if core::ptr::eq(ufuncs, parent) { return Err(Error::Alloc); } Ok(Self { raw: ufuncs }) } /// Creates a new builder with the parent set to the HarfBuzz default. pub fn new_with_harfbuzz_default_parent() -> Result { let parent = unsafe { hb_unicode_funcs_get_default() }; // The HarfBuzz refcounting convention is that "create" // sets refcount to one, not zero. // https://harfbuzz.github.io/object-model-lifecycle.html let ufuncs = unsafe { hb_unicode_funcs_create(parent) }; if core::ptr::eq(ufuncs, parent) { return Err(Error::Alloc); } Ok(Self { raw: ufuncs }) } /// Sets an implementation of [`hb_unicode_general_category_func_t`]. pub fn set_general_category_func(&mut self, f: Box) { let general_category_ptr: *mut F = Box::into_raw(f); extern "C" fn impl_general_category( _ufuncs: *mut hb_unicode_funcs_t, unicode: hb_codepoint_t, user_data: *mut c_void, ) -> hb_unicode_general_category_t { unsafe { &*(user_data as *mut F) }.general_category(hb_codepoint_t_to_char(unicode)) as hb_unicode_general_category_t } extern "C" fn destroy_general_category(user_data: *mut c_void) { let _ = unsafe { Box::from_raw(user_data as *mut F) }; } unsafe { hb_unicode_funcs_set_general_category_func( self.raw, Some(impl_general_category::), general_category_ptr as *mut c_void, Some(destroy_general_category::), ); } } /// Sets an implementation of [`hb_unicode_combining_class_func_t`]. pub fn set_combining_class_func(&mut self, f: Box) { let combining_class_ptr: *mut F = Box::into_raw(f); extern "C" fn impl_combining_class( _ufuncs: *mut hb_unicode_funcs_t, unicode: hb_codepoint_t, user_data: *mut c_void, ) -> hb_unicode_combining_class_t { unsafe { &*(user_data as *mut F) }.combining_class(hb_codepoint_t_to_char(unicode)) as hb_unicode_combining_class_t } extern "C" fn destroy_combining_class(user_data: *mut c_void) { let _ = unsafe { Box::from_raw(user_data as *mut F) }; } unsafe { hb_unicode_funcs_set_combining_class_func( self.raw, Some(impl_combining_class::), combining_class_ptr as *mut c_void, Some(destroy_combining_class::), ); } } /// Sets an implementation of [`hb_unicode_mirroring_func_t`]. pub fn set_mirroring_func(&mut self, f: Box) { let mirroring_ptr: *mut F = Box::into_raw(f); extern "C" fn impl_mirroring( _ufuncs: *mut hb_unicode_funcs_t, unicode: hb_codepoint_t, user_data: *mut c_void, ) -> hb_codepoint_t { unsafe { &*(user_data as *mut F) }.mirroring(hb_codepoint_t_to_char(unicode)) as hb_codepoint_t } extern "C" fn destroy_mirroring(user_data: *mut c_void) { let _ = unsafe { Box::from_raw(user_data as *mut F) }; } unsafe { hb_unicode_funcs_set_mirroring_func( self.raw, Some(impl_mirroring::), mirroring_ptr as *mut c_void, Some(destroy_mirroring::), ); } } /// Sets an implementation of [`hb_unicode_script_func_t`]. pub fn set_script_func(&mut self, f: Box) { let script_ptr: *mut F = Box::into_raw(f); extern "C" fn impl_script( _ufuncs: *mut hb_unicode_funcs_t, unicode: hb_codepoint_t, user_data: *mut c_void, ) -> hb_codepoint_t { let code = unsafe { &*(user_data as *mut F) }.script(hb_codepoint_t_to_char(unicode)); unsafe { hb_script_from_string(code.as_ptr() as *const core::ffi::c_char, 4) } } extern "C" fn destroy_script(user_data: *mut c_void) { let _ = unsafe { Box::from_raw(user_data as *mut F) }; } unsafe { hb_unicode_funcs_set_script_func( self.raw, Some(impl_script::), script_ptr as *mut c_void, Some(destroy_script::), ); } } /// Sets an implementation of [`hb_unicode_compose_func_t`]. pub fn set_compose_func(&mut self, f: Box) { let compose_ptr: *mut F = Box::into_raw(f); extern "C" fn impl_compose( _ufuncs: *mut hb_unicode_funcs_t, a: hb_codepoint_t, b: hb_codepoint_t, ab: *mut hb_codepoint_t, user_data: *mut c_void, ) -> hb_bool_t { let result = unsafe { &*(user_data as *mut F) } .compose(hb_codepoint_t_to_char(a), hb_codepoint_t_to_char(b)); match result { Some(ab_x) => { unsafe { *ab = ab_x as hb_codepoint_t }; true as hb_bool_t } None => false as hb_bool_t, } } extern "C" fn destroy_compose(user_data: *mut c_void) { let _ = unsafe { Box::from_raw(user_data as *mut F) }; } unsafe { hb_unicode_funcs_set_compose_func( self.raw, Some(impl_compose::), compose_ptr as *mut c_void, Some(destroy_compose::), ); } } /// Sets an implementation of [`hb_unicode_decompose_func_t`]. pub fn set_decompose_func(&mut self, f: Box) { let decompose_ptr: *mut F = Box::into_raw(f); extern "C" fn impl_decompose( _ufuncs: *mut hb_unicode_funcs_t, ab: hb_codepoint_t, a: *mut hb_codepoint_t, b: *mut hb_codepoint_t, user_data: *mut c_void, ) -> hb_bool_t { let result = unsafe { &*(user_data as *mut F) }.decompose(hb_codepoint_t_to_char(ab)); match result { Some((a_x, b_x)) => { unsafe { *a = a_x as hb_codepoint_t }; unsafe { *b = b_x as hb_codepoint_t }; true as hb_bool_t } None => false as hb_bool_t, } } extern "C" fn destroy_decompose(user_data: *mut c_void) { let _ = unsafe { Box::from_raw(user_data as *mut F) }; } unsafe { hb_unicode_funcs_set_decompose_func( self.raw, Some(impl_decompose::), decompose_ptr as *mut c_void, Some(destroy_decompose::), ); } } /// Creates a [`UnicodeFuncs`] based on this builder. pub fn build(self) -> UnicodeFuncs { let raw = self.raw; unsafe { hb_unicode_funcs_make_immutable(raw); } core::mem::forget(self); // Safety: Ownership is being transferred unsafe { UnicodeFuncs::from_raw(raw) } } /// Takes ownership of a `*mut hb_unicode_funcs_t` without incrementing /// the refcount. /// /// # Safety /// /// After the call, the previous owner must not call /// `hb_unicode_funcs_destroy()`, since `UnicodeFuncs` will now /// take care of it. pub unsafe fn from_raw(raw: *mut hb_unicode_funcs_t) -> Self { Self { raw } } /// Transfers the ownership of the wrapped pointer to the caller. /// The caller is responsible for calling `hb_unicode_funcs_destroy()`; /// `UnicodeFuncs` will no longer take care of it. pub fn into_raw(self) -> *mut hb_unicode_funcs_t { let ret = self.raw; core::mem::forget(self); ret } } impl Drop for UnicodeFuncsBuilder { fn drop(&mut self) { unsafe { hb_unicode_funcs_destroy(self.raw); } } } /// A collection of functions to run Unicode algorithms required by HarfBuzz. /// /// Create one of these using a [`UnicodeFuncsBuilder`]. /// /// # Example /// /// Implementing a custom properties provider for HarfBuzz: /// /// ``` /// # use harfbuzz::{Buffer, Direction, UnicodeFuncsBuilder, sys}; /// struct PropertyProvider; /// /// impl harfbuzz::traits::GeneralCategoryFunc for PropertyProvider { /// fn general_category(&self, ch: char) -> harfbuzz::traits::GeneralCategory { /// todo!("GeneralCategoryFunc") /// } /// } /// /// impl harfbuzz::traits::CombiningClassFunc for PropertyProvider { /// fn combining_class(&self, ch: char) -> u8 { /// todo!("CombiningClassFunc") /// } /// } /// /// impl harfbuzz::traits::MirroringFunc for PropertyProvider { /// fn mirroring(&self, ch: char) -> char { /// todo!("MirroringFunc") /// } /// } /// /// impl harfbuzz::traits::ScriptFunc for PropertyProvider { /// fn script(&self, ch: char) -> [u8; 4] { /// debug_assert!(ch as u32 >= 0x0600 && ch as u32 <= 0x06FF); // Arab code points /// *b"Arab" /// } /// } /// /// impl harfbuzz::traits::ComposeFunc for PropertyProvider { /// fn compose(&self, a: char, b:char) -> Option { /// todo!("ComposeFunc") /// } /// } /// /// impl harfbuzz::traits::DecomposeFunc for PropertyProvider { /// fn decompose(&self, ab: char) -> Option<(char, char)> { /// todo!("DecomposeFunc") /// } /// } /// /// let mut unicode_funcs = UnicodeFuncsBuilder::new_with_empty_parent().unwrap(); /// unicode_funcs.set_general_category_func(Box::new(PropertyProvider)); /// unicode_funcs.set_combining_class_func(Box::new(PropertyProvider)); /// unicode_funcs.set_mirroring_func(Box::new(PropertyProvider)); /// unicode_funcs.set_script_func(Box::new(PropertyProvider)); /// unicode_funcs.set_compose_func(Box::new(PropertyProvider)); /// unicode_funcs.set_decompose_func(Box::new(PropertyProvider)); /// let unicode_funcs = unicode_funcs.build(); /// /// let mut b = Buffer::with("مساء الخير"); /// b.set_unicode_funcs(&unicode_funcs); /// b.guess_segment_properties(); /// assert_eq!(b.get_direction(), Direction::RTL); /// assert_eq!(b.get_script(), sys::HB_SCRIPT_ARABIC); /// ``` /// /// Without the provider, the results are unexpected: /// /// ``` /// # use harfbuzz::{Buffer, Direction, UnicodeFuncsBuilder, sys}; /// /// let mut unicode_funcs = UnicodeFuncsBuilder::new_with_empty_parent().unwrap(); /// let unicode_funcs = unicode_funcs.build(); /// /// let mut b = Buffer::with("مساء الخير"); /// b.set_unicode_funcs(&unicode_funcs); /// b.guess_segment_properties(); /// assert_eq!(b.get_direction(), Direction::LTR); // WRONG! /// assert_eq!(b.get_script(), sys::HB_SCRIPT_INVALID); // WRONG! /// ``` pub struct UnicodeFuncs { raw: *mut hb_unicode_funcs_t, } impl UnicodeFuncs { /// Takes ownership of a `*mut hb_unicode_funcs_t` without incrementing /// the refcount. /// /// # Safety /// /// After the call, the previous owner must not call /// `hb_unicode_funcs_destroy()`, since `UnicodeFuncs` will now /// take care of it. pub unsafe fn from_raw(raw: *mut hb_unicode_funcs_t) -> Self { Self { raw } } /// Transfers the ownership of the wrapped pointer to the caller. /// The caller is responsible for calling `hb_unicode_funcs_destroy()`; /// `UnicodeFuncs` will no longer take care of it. pub fn into_raw(self) -> *mut hb_unicode_funcs_t { let ret = self.raw; core::mem::forget(self); ret } /// Borrows the wrapped raw pointer without transferring ownership /// and without affecting the refcount. pub fn as_ptr(&self) -> *mut hb_unicode_funcs_t { self.raw } } impl Drop for UnicodeFuncs { fn drop(&mut self) { unsafe { hb_unicode_funcs_destroy(self.raw); } } }