diff --git a/src/bindings.rs b/src/bindings.rs index 788c813..fce19d9 100644 --- a/src/bindings.rs +++ b/src/bindings.rs @@ -1430,145 +1430,14 @@ pub mod exports { arg0: *mut u8, arg1: *mut u8, arg2: usize, - ) -> *mut u8 { + ) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let len0 = arg2; - let result1 = T::add_font_from_buffer( + T::add_font_from_buffer( FontKitBorrow::lift(arg0 as u32 as usize).get(), _rt::Vec::from_raw_parts(arg1.cast(), len0, len0), ); - let ptr2 = _RET_AREA.0.as_mut_ptr().cast::(); - let vec8 = result1; - let len8 = vec8.len(); - let layout8 = _rt::alloc::Layout::from_size_align_unchecked(vec8.len() * 28, 4); - let result8 = if layout8.size() != 0 { - let ptr = _rt::alloc::alloc(layout8).cast::(); - if ptr.is_null() { - _rt::alloc::handle_alloc_error(layout8); - } - ptr - } else { - { - ::core::ptr::null_mut() - } - }; - for (i, e) in vec8.into_iter().enumerate() { - let base = result8.add(i * 28); - { - let super::super::super::super::__with_name0::FontKey { - weight: weight3, - italic: italic3, - stretch: stretch3, - family: family3, - variations: variations3, - } = e; - match weight3 { - Some(e) => { - *base.add(0).cast::() = (1i32) as u8; - *base.add(2).cast::() = (_rt::as_i32(e)) as u16; - } - None => { - *base.add(0).cast::() = (0i32) as u8; - } - }; - match italic3 { - Some(e) => { - *base.add(4).cast::() = (1i32) as u8; - *base.add(5).cast::() = (match e { - true => 1, - false => 0, - }) - as u8; - } - None => { - *base.add(4).cast::() = (0i32) as u8; - } - }; - match stretch3 { - Some(e) => { - *base.add(6).cast::() = (1i32) as u8; - *base.add(8).cast::() = (_rt::as_i32(e)) as u16; - } - None => { - *base.add(6).cast::() = (0i32) as u8; - } - }; - let vec4 = (family3.into_bytes()).into_boxed_slice(); - let ptr4 = vec4.as_ptr().cast::(); - let len4 = vec4.len(); - ::core::mem::forget(vec4); - *base.add(16).cast::() = len4; - *base.add(12).cast::<*mut u8>() = ptr4.cast_mut(); - let vec7 = variations3; - let len7 = vec7.len(); - let layout7 = - _rt::alloc::Layout::from_size_align_unchecked(vec7.len() * 12, 4); - let result7 = if layout7.size() != 0 { - let ptr = _rt::alloc::alloc(layout7).cast::(); - if ptr.is_null() { - _rt::alloc::handle_alloc_error(layout7); - } - ptr - } else { - { - ::core::ptr::null_mut() - } - }; - for (i, e) in vec7.into_iter().enumerate() { - let base = result7.add(i * 12); - { - let (t5_0, t5_1) = e; - let vec6 = (t5_0.into_bytes()).into_boxed_slice(); - let ptr6 = vec6.as_ptr().cast::(); - let len6 = vec6.len(); - ::core::mem::forget(vec6); - *base.add(4).cast::() = len6; - *base.add(0).cast::<*mut u8>() = ptr6.cast_mut(); - *base.add(8).cast::() = _rt::as_f32(t5_1); - } - } - *base.add(24).cast::() = len7; - *base.add(20).cast::<*mut u8>() = result7; - } - } - *ptr2.add(4).cast::() = len8; - *ptr2.add(0).cast::<*mut u8>() = result8; - ptr2 - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __post_return_method_font_kit_add_font_from_buffer< - T: GuestFontKit, - >( - arg0: *mut u8, - ) { - let l7 = *arg0.add(0).cast::<*mut u8>(); - let l8 = *arg0.add(4).cast::(); - let base9 = l7; - let len9 = l8; - for i in 0..len9 { - let base = base9.add(i * 28); - { - let l0 = *base.add(12).cast::<*mut u8>(); - let l1 = *base.add(16).cast::(); - _rt::cabi_dealloc(l0, l1, 1); - let l4 = *base.add(20).cast::<*mut u8>(); - let l5 = *base.add(24).cast::(); - let base6 = l4; - let len6 = l5; - for i in 0..len6 { - let base = base6.add(i * 12); - { - let l2 = *base.add(0).cast::<*mut u8>(); - let l3 = *base.add(4).cast::(); - _rt::cabi_dealloc(l2, l3, 1); - } - } - _rt::cabi_dealloc(base6, len6 * 12, 4); - } - } - _rt::cabi_dealloc(base9, len9 * 28, 4); } #[doc(hidden)] #[allow(non_snake_case)] @@ -1746,144 +1615,6 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_method_font_kit_font_keys_cabi( - arg0: *mut u8, - ) -> *mut u8 { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - let result0 = T::font_keys(FontKitBorrow::lift(arg0 as u32 as usize).get()); - let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); - let vec7 = result0; - let len7 = vec7.len(); - let layout7 = _rt::alloc::Layout::from_size_align_unchecked(vec7.len() * 28, 4); - let result7 = if layout7.size() != 0 { - let ptr = _rt::alloc::alloc(layout7).cast::(); - if ptr.is_null() { - _rt::alloc::handle_alloc_error(layout7); - } - ptr - } else { - { - ::core::ptr::null_mut() - } - }; - for (i, e) in vec7.into_iter().enumerate() { - let base = result7.add(i * 28); - { - let super::super::super::super::__with_name0::FontKey { - weight: weight2, - italic: italic2, - stretch: stretch2, - family: family2, - variations: variations2, - } = e; - match weight2 { - Some(e) => { - *base.add(0).cast::() = (1i32) as u8; - *base.add(2).cast::() = (_rt::as_i32(e)) as u16; - } - None => { - *base.add(0).cast::() = (0i32) as u8; - } - }; - match italic2 { - Some(e) => { - *base.add(4).cast::() = (1i32) as u8; - *base.add(5).cast::() = (match e { - true => 1, - false => 0, - }) - as u8; - } - None => { - *base.add(4).cast::() = (0i32) as u8; - } - }; - match stretch2 { - Some(e) => { - *base.add(6).cast::() = (1i32) as u8; - *base.add(8).cast::() = (_rt::as_i32(e)) as u16; - } - None => { - *base.add(6).cast::() = (0i32) as u8; - } - }; - let vec3 = (family2.into_bytes()).into_boxed_slice(); - let ptr3 = vec3.as_ptr().cast::(); - let len3 = vec3.len(); - ::core::mem::forget(vec3); - *base.add(16).cast::() = len3; - *base.add(12).cast::<*mut u8>() = ptr3.cast_mut(); - let vec6 = variations2; - let len6 = vec6.len(); - let layout6 = - _rt::alloc::Layout::from_size_align_unchecked(vec6.len() * 12, 4); - let result6 = if layout6.size() != 0 { - let ptr = _rt::alloc::alloc(layout6).cast::(); - if ptr.is_null() { - _rt::alloc::handle_alloc_error(layout6); - } - ptr - } else { - { - ::core::ptr::null_mut() - } - }; - for (i, e) in vec6.into_iter().enumerate() { - let base = result6.add(i * 12); - { - let (t4_0, t4_1) = e; - let vec5 = (t4_0.into_bytes()).into_boxed_slice(); - let ptr5 = vec5.as_ptr().cast::(); - let len5 = vec5.len(); - ::core::mem::forget(vec5); - *base.add(4).cast::() = len5; - *base.add(0).cast::<*mut u8>() = ptr5.cast_mut(); - *base.add(8).cast::() = _rt::as_f32(t4_1); - } - } - *base.add(24).cast::() = len6; - *base.add(20).cast::<*mut u8>() = result6; - } - } - *ptr1.add(4).cast::() = len7; - *ptr1.add(0).cast::<*mut u8>() = result7; - ptr1 - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __post_return_method_font_kit_font_keys( - arg0: *mut u8, - ) { - let l7 = *arg0.add(0).cast::<*mut u8>(); - let l8 = *arg0.add(4).cast::(); - let base9 = l7; - let len9 = l8; - for i in 0..len9 { - let base = base9.add(i * 28); - { - let l0 = *base.add(12).cast::<*mut u8>(); - let l1 = *base.add(16).cast::(); - _rt::cabi_dealloc(l0, l1, 1); - let l4 = *base.add(20).cast::<*mut u8>(); - let l5 = *base.add(24).cast::(); - let base6 = l4; - let len6 = l5; - for i in 0..len6 { - let base = base6.add(i * 12); - { - let l2 = *base.add(0).cast::<*mut u8>(); - let l3 = *base.add(4).cast::(); - _rt::cabi_dealloc(l2, l3, 1); - } - } - _rt::cabi_dealloc(base6, len6 * 12, 4); - } - } - _rt::cabi_dealloc(base9, len9 * 28, 4); - } - #[doc(hidden)] - #[allow(non_snake_case)] pub unsafe fn _export_method_font_kit_fonts_info_cabi( arg0: *mut u8, ) -> *mut u8 { @@ -2613,7 +2344,7 @@ pub mod exports { /// return the keys of added fonts. /// The file type is extracted from the buffer by checking /// magic numbers - fn add_font_from_buffer(&self, buffer: _rt::Vec) -> _rt::Vec; + fn add_font_from_buffer(&self, buffer: _rt::Vec); /// Search and add fonts from a path fn add_search_path(&self, path: _rt::String); /// Query font using a key, this API returns valid result @@ -2622,8 +2353,6 @@ pub mod exports { /// Using exact-match method to directly obtain a font, /// skipping the querying logic fn exact_match(&self, key: FontKey) -> Option; - /// Get all registered fonts' keys - fn font_keys(&self) -> _rt::Vec; /// Get detailed info of all fonts registered fn fonts_info(&self) -> _rt::Vec; /// Get number of registered fonts @@ -2827,13 +2556,9 @@ pub mod exports { $($path_to_types)*::_export_constructor_font_kit_cabi::<<$ty as $($path_to_types)*::Guest>::FontKit>() } #[export_name = "alibaba:fontkit/fontkit-interface#[method]font-kit.add-font-from-buffer"] - unsafe extern "C" fn export_method_font_kit_add_font_from_buffer(arg0: *mut u8,arg1: *mut u8,arg2: usize,) -> *mut u8 { + unsafe extern "C" fn export_method_font_kit_add_font_from_buffer(arg0: *mut u8,arg1: *mut u8,arg2: usize,) { $($path_to_types)*::_export_method_font_kit_add_font_from_buffer_cabi::<<$ty as $($path_to_types)*::Guest>::FontKit>(arg0, arg1, arg2) } - #[export_name = "cabi_post_alibaba:fontkit/fontkit-interface#[method]font-kit.add-font-from-buffer"] - unsafe extern "C" fn _post_return_method_font_kit_add_font_from_buffer(arg0: *mut u8,) { - $($path_to_types)*::__post_return_method_font_kit_add_font_from_buffer::<<$ty as $($path_to_types)*::Guest>::FontKit>(arg0) - } #[export_name = "alibaba:fontkit/fontkit-interface#[method]font-kit.add-search-path"] unsafe extern "C" fn export_method_font_kit_add_search_path(arg0: *mut u8,arg1: *mut u8,arg2: usize,) { $($path_to_types)*::_export_method_font_kit_add_search_path_cabi::<<$ty as $($path_to_types)*::Guest>::FontKit>(arg0, arg1, arg2) @@ -2846,14 +2571,6 @@ pub mod exports { unsafe extern "C" fn export_method_font_kit_exact_match(arg0: *mut u8,arg1: i32,arg2: i32,arg3: i32,arg4: i32,arg5: i32,arg6: i32,arg7: *mut u8,arg8: usize,arg9: *mut u8,arg10: usize,) -> *mut u8 { $($path_to_types)*::_export_method_font_kit_exact_match_cabi::<<$ty as $($path_to_types)*::Guest>::FontKit>(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) } - #[export_name = "alibaba:fontkit/fontkit-interface#[method]font-kit.font-keys"] - unsafe extern "C" fn export_method_font_kit_font_keys(arg0: *mut u8,) -> *mut u8 { - $($path_to_types)*::_export_method_font_kit_font_keys_cabi::<<$ty as $($path_to_types)*::Guest>::FontKit>(arg0) - } - #[export_name = "cabi_post_alibaba:fontkit/fontkit-interface#[method]font-kit.font-keys"] - unsafe extern "C" fn _post_return_method_font_kit_font_keys(arg0: *mut u8,) { - $($path_to_types)*::__post_return_method_font_kit_font_keys::<<$ty as $($path_to_types)*::Guest>::FontKit>(arg0) - } #[export_name = "alibaba:fontkit/fontkit-interface#[method]font-kit.fonts-info"] unsafe extern "C" fn export_method_font_kit_fonts_info(arg0: *mut u8,) -> *mut u8 { $($path_to_types)*::_export_method_font_kit_fonts_info_cabi::<<$ty as $($path_to_types)*::Guest>::FontKit>(arg0) @@ -3214,11 +2931,11 @@ pub(crate) use __export_fontkit_impl as export; #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.25.0:fontkit:encoded world"] #[doc(hidden)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 2742] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xb8\x14\x01A\x02\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 2696] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x8a\x14\x01A\x02\x01\ A\x05\x01B\x06\x01k{\x01k\x7f\x01o\x02sv\x01p\x02\x01r\x05\x06weight\0\x06italic\ \x01\x07stretch\0\x06familys\x0avariations\x03\x04\0\x08font-key\x03\0\x04\x03\x01\ -\x17alibaba:fontkit/commons\x05\0\x02\x03\0\0\x08font-key\x01Bz\x02\x03\x02\x01\x01\ +\x17alibaba:fontkit/commons\x05\0\x02\x03\0\0\x08font-key\x01Bw\x02\x03\x02\x01\x01\ \x04\0\x08font-key\x03\0\0\x01r\x03\x02id{\x04names\x0blanguage-id{\x04\0\x04nam\ e\x03\0\x02\x01p\x03\x01ks\x01r\x04\x0bstyle-names\x04\x05names\x04\x04path\x05\x03\ key\x01\x04\0\x09font-info\x03\0\x06\x01r\x02\x08position|\x09thickness|\x04\0\x0c\ @@ -3256,19 +2973,18 @@ exts\0.\x04\0\x14[method]font.measure\x01/\x01@\x01\x04self(\0|\x04\0\x15[method \0\x19[method]font.units-per-em\x011\x01i\x0b\x01k2\x01@\x04\x04self(\x01ct\x09f\ ont-sizev\x0cstroke-widthv\03\x04\0\x13[method]font.bitmap\x014\x01k\x09\x01@\x01\ \x04self(\05\x04\0\x1e[method]font.underline-metrics\x016\x01i\x0d\x01@\0\07\x04\ -\0\x15[constructor]font-kit\x018\x01h\x0d\x01p\x01\x01@\x02\x04self9\x06buffer\"\ -\0:\x04\0%[method]font-kit.add-font-from-buffer\x01;\x01@\x02\x04self9\x04paths\x01\ -\0\x04\0\x20[method]font-kit.add-search-path\x01<\x01i\x0c\x01k=\x01@\x02\x04sel\ -f9\x03key\x01\0>\x04\0\x16[method]font-kit.query\x01?\x04\0\x1c[method]font-kit.\ -exact-match\x01?\x01@\x01\x04self9\0:\x04\0\x1a[method]font-kit.font-keys\x01@\x01\ -p\x07\x01@\x01\x04self9\0\xc1\0\x04\0\x1b[method]font-kit.fonts-info\x01B\x01@\x01\ -\x04self9\0y\x04\0\x14[method]font-kit.len\x01C\x01@\x02\x04self9\x03key\x01\x01\ -\0\x04\0\x17[method]font-kit.remove\x01D\x01k\x0e\x01@\x03\x04self9\x03key\x01\x04\ -texts\0\xc5\0\x04\0\x18[method]font-kit.measure\x01F\x01@\x01\x05widths\0{\x04\0\ -\x13str-width-to-number\x01G\x01@\x01\x05width{\0s\x04\0\x13number-width-to-str\x01\ -H\x04\x01!alibaba:fontkit/fontkit-interface\x05\x02\x04\x01\x17alibaba:fontkit/f\ -ontkit\x04\0\x0b\x0d\x01\0\x07fontkit\x03\0\0\0G\x09producers\x01\x0cprocessed-b\ -y\x02\x0dwit-component\x070.208.1\x10wit-bindgen-rust\x060.25.0"; +\0\x15[constructor]font-kit\x018\x01h\x0d\x01@\x02\x04self9\x06buffer\"\x01\0\x04\ +\0%[method]font-kit.add-font-from-buffer\x01:\x01@\x02\x04self9\x04paths\x01\0\x04\ +\0\x20[method]font-kit.add-search-path\x01;\x01i\x0c\x01k<\x01@\x02\x04self9\x03\ +key\x01\0=\x04\0\x16[method]font-kit.query\x01>\x04\0\x1c[method]font-kit.exact-\ +match\x01>\x01p\x07\x01@\x01\x04self9\0?\x04\0\x1b[method]font-kit.fonts-info\x01\ +@\x01@\x01\x04self9\0y\x04\0\x14[method]font-kit.len\x01A\x01@\x02\x04self9\x03k\ +ey\x01\x01\0\x04\0\x17[method]font-kit.remove\x01B\x01k\x0e\x01@\x03\x04self9\x03\ +key\x01\x04texts\0\xc3\0\x04\0\x18[method]font-kit.measure\x01D\x01@\x01\x05widt\ +hs\0{\x04\0\x13str-width-to-number\x01E\x01@\x01\x05width{\0s\x04\0\x13number-wi\ +dth-to-str\x01F\x04\x01!alibaba:fontkit/fontkit-interface\x05\x02\x04\x01\x17ali\ +baba:fontkit/fontkit\x04\0\x0b\x0d\x01\0\x07fontkit\x03\0\0\0G\x09producers\x01\x0c\ +processed-by\x02\x0dwit-component\x070.208.1\x10wit-bindgen-rust\x060.25.0"; #[inline(never)] #[doc(hidden)] diff --git a/src/font.rs b/src/font.rs index c934df3..ab2ae97 100644 --- a/src/font.rs +++ b/src/font.rs @@ -12,9 +12,9 @@ use std::io::Read; use std::path::PathBuf; use std::sync::Arc; pub use ttf_parser::LineMetrics; -use ttf_parser::{Face, Fixed, VariationAxis, Width as ParserWidth}; +use ttf_parser::{Face, Fixed, Tag, VariationAxis, Width as ParserWidth}; -use crate::Error; +use crate::{Error, Filter}; pub fn str_width_to_number(width: &str) -> u16 { match width { @@ -154,107 +154,20 @@ pub fn is_otf(buf: &[u8]) -> bool { && buf[4] == 0x00 } -#[derive(Clone)] -pub struct Font { - key: FontKey, - /// [Font variation](https://learn.microsoft.com/en-us/typography/opentype/spec/fvar) data - variant: Variant, - pub(super) names: Vec, - #[allow(unused)] - pub(super) style_names: Vec, - pub(super) face: Arc>>, - pub(super) path: Option, +pub(crate) struct VariationData { + pub key: FontKey, + pub names: Vec, + pub variation_names: Vec, + pub style_names: Vec, + pub index: u32, } -impl Font { - pub fn key(&self) -> FontKey { - self.key.clone() - } - - pub fn variantions(&self) -> Vec<(String, f32)> { - match &self.variant { - Variant::Instance { coords, axes, .. } => axes - .iter() - .map(|a| format!("{}", a.tag)) - .zip(coords.iter().map(|c| c.0)) - .collect(), - _ => vec![], - } - } - - pub(super) fn has_name(&self, name: &str) -> bool { - if self.key.family == name { - return true; - } - if self.names.iter().any(|n| n.name == name) { - return true; - } - if let Variant::Instance { names, .. } = &self.variant { - use inflections::Inflect; - return names.iter().any(|n| { - n.postscript.name == name - || n.postscript - .name - .replace(&n.sub_family.name, &n.sub_family.name.to_pascal_case()) - == name - }); - } - false - } - - #[cfg(feature = "parse")] - pub(super) fn from_buffer(buffer: &[u8]) -> Result, Error> { - let mut variants = vec![Variant::Index(0)]; - let result = if is_ttf(&buffer) { - buffer.to_vec() - } else if is_otf(&buffer) { - variants = (0..ttf_parser::fonts_in_collection(&buffer).unwrap_or(1)) - .map(|i| Variant::Index(i)) - .collect(); - buffer.to_vec() - } else { - buffer.to_vec() - }; - Ok(variants - .into_iter() - .map(|v| Font::from_buffer_with_variant(result.clone(), v)) - .collect::, _>>()? - .into_iter() - .flatten() - .collect()) - } - - #[cfg(feature = "parse")] - fn from_buffer_with_variant(mut buffer: Vec, variant: Variant) -> Result, Error> { - #[cfg(feature = "woff2-patched")] - if is_woff2(&buffer) { - buffer = woff2_patched::convert_woff2_to_ttf(&mut buffer.as_slice())?; - } - #[cfg(feature = "parse")] - if is_woff(&buffer) { - use std::io::Cursor; - - let reader = Cursor::new(buffer); - let mut otf_buf = Cursor::new(Vec::new()); - crate::conv::woff::convert_woff_to_otf(reader, &mut otf_buf)?; - buffer = otf_buf.into_inner(); - } - if buffer.is_empty() { - return Err(Error::UnsupportedMIME("unknown")); - } - +impl VariationData { + fn parse_buffer_with_index(buffer: &[u8], index: u32) -> Result, Error> { use ttf_parser::name_id; - let index = match variant { - Variant::Index(i) => i, - _ => 0, - }; - let mut face = StaticFaceTryBuilder { - buffer: buffer.clone(), - face_builder: |buf| Face::parse(buf, index), - } - .try_build()?; + + let face = Face::parse(buffer, index)?; let axes: Vec = face - .borrow_face() .tables() .fvar .map(|v| v.axes.into_iter()) @@ -262,106 +175,89 @@ impl Font { .flatten() .collect::>(); - if let Variant::Index(_) = variant { - // get fvar if any - let mut instances: HashMap>, Vec> = HashMap::new(); - if let (Some(_), Some(name_table)) = ( - face.borrow_face().tables().fvar, - face.borrow_face().tables().name, - ) { - // currently ttf-parser is missing `fvar`'s instance records, we parse them - // directly from `RawFace` - let data: &[u8] = face - .borrow_face() - .raw_face() - .table(ttf_parser::Tag::from_bytes(b"fvar")) - .unwrap(); - let mut raw = &*data; - let _version = raw.read_u32::()?; - let axis_offset = raw.read_u16::()?; + // get fvar if any + let mut instances: HashMap>, Vec> = HashMap::new(); + if let (Some(_), Some(name_table)) = (face.tables().fvar, face.tables().name) { + // currently ttf-parser is missing `fvar`'s instance records, we parse them + // directly from `RawFace` + let data: &[u8] = face + .raw_face() + .table(ttf_parser::Tag::from_bytes(b"fvar")) + .unwrap(); + let mut raw = &*data; + let _version = raw.read_u32::()?; + let axis_offset = raw.read_u16::()?; + let _ = raw.read_u16::()?; + let axis_count = raw.read_u16::()?; + let axis_size = raw.read_u16::()?; + let instance_count = raw.read_u16::()?; + let instance_size = raw.read_u16::()?; + + let data = &data[(axis_offset as usize + (axis_count as usize * axis_size as usize))..]; + for i in 0..instance_count { + let mut raw = &data[(i as usize * instance_size as usize)..]; + let sub_family_name_id = raw.read_u16::()?; let _ = raw.read_u16::()?; - let axis_count = raw.read_u16::()?; - let axis_size = raw.read_u16::()?; - let instance_count = raw.read_u16::()?; - let instance_size = raw.read_u16::()?; - - let data = - &data[(axis_offset as usize + (axis_count as usize * axis_size as usize))..]; - for i in 0..instance_count { - let mut raw = &data[(i as usize * instance_size as usize)..]; - let sub_family_name_id = raw.read_u16::()?; - let _ = raw.read_u16::()?; - let coords = (0..axis_count) - .map(|_| { - use ttf_parser::FromData; - let mut v = [0_u8; 4]; - raw.read_exact(&mut v) - .map(|_| OrderedFloat(Fixed::parse(&v).unwrap().0)) - }) - .collect::, _>>()?; - let postscript_name_id = if raw.is_empty() { - None - } else { - Some(raw.read_u16::()?) - }; - let sub_family = name_table - .names - .into_iter() - .find(|name| name.name_id == sub_family_name_id) - .and_then(|name| { - Some(Name { - id: name.name_id, - name: name.to_string().or_else(|| { - // try to force unicode encoding - Some(std::str::from_utf8(name.name).ok()?.to_string()) - })?, - language_id: name.language_id, - }) - }); - let postscript = name_table - .names - .into_iter() - .find(|name| Some(name.name_id) == postscript_name_id) - .and_then(|name| { - Some(Name { - id: name.name_id, - name: name.to_string().or_else(|| { - // try to force unicode encoding - Some(std::str::from_utf8(name.name).ok()?.to_string()) - })?, - language_id: name.language_id, - }) - }); - if let (Some(sub_family), Some(postscript)) = (sub_family, postscript) { - instances.entry(coords).or_default().push(FvarInstance { - sub_family, - postscript, + let coords = (0..axis_count) + .map(|_| { + use ttf_parser::FromData; + let mut v = [0_u8; 4]; + raw.read_exact(&mut v) + .map(|_| OrderedFloat(Fixed::parse(&v).unwrap().0)) + }) + .collect::, _>>()?; + let postscript_name_id = if raw.is_empty() { + None + } else { + Some(raw.read_u16::()?) + }; + let sub_family = name_table + .names + .into_iter() + .find(|name| name.name_id == sub_family_name_id) + .and_then(|name| { + Some(Name { + id: name.name_id, + name: name.to_string().or_else(|| { + // try to force unicode encoding + Some(std::str::from_utf8(name.name).ok()?.to_string()) + })?, + language_id: name.language_id, }) - } - } - } - if !instances.is_empty() { - return Ok(instances + }); + let postscript = name_table + .names .into_iter() - .map(|(coords, names)| { - Font::from_buffer_with_variant( - buffer.clone(), - Variant::Instance { - coords: coords.into_iter().map(|v| Fixed(v.0)).collect(), - names, - axes: axes.clone(), - }, - ) + .find(|name| Some(name.name_id) == postscript_name_id) + .and_then(|name| { + Some(Name { + id: name.name_id, + name: name.to_string().or_else(|| { + // try to force unicode encoding + Some(std::str::from_utf8(name.name).ok()?.to_string()) + })?, + language_id: name.language_id, + }) + }); + if let (Some(sub_family), Some(postscript)) = (sub_family, postscript) { + instances.entry(coords).or_default().push(FvarInstance { + sub_family, + postscript, }) - .collect::, _>>()? - .into_iter() - .flatten() - .collect()); + } } } + let instances = instances + .into_iter() + .map(|(coords, names)| { + return ( + coords.into_iter().map(|v| Fixed(v.0)).collect::>(), + names, + ); + }) + .collect::>(); let mut style_names = vec![]; let names = face - .borrow_face() .names() .into_iter() .filter_map(|name| { @@ -420,19 +316,16 @@ impl Font { name } }); - let mut key = FontKey { - weight: Some(face.borrow_face().weight().to_number()), - italic: Some(face.borrow_face().is_italic()), - stretch: Some(face.borrow_face().width().to_number()), - family: ascii_name.unwrap_or_else(|| names[0].name.clone()), + let mut results = vec![]; + let key = FontKey { + weight: Some(face.weight().to_number()), + italic: Some(face.is_italic()), + stretch: Some(face.width().to_number()), + family: ascii_name.clone().unwrap_or_else(|| names[0].name.clone()), variations: vec![], }; - if let Variant::Instance { - coords, - names, - axes, - } = &variant - { + for (coords, variation_names) in instances { + let mut key = key.clone(); let width_axis_index = axes .iter() .position(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wdth")); @@ -445,34 +338,133 @@ impl Font { if let Some(value) = weight_axis_index.and_then(|i| coords.get(i)) { key.weight = Some(value.0 as u16); } - key.family = names[0].postscript.name.clone(); - face.with_face_mut(|face| { - for (coord, axis) in coords.iter().zip(axes.iter()) { - face.set_variation(axis.tag, coord.0); - } - }); + key.family = variation_names[0].postscript.name.clone(); for (coord, axis) in coords.iter().zip(axes.iter()) { key.variations .push((String::from_utf8(axis.tag.to_bytes().to_vec())?, coord.0)); } + results.push(VariationData { + key, + names: names.clone(), + style_names: style_names.clone(), + variation_names, + index, + }); + } + if results.is_empty() { + // this is not a variable font, add normal font data + results.push(VariationData { + names, + key, + variation_names: vec![], + style_names, + index, + }) + } + Ok(results) + } + + fn is_variable(&self) -> bool { + !self.key.variations.is_empty() + } + + fn fulfils(&self, query: &Filter) -> bool { + match *query { + Filter::Family(name) => { + if self.key.family == name { + return true; + } + if self.names.iter().any(|n| n.name == name) { + return true; + } + if self.is_variable() { + use inflections::Inflect; + return self.variation_names.iter().any(|n| { + n.postscript.name == name + || n.postscript + .name + .replace(&n.sub_family.name, &n.sub_family.name.to_pascal_case()) + == name + }); + } + + false + } + Filter::Italic(i) => self.key.italic.unwrap_or_default() == i, + Filter::Stretch(s) => self.key.stretch.unwrap_or(5) == s, + Filter::Weight(w) => w == 0 || self.key.weight.unwrap_or(400) == w, + Filter::Variations(v) => v.iter().all(|(s, v)| { + self.key + .variations + .iter() + .any(|(ss, sv)| ss == s && v == sv) + }), + } + } +} + +pub(crate) struct Font { + path: Option, + buffer: ArcSwap>, + /// [Font variation](https://learn.microsoft.com/en-us/typography/opentype/spec/fvar) and font collection data + variants: Vec, +} + +impl Font { + pub fn fulfils(&self, query: &Filter) -> bool { + self.variants.iter().any(|v| v.fulfils(query)) + } + + pub fn first_key(&self) -> FontKey { + self.variants[0].key.clone() + } + + pub fn set_path(&mut self, path: PathBuf) { + self.path = Some(path); + } + + #[cfg(feature = "parse")] + pub(super) fn from_buffer(mut buffer: Vec) -> Result { + let mut variants = vec![0]; + if is_otf(&buffer) { + variants = (0..ttf_parser::fonts_in_collection(&buffer).unwrap_or(1)).collect(); + } + #[cfg(feature = "woff2-patched")] + if is_woff2(&buffer) { + buffer = woff2_patched::convert_woff2_to_ttf(&mut buffer.as_slice())?; } - let font = Font { - names, - key, - variant, - face: Arc::new(ArcSwap::new(Arc::new(Some(face)))), + #[cfg(feature = "parse")] + if is_woff(&buffer) { + use std::io::Cursor; + + let reader = Cursor::new(buffer); + let mut otf_buf = Cursor::new(Vec::new()); + crate::conv::woff::convert_woff_to_otf(reader, &mut otf_buf)?; + buffer = otf_buf.into_inner(); + } + if buffer.is_empty() { + return Err(Error::UnsupportedMIME("unknown")); + } + let variants = variants + .into_iter() + .map(|v| VariationData::parse_buffer_with_index(&buffer, v)) + .collect::, _>>()? + .into_iter() + .flatten() + .collect::>(); + Ok(Font { path: None, - style_names, - }; - Ok(vec![font]) + buffer: ArcSwap::new(Arc::new(buffer)), + variants, + }) } pub fn unload(&self) { - self.face.swap(Arc::new(None)); + self.buffer.swap(Arc::default()); } pub fn load(&self) -> Result<(), Error> { - if self.face.load().is_some() { + if !self.buffer.load().is_empty() { return Ok(()); } #[cfg(feature = "parse")] @@ -480,67 +472,89 @@ impl Font { let mut buffer = Vec::new(); let mut file = std::fs::File::open(path)?; file.read_to_end(&mut buffer).unwrap(); - let mut fonts = Font::from_buffer_with_variant(buffer, self.variant.clone())?; - fonts.truncate(1); - if let Some(font) = fonts.pop() { - self.face.store(font.face.load_full()); - } + self.buffer.swap(Arc::new(buffer)); } Ok(()) } + pub fn path(&self) -> Option<&PathBuf> { + self.path.as_ref() + } + + pub fn face(&self, key: &FontKey) -> Result { + let buffer = self.buffer.load().clone(); + let filters = Filter::from_key(key); + let mut variant = &self.variants[0]; + let mut queue = self.variants.iter().collect::>(); + for filter in filters { + queue.retain(|v| v.fulfils(&filter)); + if queue.len() == 1 { + variant = queue[0]; + break; + } + } + let mut face = StaticFaceTryBuilder { + key: variant.key.clone(), + path: self.path.clone().unwrap_or_default(), + buffer, + face_builder: |buf| Face::parse(buf, variant.index), + } + .try_build()?; + face.with_face_mut(|face| { + for (coord, axis) in &variant.key.variations { + face.set_variation(Tag::from_bytes_lossy(coord.as_bytes()), *axis); + } + }); + Ok(face) + } + + pub fn variants(&self) -> &[VariationData] { + &self.variants + } +} + +#[self_referencing] +pub struct StaticFace { + key: FontKey, + pub(crate) path: PathBuf, + pub(crate) buffer: Arc>, + #[borrows(buffer)] + #[covariant] + pub(crate) face: Face<'this>, +} + +impl StaticFace { pub fn has_glyph(&self, c: char) -> bool { - self.load().unwrap(); - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); f.glyph_index(c).is_some() } pub fn ascender(&self) -> i16 { - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); f.ascender() } pub fn descender(&self) -> i16 { - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); f.descender() } pub fn units_per_em(&self) -> u16 { - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); f.units_per_em() } - pub fn path(&self) -> Option<&PathBuf> { - self.path.as_ref() - } - pub fn strikeout_metrics(&self) -> Option { - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); f.strikeout_metrics() } pub fn underline_metrics(&self) -> Option { - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); f.underline_metrics() } -} -#[self_referencing] -pub struct StaticFace { - pub(crate) buffer: Vec, - #[borrows(buffer)] - #[covariant] - pub(crate) face: Face<'this>, + pub fn key(&self) -> FontKey { + self.borrow_key().clone() + } } diff --git a/src/lib.rs b/src/lib.rs index 8d304c8..905bf61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -use dashmap::mapref::one::Ref; use std::collections::HashSet; #[cfg(feature = "parse")] use std::path::Path; @@ -113,86 +112,50 @@ impl FontKit { /// If the given buffer is a font collection (ttc), multiple keys will be /// returned. #[cfg(feature = "parse")] - pub fn add_font_from_buffer(&self, buffer: Vec) -> Result, Error> { - Ok(Font::from_buffer(&buffer)? - .into_iter() - .map(|font| { - let key = font.key().clone(); - self.fonts.insert(key.clone(), font); - key - }) - .collect::>()) - } - - pub fn font_keys(&self) -> impl Iterator + '_ { - return self.fonts.iter().map(|i| { - log::debug!("{:?}", i.names); - i.key().clone() - }); + pub fn add_font_from_buffer(&self, buffer: Vec) -> Result<(), Error> { + let font = Font::from_buffer(buffer)?; + let key = font.first_key(); + self.fonts.insert(key, font); + Ok(()) } /// Recursively scan a local path for fonts, this method will not store the /// font buffer to reduce memory consumption #[cfg(feature = "parse")] pub fn search_fonts_from_path(&self, path: impl AsRef) -> Result<(), Error> { - if let Some(fonts) = load_font_from_path(&path) { - for font in fonts { - self.fonts.insert(font.key(), font); - } + if let Some(font) = load_font_from_path(&path) { + self.fonts.insert(font.first_key(), font); } - // } Ok(()) } - pub fn exact_match(&self, key: &font::FontKey) -> Option> { - return self.fonts.get(key); + pub fn exact_match(&self, key: &font::FontKey) -> Option { + return self.fonts.get(key)?.face(key).ok(); } - pub fn query(&self, key: &font::FontKey) -> Option> { + pub fn query(&self, key: &font::FontKey) -> Option { if let Some(result) = self.exact_match(key) { return Some(result as _); } - let mut filters = vec![Filter::Family(&key.family)]; - if let Some(italic) = key.italic { - filters.push(Filter::Italic(italic)); - } - if let Some(weight) = key.weight { - filters.push(Filter::Weight(weight)); - } - if let Some(stretch) = key.stretch { - filters.push(Filter::Stretch(stretch)); - } - - filters.push(Filter::Variations(&key.variations)); - - // Fallback weight logic - filters.push(Filter::Weight(0)); let mut search_results = self .fonts .iter() .map(|item| item.key().clone()) .collect::>(); + let filters = Filter::from_key(key); for filter in filters { let mut s = search_results.clone(); - let mut is_family = false; - match filter { - Filter::Family(f) => { - is_family = true; - s.retain(|key| { - let font = self.fonts.get(key).unwrap(); - font.has_name(f) - }) - } - Filter::Italic(i) => s.retain(|key| key.italic == Some(i)), - Filter::Weight(w) => s.retain(|key| w == 0 || key.weight == Some(w)), - Filter::Stretch(st) => s.retain(|key| key.stretch == Some(st)), - Filter::Variations(v) => s.retain(|key| { - v.iter() - .all(|(s, v)| key.variations.iter().any(|(ss, sv)| ss == s && v == sv)) - }), + let is_family = if let Filter::Family(_) = filter { + true + } else { + false }; + s.retain(|key| { + let font = self.fonts.get(key).unwrap(); + font.fulfils(&filter) + }); match s.len() { - 1 => return self.fonts.get(s.iter().next()?), + 1 => return self.fonts.get(s.iter().next()?)?.face(key).ok(), 0 if is_family => return None, 0 => {} _ => search_results = s, @@ -203,7 +166,7 @@ impl FontKit { } #[cfg(feature = "parse")] -fn load_font_from_path(path: impl AsRef) -> Option> { +fn load_font_from_path(path: impl AsRef) -> Option { use std::io::Read; // if path.as_ref().is_dir() { @@ -235,19 +198,17 @@ fn load_font_from_path(path: impl AsRef) -> Option> { let mut file = std::fs::File::open(&path).unwrap(); buffer.clear(); file.read_to_end(&mut buffer).unwrap(); - let mut fonts = match Font::from_buffer(&buffer) { + let mut font = match Font::from_buffer(buffer) { Ok(f) => f, Err(e) => { log::warn!("Failed loading font {:?}: {:?}", path, e); return None; } }; - for font in &mut fonts { - font.path = Some(path.to_path_buf()); - // println!("{:?}", font.names); - font.unload(); - } - Some(fonts) + font.set_path(path.to_path_buf()); + // println!("{:?}", font.names); + font.unload(); + Some(font) } _ => None, } @@ -260,3 +221,24 @@ enum Filter<'a> { Stretch(u16), Variations(&'a Vec<(String, f32)>), } + +impl<'a> Filter<'a> { + pub fn from_key(key: &'a FontKey) -> Vec> { + let mut filters = vec![Filter::Family(&key.family)]; + if let Some(italic) = key.italic { + filters.push(Filter::Italic(italic)); + } + if let Some(weight) = key.weight { + filters.push(Filter::Weight(weight)); + } + if let Some(stretch) = key.stretch { + filters.push(Filter::Stretch(stretch)); + } + + filters.push(Filter::Variations(&key.variations)); + + // Fallback weight logic + filters.push(Filter::Weight(0)); + filters + } +} diff --git a/src/metrics.rs b/src/metrics.rs index cf3b3ab..bcee95a 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -1,4 +1,4 @@ -use crate::{Error, Font}; +use crate::{Error, StaticFace}; pub use compose::*; use std::borrow::Cow; use std::sync::{Arc, RwLock}; @@ -10,14 +10,11 @@ use unicode_script::{Script, ScriptExtension}; mod arabic; mod compose; -impl Font { +impl StaticFace { /// Measure a string slice. If certain character is missing, the related /// [`CharMetrics`] 's `missing` field will be `true` pub fn measure(&self, text: &str) -> Result { - self.load()?; - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let font = f.borrow_face(); + let font = self.borrow_face(); let mut positions = vec![]; let mut prev = 0 as char; let mut value = Cow::Borrowed(text); @@ -104,9 +101,7 @@ impl Font { /// Measure the metrics of a single unicode charactor pub(crate) fn measure_char(&self, c: char) -> Option { - let f = self.face.load(); - let f = f.as_ref().as_ref()?; - let f = f.borrow_face(); + let f = self.borrow_face(); let height = f.height(); let units = f.units_per_em() as f32; let glyph_id = f.glyph_index(c)?; @@ -137,9 +132,7 @@ impl Font { /// Check if there's any kerning data between two charactors, units are /// handled fn kerning(&self, prev: char, c: char) -> Option { - let f = self.face.load(); - let f = f.as_ref().as_ref()?; - let f = f.borrow_face(); + let f = self.borrow_face(); let pid = f.glyph_index(prev)?; let cid = f.glyph_index(c)?; let mut kerning = 0; diff --git a/src/ras.rs b/src/ras.rs index ab4e3ee..5d16018 100644 --- a/src/ras.rs +++ b/src/ras.rs @@ -12,14 +12,11 @@ use ttf_parser::{OutlineBuilder, Rect}; use crate::metrics::CharMetrics; use crate::*; -impl Font { +impl StaticFace { /// Output the outline instructions of a glyph pub fn outline(&self, c: char) -> Option<(Glyph, Outline)> { - self.load().ok()?; let mut builder = PathBuilder::new(); - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); let CharMetrics { glyph_id, bbox, @@ -44,11 +41,7 @@ impl Font { if !self.has_glyph(c) { return None; } - self.load().ok()?; - - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - let f = f.borrow_face(); + let f = self.borrow_face(); let a = f.ascender(); let d = f.descender(); let units = f.units_per_em() as f32; diff --git a/src/wit.rs b/src/wit.rs index 9ef4f4b..f7b725b 100644 --- a/src/wit.rs +++ b/src/wit.rs @@ -1,27 +1,21 @@ use crate::bindings::exports::alibaba::fontkit::fontkit_interface as fi; use crate::font::FontKey; use crate::metrics::TextMetrics; -use crate::{Font, FontKit, GlyphBitmap}; +use crate::{FontKit, GlyphBitmap, StaticFace}; use crate::bindings::exports::alibaba::fontkit::fontkit_interface::GuestTextMetrics; -impl fi::GuestFont for Font { +impl fi::GuestFont for StaticFace { fn has_glyph(&self, c: char) -> bool { self.has_glyph(c) } fn buffer(&self) -> Vec { - self.load().unwrap(); - let f = self.face.load(); - let f = f.as_ref().as_ref().unwrap(); - f.borrow_buffer().clone() + self.borrow_buffer().as_ref().clone() } fn path(&self) -> String { - self.path() - .and_then(|p| p.to_str()) - .unwrap_or_default() - .to_string() + self.borrow_path().to_str().unwrap_or_default().to_string() } fn key(&self) -> fi::FontKey { @@ -126,30 +120,17 @@ impl fi::GuestFontKit for FontKit { FontKit::new() } - #[allow(unused)] - fn add_font_from_buffer(&self, buffer: Vec) -> Vec { - #[cfg(not(feature = "parse"))] - return vec![]; + fn add_font_from_buffer(&self, buffer: Vec) { #[cfg(feature = "parse")] - self.add_font_from_buffer(buffer) - .into_iter() - .flatten() - .map(fi::FontKey::from) - .collect() + let _ = self.add_font_from_buffer(buffer); } fn query(&self, key: fi::FontKey) -> Option { - self.query(&FontKey::from(key)) - .map(|f| fi::Font::new(f.clone())) + self.query(&FontKey::from(key)).map(fi::Font::new) } fn exact_match(&self, key: fi::FontKey) -> Option { - self.exact_match(&FontKey::from(key)) - .map(|f| fi::Font::new(f.clone())) - } - - fn font_keys(&self) -> Vec { - self.font_keys().map(fi::FontKey::from).collect() + self.exact_match(&FontKey::from(key)).map(fi::Font::new) } fn len(&self) -> u32 { @@ -168,27 +149,32 @@ impl fi::GuestFontKit for FontKit { fn fonts_info(&self) -> Vec { self.fonts .iter() - .map(|i| fi::FontInfo { - style_names: i - .style_names - .iter() - .map(|n| fi::Name { - id: n.id, - name: n.name.clone(), - language_id: n.language_id, - }) - .collect(), - key: fi::FontKey::from(i.key().clone()), - names: i - .names + .flat_map(|i| { + i.variants() .iter() - .map(|n| fi::Name { - id: n.id, - name: n.name.clone(), - language_id: n.language_id, + .map(|v| fi::FontInfo { + style_names: v + .style_names + .iter() + .map(|n| fi::Name { + id: n.id, + name: n.name.clone(), + language_id: n.language_id, + }) + .collect(), + key: fi::FontKey::from(v.key.clone()), + names: v + .names + .iter() + .map(|n| fi::Name { + id: n.id, + name: n.name.clone(), + language_id: n.language_id, + }) + .collect(), + path: i.path().and_then(|p| Some(p.to_str()?.to_string())), }) - .collect(), - path: i.path().and_then(|p| Some(p.to_str()?.to_string())), + .collect::>() }) .collect() } @@ -264,7 +250,7 @@ impl GuestTextMetrics for TextMetrics { struct Component; impl fi::Guest for Component { - type Font = Font; + type Font = StaticFace; type FontKit = FontKit; type GlyphBitmap = GlyphBitmap; type TextMetrics = TextMetrics; diff --git a/wit/world.wit b/wit/world.wit index aeeec50..0d1c4db 100644 --- a/wit/world.wit +++ b/wit/world.wit @@ -84,15 +84,13 @@ interface fontkit-interface { constructor(); /// Register a font (or several fonts in case of ttc), return the keys of added fonts. /// The file type is extracted from the buffer by checking magic numbers - add-font-from-buffer: func(buffer: list) -> list; + add-font-from-buffer: func(buffer: list) ; /// Search and add fonts from a path add-search-path: func(path: string); /// Query font using a key, this API returns valid result only if a single result is found query: func(key: font-key) -> option; /// Using exact-match method to directly obtain a font, skipping the querying logic exact-match: func(key: font-key) -> option; - /// Get all registered fonts' keys - font-keys: func() -> list; /// Get detailed info of all fonts registered fonts-info: func() -> list; /// Get number of registered fonts