| #![allow(non_upper_case_globals)] |
| |
| use super::hb::*; |
| |
| use std::os::raw::c_void; |
| use std::ptr::null_mut; |
| use std::str::FromStr; |
| |
| use font_types::Tag; |
| use harfruzz::{Face, FontRef, ShaperFont}; |
| |
| pub struct HBHarfRuzzFaceData<'a> { |
| face_blob: *mut hb_blob_t, |
| font_ref: FontRef<'a>, |
| shaper_font: Box<ShaperFont>, |
| } |
| |
| #[no_mangle] |
| pub unsafe extern "C" fn _hb_harfruzz_shaper_face_data_create_rs( |
| face: *mut hb_face_t, |
| ) -> *mut c_void { |
| let face_index = hb_face_get_index(face); |
| let face_blob = hb_face_reference_blob(face); |
| let blob_length = hb_blob_get_length(face_blob); |
| let blob_data = hb_blob_get_data(face_blob, null_mut()); |
| let face_data = std::slice::from_raw_parts(blob_data as *const u8, blob_length as usize); |
| |
| let font_ref = FontRef::from_index(face_data, face_index); |
| if font_ref.is_err() { |
| return null_mut() as *mut c_void; |
| } |
| let font_ref = font_ref.unwrap(); |
| let shaper_font = Box::new(ShaperFont::new(&font_ref)); |
| |
| let hr_face_data = Box::new(HBHarfRuzzFaceData { |
| face_blob, |
| font_ref, |
| shaper_font, |
| }); |
| |
| Box::into_raw(hr_face_data) as *mut c_void |
| } |
| |
| #[no_mangle] |
| pub unsafe extern "C" fn _hb_harfruzz_shaper_face_data_destroy_rs(data: *mut c_void) { |
| let data = data as *mut HBHarfRuzzFaceData; |
| let hr_face_data = Box::from_raw(data); |
| let blob = hr_face_data.face_blob; |
| hb_blob_destroy(blob); |
| } |
| |
| pub struct HBHarfRuzzFontData<'a> { |
| coords: Vec<font_types::F2Dot14>, |
| face: Option<Face<'a>>, |
| } |
| |
| fn font_coords_to_f2dot14(font: *mut hb_font_t) -> Vec<font_types::F2Dot14> { |
| let mut num_coords: u32 = 0; |
| let coords = unsafe { hb_font_get_var_coords_normalized(font, &mut num_coords) }; |
| let coords = if coords.is_null() { |
| &[] |
| } else { |
| unsafe { std::slice::from_raw_parts(coords, num_coords as usize) } |
| }; |
| coords |
| .iter() |
| .map(|v| font_types::F2Dot14::from_bits(*v as i16)) |
| .collect::<Vec<_>>() |
| } |
| |
| #[no_mangle] |
| pub unsafe extern "C" fn _hb_harfruzz_shaper_font_data_create_rs( |
| font: *mut hb_font_t, |
| face_data: *const c_void, |
| ) -> *mut c_void { |
| let face_data = face_data as *const HBHarfRuzzFaceData; |
| |
| let coords = font_coords_to_f2dot14(font); |
| |
| let hr_font_data = Box::new(HBHarfRuzzFontData { coords, face: None }); |
| let hr_font_data_ptr = Box::into_raw(hr_font_data); |
| (*hr_font_data_ptr).face = Some( |
| (*face_data) |
| .shaper_font |
| .shaper(&(*face_data).font_ref, &(*hr_font_data_ptr).coords), |
| ); |
| |
| hr_font_data_ptr as *mut c_void |
| } |
| |
| #[no_mangle] |
| pub unsafe extern "C" fn _hb_harfruzz_shaper_font_data_destroy_rs(data: *mut c_void) { |
| let data = data as *mut HBHarfRuzzFontData; |
| let _hr_font_data = Box::from_raw(data); |
| } |
| |
| #[no_mangle] |
| pub unsafe extern "C" fn _hb_harfruzz_shape_plan_create_rs( |
| font_data: *const c_void, |
| script: hb_script_t, |
| language: hb_language_t, |
| direction: hb_direction_t, |
| ) -> *mut c_void { |
| let font_data = font_data as *const HBHarfRuzzFontData; |
| |
| let script = harfruzz::Script::from_iso15924_tag(Tag::from_u32(script)); |
| let language_str = hb_language_to_string(language); |
| let language_str = std::ffi::CStr::from_ptr(language_str); |
| let language_str = language_str.to_str().unwrap_or(""); |
| let language = harfruzz::Language::from_str(language_str).unwrap(); |
| let direction = match direction { |
| hb_direction_t_HB_DIRECTION_LTR => harfruzz::Direction::LeftToRight, |
| hb_direction_t_HB_DIRECTION_RTL => harfruzz::Direction::RightToLeft, |
| hb_direction_t_HB_DIRECTION_TTB => harfruzz::Direction::TopToBottom, |
| hb_direction_t_HB_DIRECTION_BTT => harfruzz::Direction::BottomToTop, |
| _ => harfruzz::Direction::Invalid, |
| }; |
| |
| let hr_shape_plan = harfruzz::ShapePlan::new( |
| (*font_data).face.as_ref().unwrap(), |
| direction, |
| script, |
| Some(&language), |
| &[], |
| ); |
| let hr_shape_plan = Box::new(hr_shape_plan); |
| Box::into_raw(hr_shape_plan) as *mut c_void |
| } |
| |
| #[no_mangle] |
| pub unsafe extern "C" fn _hb_harfruzz_shape_plan_destroy_rs(data: *mut c_void) { |
| let data = data as *mut harfruzz::ShapePlan; |
| let _hr_shape_plan = Box::from_raw(data); |
| } |
| |
| #[no_mangle] |
| pub unsafe extern "C" fn _hb_harfruzz_shape_rs( |
| font_data: *const c_void, |
| shape_plan: *const c_void, |
| font: *mut hb_font_t, |
| buffer: *mut hb_buffer_t, |
| features: *const hb_feature_t, |
| num_features: u32, |
| ) -> hb_bool_t { |
| let font_data = font_data as *const HBHarfRuzzFontData; |
| |
| let mut hr_buffer = harfruzz::UnicodeBuffer::new(); |
| |
| // Set buffer properties |
| let cluster_level = hb_buffer_get_cluster_level(buffer); |
| let cluster_level = match cluster_level { |
| hb_buffer_cluster_level_t_HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES => { |
| harfruzz::BufferClusterLevel::MonotoneGraphemes |
| } |
| hb_buffer_cluster_level_t_HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS => { |
| harfruzz::BufferClusterLevel::MonotoneCharacters |
| } |
| hb_buffer_cluster_level_t_HB_BUFFER_CLUSTER_LEVEL_CHARACTERS => { |
| harfruzz::BufferClusterLevel::Characters |
| } |
| /* TODO: Enable when added to HarfRuzz |
| hb_buffer_cluster_level_t_HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES => { |
| harfruzz::BufferClusterLevel::Graphemes |
| } |
| */ |
| _ => harfruzz::BufferClusterLevel::default(), |
| }; |
| hr_buffer.set_cluster_level(cluster_level); |
| let flags = hb_buffer_get_flags(buffer); |
| hr_buffer.set_flags(harfruzz::BufferFlags::from_bits_truncate(flags)); |
| let not_found_variation_selector_glyph = |
| hb_buffer_get_not_found_variation_selector_glyph(buffer); |
| hr_buffer.set_not_found_variation_selector_glyph(not_found_variation_selector_glyph); |
| |
| // Segment properties: |
| let script = hb_buffer_get_script(buffer); |
| let language = hb_buffer_get_language(buffer); |
| let direction = hb_buffer_get_direction(buffer); |
| // Convert to HarfRuzz types |
| let script = harfruzz::Script::from_iso15924_tag(Tag::from_u32(script)) |
| .unwrap_or(harfruzz::script::UNKNOWN); |
| let language_str = hb_language_to_string(language); |
| let language_str = std::ffi::CStr::from_ptr(language_str); |
| let language_str = language_str.to_str().unwrap_or(""); |
| let language = harfruzz::Language::from_str(language_str).unwrap(); |
| let direction = match direction { |
| hb_direction_t_HB_DIRECTION_LTR => harfruzz::Direction::LeftToRight, |
| hb_direction_t_HB_DIRECTION_RTL => harfruzz::Direction::RightToLeft, |
| hb_direction_t_HB_DIRECTION_TTB => harfruzz::Direction::TopToBottom, |
| hb_direction_t_HB_DIRECTION_BTT => harfruzz::Direction::BottomToTop, |
| _ => harfruzz::Direction::Invalid, |
| }; |
| // Set properties on the buffer |
| hr_buffer.set_script(script); |
| hr_buffer.set_language(language); |
| hr_buffer.set_direction(direction); |
| |
| // Populate buffer |
| let count = hb_buffer_get_length(buffer); |
| let infos = hb_buffer_get_glyph_infos(buffer, null_mut()); |
| |
| for i in 0..count { |
| let info = infos.add(i as usize); |
| let unicode = (*info).codepoint; |
| let cluster = (*info).cluster; |
| hr_buffer.add(char::from_u32_unchecked(unicode), cluster); |
| } |
| |
| let face = &(*font_data).face.as_ref().unwrap(); |
| |
| let glyphs = if shape_plan.is_null() { |
| let features = if features.is_null() { |
| Vec::new() |
| } else { |
| let features = std::slice::from_raw_parts(features, num_features as usize); |
| features |
| .iter() |
| .map(|f| { |
| let tag = f.tag; |
| let value = f.value; |
| let start = f.start; |
| let end = f.end; |
| harfruzz::Feature { |
| tag: Tag::from_u32(tag), |
| value, |
| start, |
| end, |
| } |
| }) |
| .collect::<Vec<_>>() |
| }; |
| harfruzz::shape(face, &features, hr_buffer) |
| } else { |
| let shape_plan = shape_plan as *const harfruzz::ShapePlan; |
| harfruzz::shape_with_plan(face, shape_plan.as_ref().unwrap(), hr_buffer) |
| }; |
| |
| let count = glyphs.len(); |
| hb_buffer_set_content_type( |
| buffer, |
| hb_buffer_content_type_t_HB_BUFFER_CONTENT_TYPE_GLYPHS, |
| ); |
| hb_buffer_set_length(buffer, count as u32); |
| if hb_buffer_get_length(buffer) != count as u32 { |
| return false as hb_bool_t; |
| } |
| let infos = hb_buffer_get_glyph_infos(buffer, null_mut()); |
| let positions = hb_buffer_get_glyph_positions(buffer, null_mut()); |
| |
| let mut x_scale: i32 = 0; |
| let mut y_scale: i32 = 0; |
| hb_font_get_scale(font, &mut x_scale, &mut y_scale); |
| let upem = face.units_per_em(); |
| let x_scale = x_scale as f32 / upem as f32; |
| let y_scale = y_scale as f32 / upem as f32; |
| |
| for (i, (hr_info, hr_pos)) in glyphs |
| .glyph_infos() |
| .iter() |
| .zip(glyphs.glyph_positions()) |
| .enumerate() |
| { |
| let info = infos.add(i); |
| let pos = positions.add(i); |
| (*info).codepoint = hr_info.glyph_id; |
| (*info).cluster = hr_info.cluster; |
| (*pos).x_advance = (hr_pos.x_advance as f32 * x_scale).round() as hb_position_t; |
| (*pos).y_advance = (hr_pos.y_advance as f32 * y_scale).round() as hb_position_t; |
| (*pos).x_offset = (hr_pos.x_offset as f32 * x_scale).round() as hb_position_t; |
| (*pos).y_offset = (hr_pos.y_offset as f32 * y_scale).round() as hb_position_t; |
| } |
| |
| true as hb_bool_t |
| } |