| 1 | use std::{ |
| 2 | ffi::{CStr, CString}, |
| 3 | fmt, |
| 4 | marker::PhantomData, |
| 5 | os::raw, |
| 6 | }; |
| 7 | |
| 8 | use skia_bindings::{ |
| 9 | self as sb, RustRunHandler, SkShaper, SkShaper_BiDiRunIterator, SkShaper_FontRunIterator, |
| 10 | SkShaper_LanguageRunIterator, SkShaper_RunHandler, SkShaper_RunIterator, |
| 11 | SkShaper_ScriptRunIterator, SkTextBlobBuilderRunHandler, |
| 12 | }; |
| 13 | |
| 14 | use crate::{prelude::*, scalar, Font, FontMgr, FourByteTag, Point, TextBlob}; |
| 15 | |
| 16 | // The following three are re-exported in `modules.rs` via `mod shapers {}`. |
| 17 | pub(crate) mod core_text; |
| 18 | pub(crate) mod harfbuzz; |
| 19 | pub(crate) mod unicode; |
| 20 | |
| 21 | pub use run_handler::RunHandler; |
| 22 | |
| 23 | pub type Shaper = RefHandle<SkShaper>; |
| 24 | unsafe_send_sync!(Shaper); |
| 25 | |
| 26 | impl NativeDrop for SkShaper { |
| 27 | fn drop(&mut self) { |
| 28 | unsafe { sb::C_SkShaper_delete(self) } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | impl Default for Shaper { |
| 33 | fn default() -> Self { |
| 34 | Self::new(font_mgr:None) |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | impl fmt::Debug for Shaper { |
| 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 40 | f.debug_struct(name:"Shaper" ).finish() |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | impl Shaper { |
| 45 | #[deprecated (since = "0.74.0" , note = "use shapers::primitive::primitive_text()" )] |
| 46 | pub fn new_primitive() -> Self { |
| 47 | crate::shapers::primitive::primitive_text() |
| 48 | } |
| 49 | |
| 50 | pub fn new_shaper_driven_wrapper( |
| 51 | fallback_font_mgr: impl Into<Option<FontMgr>>, |
| 52 | ) -> Option<Self> { |
| 53 | crate::shapers::hb::shaper_driven_wrapper(fallback_font_mgr) |
| 54 | } |
| 55 | |
| 56 | pub fn new_shape_then_wrap(fallback_font_mgr: impl Into<Option<FontMgr>>) -> Option<Self> { |
| 57 | crate::shapers::hb::shape_then_wrap(fallback_font_mgr) |
| 58 | } |
| 59 | |
| 60 | #[deprecated ( |
| 61 | since = "0.74.0" , |
| 62 | note = "use shapers::hb::shape_dont_wrap_or_reorder()" |
| 63 | )] |
| 64 | pub fn new_shape_dont_wrap_or_reorder( |
| 65 | fallback_font_mgr: impl Into<Option<FontMgr>>, |
| 66 | ) -> Option<Self> { |
| 67 | crate::shapers::hb::shape_dont_wrap_or_reorder(fallback_font_mgr) |
| 68 | } |
| 69 | |
| 70 | pub fn purge_harf_buzz_cache() { |
| 71 | unsafe { sb::SkShaper_PurgeHarfBuzzCache() } |
| 72 | } |
| 73 | |
| 74 | pub fn new_core_text() -> Option<Self> { |
| 75 | #[cfg (feature = "embed-icudtl" )] |
| 76 | crate::icu::init(); |
| 77 | |
| 78 | Self::from_ptr(unsafe { sb::C_SkShaper_MakeCoreText() }) |
| 79 | } |
| 80 | |
| 81 | pub fn new(font_mgr: impl Into<Option<FontMgr>>) -> Self { |
| 82 | #[cfg (feature = "embed-icudtl" )] |
| 83 | crate::icu::init(); |
| 84 | |
| 85 | Self::from_ptr(unsafe { sb::C_SkShaper_Make(font_mgr.into().into_ptr_or_null()) }).unwrap() |
| 86 | } |
| 87 | |
| 88 | pub fn purge_caches() { |
| 89 | unsafe { sb::SkShaper_PurgeCaches() } |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | pub use skia_bindings::SkShaper_Feature as Feature; |
| 94 | |
| 95 | pub trait RunIterator { |
| 96 | fn consume(&mut self); |
| 97 | fn end_of_current_run(&self) -> usize; |
| 98 | fn at_end(&self) -> bool; |
| 99 | } |
| 100 | |
| 101 | impl<T> RunIterator for RefHandle<T> |
| 102 | where |
| 103 | T: NativeDrop, |
| 104 | T: NativeBase<SkShaper_RunIterator>, |
| 105 | { |
| 106 | fn consume(&mut self) { |
| 107 | unsafe { sb::C_SkShaper_RunIterator_consume(self.native_mut().base_mut()) } |
| 108 | } |
| 109 | |
| 110 | fn end_of_current_run(&self) -> usize { |
| 111 | unsafe { sb::C_SkShaper_RunIterator_endOfCurrentRun(self.native().base()) } |
| 112 | } |
| 113 | |
| 114 | fn at_end(&self) -> bool { |
| 115 | unsafe { sb::C_SkShaper_RunIterator_atEnd(self.native().base()) } |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | pub type FontRunIterator = RefHandle<SkShaper_FontRunIterator>; |
| 120 | |
| 121 | impl NativeBase<SkShaper_RunIterator> for SkShaper_FontRunIterator {} |
| 122 | |
| 123 | impl NativeDrop for SkShaper_FontRunIterator { |
| 124 | fn drop(&mut self) { |
| 125 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | impl fmt::Debug for FontRunIterator { |
| 130 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 131 | f&mut DebugStruct<'_, '_>.debug_struct("FontRunIterator" ) |
| 132 | .field(name:"current_font" , &self.current_font()) |
| 133 | .finish() |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | impl FontRunIterator { |
| 138 | pub fn current_font(&self) -> &Font { |
| 139 | Font::from_native_ref(unsafe { |
| 140 | &*sb::C_SkShaper_FontRunIterator_currentFont(self.native()) |
| 141 | }) |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | impl Shaper { |
| 146 | pub fn new_font_mgr_run_iterator<'a>( |
| 147 | utf8: &'a str, |
| 148 | font: &Font, |
| 149 | fallback: impl Into<Option<FontMgr>>, |
| 150 | ) -> Borrows<'a, FontRunIterator> { |
| 151 | let bytes = utf8.as_bytes(); |
| 152 | FontRunIterator::from_ptr(unsafe { |
| 153 | sb::C_SkShaper_MakeFontMgrRunIterator( |
| 154 | bytes.as_ptr() as _, |
| 155 | bytes.len(), |
| 156 | font.native(), |
| 157 | fallback.into().into_ptr_or_null(), |
| 158 | ) |
| 159 | }) |
| 160 | .unwrap() |
| 161 | .borrows(utf8) |
| 162 | } |
| 163 | |
| 164 | // TODO: m79: wrap MakeFontMgrRunIterator with requestName (borrowed), requestStyle and |
| 165 | // a LanguageRunIterator. |
| 166 | |
| 167 | pub fn new_trivial_font_run_iterator(font: &Font, utf8_bytes: usize) -> FontRunIterator { |
| 168 | FontRunIterator::from_ptr(unsafe { |
| 169 | sb::C_SkShaper_TrivialFontRunIterator_new(font.native(), utf8_bytes) |
| 170 | }) |
| 171 | .unwrap() |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | pub type BiDiRunIterator = RefHandle<SkShaper_BiDiRunIterator>; |
| 176 | |
| 177 | impl NativeBase<SkShaper_RunIterator> for SkShaper_BiDiRunIterator {} |
| 178 | |
| 179 | impl NativeDrop for SkShaper_BiDiRunIterator { |
| 180 | fn drop(&mut self) { |
| 181 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | impl fmt::Debug for BiDiRunIterator { |
| 186 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 187 | f&mut DebugStruct<'_, '_>.debug_struct("BiDiRunIterator" ) |
| 188 | .field(name:"current_level" , &self.current_level()) |
| 189 | .finish() |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | impl BiDiRunIterator { |
| 194 | pub fn current_level(&self) -> u8 { |
| 195 | unsafe { sb::C_SkShaper_BiDiRunIterator_currentLevel(self.native()) } |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | impl Shaper { |
| 200 | pub fn new_bidi_run_iterator(utf8: &str, bidi_level: u8) -> Option<Borrows<BiDiRunIterator>> { |
| 201 | let bytes = utf8.as_bytes(); |
| 202 | BiDiRunIterator::from_ptr(unsafe { |
| 203 | sb::C_SkShaper_MakeBidiRunIterator(bytes.as_ptr() as _, bytes.len(), bidi_level) |
| 204 | }) |
| 205 | .map(|i| i.borrows(utf8)) |
| 206 | } |
| 207 | |
| 208 | pub fn new_icu_bidi_run_iterator(utf8: &str, level: u8) -> Option<Borrows<BiDiRunIterator>> { |
| 209 | let bytes = utf8.as_bytes(); |
| 210 | BiDiRunIterator::from_ptr(unsafe { |
| 211 | sb::C_SkShaper_MakeIcuBidiRunIterator(bytes.as_ptr() as _, bytes.len(), level) |
| 212 | }) |
| 213 | .map(|i| i.borrows(utf8)) |
| 214 | } |
| 215 | |
| 216 | #[deprecated ( |
| 217 | since = "0.74.0" , |
| 218 | note = "use shapers::primitive::trivial_bidi_run_iterator()" |
| 219 | )] |
| 220 | pub fn new_trivial_bidi_run_iterator(bidi_level: u8, utf8_bytes: usize) -> BiDiRunIterator { |
| 221 | shapers::primitive::trivial_bidi_run_iterator(bidi_level, utf8_bytes) |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | pub type ScriptRunIterator = RefHandle<SkShaper_ScriptRunIterator>; |
| 226 | |
| 227 | impl NativeBase<SkShaper_RunIterator> for SkShaper_ScriptRunIterator {} |
| 228 | |
| 229 | impl NativeDrop for SkShaper_ScriptRunIterator { |
| 230 | fn drop(&mut self) { |
| 231 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | impl fmt::Debug for ScriptRunIterator { |
| 236 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 237 | f&mut DebugStruct<'_, '_>.debug_struct("ScriptRunIterator" ) |
| 238 | .field(name:"current_script" , &self.current_script()) |
| 239 | .finish() |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | impl ScriptRunIterator { |
| 244 | pub fn current_script(&self) -> FourByteTag { |
| 245 | FourByteTag::from_native_c(nt:unsafe { |
| 246 | sb::C_SkShaper_ScriptRunIterator_currentScript(self.native()) |
| 247 | }) |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | impl Shaper { |
| 252 | pub fn new_script_run_iterator(utf8: &str, script: FourByteTag) -> Borrows<ScriptRunIterator> { |
| 253 | let bytes = utf8.as_bytes(); |
| 254 | ScriptRunIterator::from_ptr(unsafe { |
| 255 | sb::C_SkShaper_MakeScriptRunIterator( |
| 256 | bytes.as_ptr() as _, |
| 257 | bytes.len(), |
| 258 | script.into_native(), |
| 259 | ) |
| 260 | }) |
| 261 | .unwrap() |
| 262 | .borrows(utf8) |
| 263 | } |
| 264 | |
| 265 | // TODO: wrap MakeSkUnicodeHbScriptRunIterator (m88: uses type SkUnicode defined in src/). |
| 266 | |
| 267 | pub fn new_hb_icu_script_run_iterator(utf8: &str) -> Borrows<ScriptRunIterator> { |
| 268 | let bytes = utf8.as_bytes(); |
| 269 | ScriptRunIterator::from_ptr(unsafe { |
| 270 | sb::C_SkShaper_MakeHbIcuScriptRunIterator(bytes.as_ptr() as _, bytes.len()) |
| 271 | }) |
| 272 | .unwrap() |
| 273 | .borrows(utf8) |
| 274 | } |
| 275 | |
| 276 | #[deprecated ( |
| 277 | since = "0.74.0" , |
| 278 | note = "use shapers::primitive::trivial_script_run_iterator" |
| 279 | )] |
| 280 | pub fn new_trivial_script_run_iterator(bidi_level: u8, utf8_bytes: usize) -> ScriptRunIterator { |
| 281 | shapers::primitive::trivial_script_run_iterator(bidi_level, utf8_bytes) |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | pub type LanguageRunIterator = RefHandle<SkShaper_LanguageRunIterator>; |
| 286 | |
| 287 | impl NativeBase<SkShaper_RunIterator> for SkShaper_LanguageRunIterator {} |
| 288 | |
| 289 | impl NativeDrop for SkShaper_LanguageRunIterator { |
| 290 | fn drop(&mut self) { |
| 291 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | impl fmt::Debug for LanguageRunIterator { |
| 296 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 297 | f&mut DebugStruct<'_, '_>.debug_struct("LanguageRunIterator" ) |
| 298 | .field(name:"current_language" , &self.current_language()) |
| 299 | .finish() |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | impl LanguageRunIterator { |
| 304 | pub fn current_language(&self) -> &CStr { |
| 305 | unsafe { |
| 306 | CStr::from_ptr(sb::C_SkShaper_LanguageRunIterator_currentLanguage( |
| 307 | self.native(), |
| 308 | )) |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | impl Shaper { |
| 314 | pub fn new_std_language_run_iterator(utf8: &str) -> Option<LanguageRunIterator> { |
| 315 | // a LanguageRunIterator never accesses the UTF8 string, so it's safe to |
| 316 | // not borrow the string. |
| 317 | let bytes: &[u8] = utf8.as_bytes(); |
| 318 | LanguageRunIterator::from_ptr(unsafe { |
| 319 | sb::C_SkShaper_MakeStdLanguageRunIterator(utf8:bytes.as_ptr() as _, utf8Bytes:bytes.len()) |
| 320 | }) |
| 321 | } |
| 322 | |
| 323 | pub fn new_trivial_language_run_iterator( |
| 324 | language: impl AsRef<str>, |
| 325 | utf8_bytes: usize, |
| 326 | ) -> LanguageRunIterator { |
| 327 | let language: CString = CString::new(language.as_ref()).unwrap(); |
| 328 | LanguageRunIteratorOption>::from_ptr(unsafe { |
| 329 | sb::C_SkShaper_TrivialLanguageRunIterator_new(language.as_ptr(), utf8Bytes:utf8_bytes) |
| 330 | }) |
| 331 | .unwrap() |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | pub mod run_handler { |
| 336 | use crate::prelude::*; |
| 337 | use crate::{Font, GlyphId, Point, Vector}; |
| 338 | use skia_bindings::{ |
| 339 | SkShaper_RunHandler_Buffer, SkShaper_RunHandler_Range, SkShaper_RunHandler_RunInfo, |
| 340 | }; |
| 341 | use std::ops::Range; |
| 342 | use std::slice; |
| 343 | |
| 344 | pub trait RunHandler { |
| 345 | fn begin_line(&mut self); |
| 346 | fn run_info(&mut self, info: &RunInfo); |
| 347 | fn commit_run_info(&mut self); |
| 348 | fn run_buffer(&mut self, info: &RunInfo) -> Buffer; |
| 349 | fn commit_run_buffer(&mut self, info: &RunInfo); |
| 350 | fn commit_line(&mut self); |
| 351 | } |
| 352 | |
| 353 | #[derive (Debug)] |
| 354 | pub struct RunInfo<'a> { |
| 355 | pub font: &'a Font, |
| 356 | pub bidi_level: u8, |
| 357 | pub advance: Vector, |
| 358 | pub glyph_count: usize, |
| 359 | pub utf8_range: Range<usize>, |
| 360 | } |
| 361 | |
| 362 | impl RunInfo<'_> { |
| 363 | pub(crate) fn from_native(ri: &SkShaper_RunHandler_RunInfo) -> Self { |
| 364 | // TODO: should we avoid that copy and wrap RunInfo with functions? |
| 365 | let utf8_range = ri.utf8Range; |
| 366 | RunInfo { |
| 367 | font: Font::from_native_ref(unsafe { &*ri.fFont }), |
| 368 | bidi_level: ri.fBidiLevel, |
| 369 | advance: Vector::from_native_c(ri.fAdvance), |
| 370 | glyph_count: ri.glyphCount, |
| 371 | utf8_range: utf8_range.fBegin..utf8_range.fBegin + utf8_range.fSize, |
| 372 | } |
| 373 | } |
| 374 | |
| 375 | #[allow (unused)] |
| 376 | pub(crate) fn to_native(&self) -> SkShaper_RunHandler_RunInfo { |
| 377 | SkShaper_RunHandler_RunInfo { |
| 378 | fFont: self.font.native(), |
| 379 | fBidiLevel: self.bidi_level, |
| 380 | fAdvance: self.advance.into_native(), |
| 381 | glyphCount: self.glyph_count, |
| 382 | utf8Range: SkShaper_RunHandler_Range { |
| 383 | fBegin: self.utf8_range.start, |
| 384 | fSize: self.utf8_range.end - self.utf8_range.start, |
| 385 | }, |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | #[derive (Debug)] |
| 391 | pub struct Buffer<'a> { |
| 392 | pub glyphs: &'a mut [GlyphId], |
| 393 | pub positions: &'a mut [Point], |
| 394 | pub offsets: Option<&'a mut [Point]>, |
| 395 | pub clusters: Option<&'a mut [u32]>, |
| 396 | pub point: Point, |
| 397 | } |
| 398 | |
| 399 | impl<'a> Buffer<'a> { |
| 400 | pub fn new( |
| 401 | glyphs: &'a mut [GlyphId], |
| 402 | positions: &'a mut [Point], |
| 403 | point: impl Into<Option<Point>>, |
| 404 | ) -> Self { |
| 405 | Buffer { |
| 406 | glyphs, |
| 407 | positions, |
| 408 | offsets: None, |
| 409 | clusters: None, |
| 410 | point: point.into().unwrap_or_default(), |
| 411 | } |
| 412 | } |
| 413 | |
| 414 | #[allow (unused)] |
| 415 | pub(crate) unsafe fn from_native( |
| 416 | buffer: &SkShaper_RunHandler_Buffer, |
| 417 | glyph_count: usize, |
| 418 | ) -> Buffer { |
| 419 | let offsets = buffer.offsets.into_option().map(|mut offsets| { |
| 420 | slice::from_raw_parts_mut(Point::from_native_ref_mut(offsets.as_mut()), glyph_count) |
| 421 | }); |
| 422 | |
| 423 | let clusters = buffer |
| 424 | .clusters |
| 425 | .into_option() |
| 426 | .map(|clusters| slice::from_raw_parts_mut(clusters.as_ptr(), glyph_count)); |
| 427 | |
| 428 | Buffer { |
| 429 | glyphs: safer::from_raw_parts_mut(buffer.glyphs, glyph_count), |
| 430 | positions: safer::from_raw_parts_mut( |
| 431 | Point::from_native_ptr_mut(buffer.positions), |
| 432 | glyph_count, |
| 433 | ), |
| 434 | offsets, |
| 435 | clusters, |
| 436 | point: Point::from_native_c(buffer.point), |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | pub(crate) fn native_buffer_mut( |
| 441 | &mut self, |
| 442 | glyph_count: usize, |
| 443 | ) -> SkShaper_RunHandler_Buffer { |
| 444 | assert_eq!(self.glyphs.len(), glyph_count); |
| 445 | assert_eq!(self.positions.len(), glyph_count); |
| 446 | if let Some(offsets) = &self.offsets { |
| 447 | assert_eq!(offsets.len(), glyph_count) |
| 448 | } |
| 449 | if let Some(clusters) = &self.clusters { |
| 450 | assert_eq!(clusters.len(), glyph_count) |
| 451 | } |
| 452 | SkShaper_RunHandler_Buffer { |
| 453 | glyphs: self.glyphs.as_mut_ptr(), |
| 454 | positions: self.positions.native_mut().as_mut_ptr(), |
| 455 | offsets: self.offsets.native_mut().as_ptr_or_null_mut(), |
| 456 | clusters: self.clusters.as_ptr_or_null_mut(), |
| 457 | point: self.point.into_native(), |
| 458 | } |
| 459 | } |
| 460 | } |
| 461 | } |
| 462 | |
| 463 | pub trait AsRunHandler<'a> { |
| 464 | type RunHandler: AsNativeRunHandler + 'a; |
| 465 | fn as_run_handler<'b>(&'b mut self) -> Self::RunHandler |
| 466 | where |
| 467 | 'b: 'a; |
| 468 | } |
| 469 | |
| 470 | /// A trait for accessing the native run handler instance used in the shape_native* functions. |
| 471 | pub trait AsNativeRunHandler { |
| 472 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler; |
| 473 | } |
| 474 | |
| 475 | impl<'a, T: RunHandler> AsRunHandler<'a> for T { |
| 476 | type RunHandler = RustRunHandler; |
| 477 | |
| 478 | fn as_run_handler<'b>(&'b mut self) -> Self::RunHandler |
| 479 | where |
| 480 | 'b: 'a, |
| 481 | { |
| 482 | let param: RustRunHandler_Param = unsafe { rust_run_handler::new_param(self) }; |
| 483 | rust_run_handler::from_param(¶m) |
| 484 | } |
| 485 | } |
| 486 | |
| 487 | impl Shaper { |
| 488 | pub fn shape<'a, 'b: 'a>( |
| 489 | &self, |
| 490 | utf8: &str, |
| 491 | font: &Font, |
| 492 | left_to_right: bool, |
| 493 | width: scalar, |
| 494 | run_handler: &'b mut impl AsRunHandler<'a>, |
| 495 | ) { |
| 496 | let bytes = utf8.as_bytes(); |
| 497 | let mut run_handler = run_handler.as_run_handler(); |
| 498 | |
| 499 | unsafe { |
| 500 | sb::C_SkShaper_shape( |
| 501 | self.native(), |
| 502 | bytes.as_ptr() as _, |
| 503 | bytes.len(), |
| 504 | font.native(), |
| 505 | left_to_right, |
| 506 | width, |
| 507 | run_handler.as_native_run_handler(), |
| 508 | ) |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | #[allow (clippy::too_many_arguments)] |
| 513 | pub fn shape_with_iterators<'a, 'b: 'a>( |
| 514 | &self, |
| 515 | utf8: &str, |
| 516 | font_run_iterator: &mut FontRunIterator, |
| 517 | bidi_run_iterator: &mut BiDiRunIterator, |
| 518 | script_run_iterator: &mut ScriptRunIterator, |
| 519 | language_run_iterator: &mut LanguageRunIterator, |
| 520 | width: scalar, |
| 521 | run_handler: &'b mut impl AsRunHandler<'a>, |
| 522 | ) { |
| 523 | let mut run_handler = run_handler.as_run_handler(); |
| 524 | |
| 525 | let bytes = utf8.as_bytes(); |
| 526 | unsafe { |
| 527 | sb::C_SkShaper_shape2( |
| 528 | self.native(), |
| 529 | bytes.as_ptr() as _, |
| 530 | bytes.len(), |
| 531 | font_run_iterator.native_mut(), |
| 532 | bidi_run_iterator.native_mut(), |
| 533 | script_run_iterator.native_mut(), |
| 534 | language_run_iterator.native_mut(), |
| 535 | width, |
| 536 | run_handler.as_native_run_handler(), |
| 537 | ) |
| 538 | } |
| 539 | } |
| 540 | |
| 541 | #[allow (clippy::too_many_arguments)] |
| 542 | pub fn shape_with_iterators_and_features<'a, 'b: 'a>( |
| 543 | &self, |
| 544 | utf8: &str, |
| 545 | font_run_iterator: &mut FontRunIterator, |
| 546 | bidi_run_iterator: &mut BiDiRunIterator, |
| 547 | script_run_iterator: &mut ScriptRunIterator, |
| 548 | language_run_iterator: &mut LanguageRunIterator, |
| 549 | features: &[Feature], |
| 550 | width: scalar, |
| 551 | run_handler: &'b mut impl AsRunHandler<'a>, |
| 552 | ) { |
| 553 | let mut run_handler = run_handler.as_run_handler(); |
| 554 | |
| 555 | let bytes = utf8.as_bytes(); |
| 556 | unsafe { |
| 557 | sb::C_SkShaper_shape3( |
| 558 | self.native(), |
| 559 | bytes.as_ptr() as _, |
| 560 | bytes.len(), |
| 561 | font_run_iterator.native_mut(), |
| 562 | bidi_run_iterator.native_mut(), |
| 563 | script_run_iterator.native_mut(), |
| 564 | language_run_iterator.native_mut(), |
| 565 | features.as_ptr(), |
| 566 | features.len(), |
| 567 | width, |
| 568 | run_handler.as_native_run_handler(), |
| 569 | ) |
| 570 | } |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | mod rust_run_handler { |
| 575 | use std::mem; |
| 576 | |
| 577 | use skia_bindings as sb; |
| 578 | use skia_bindings::{ |
| 579 | RustRunHandler, RustRunHandler_Param, SkShaper_RunHandler, SkShaper_RunHandler_Buffer, |
| 580 | SkShaper_RunHandler_RunInfo, TraitObject, |
| 581 | }; |
| 582 | |
| 583 | use crate::{ |
| 584 | prelude::*, |
| 585 | shaper::{run_handler::RunInfo, AsNativeRunHandler, RunHandler}, |
| 586 | }; |
| 587 | |
| 588 | impl NativeBase<SkShaper_RunHandler> for RustRunHandler {} |
| 589 | |
| 590 | impl AsNativeRunHandler for RustRunHandler { |
| 591 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler { |
| 592 | self.base_mut() |
| 593 | } |
| 594 | } |
| 595 | |
| 596 | pub unsafe fn new_param(run_handler: &mut dyn RunHandler) -> RustRunHandler_Param { |
| 597 | RustRunHandler_Param { |
| 598 | trait_: mem::transmute::<&mut dyn RunHandler, TraitObject>(run_handler), |
| 599 | beginLine: Some(begin_line), |
| 600 | runInfo: Some(run_info), |
| 601 | commitRunInfo: Some(commit_run_info), |
| 602 | runBuffer: Some(run_buffer), |
| 603 | commitRunBuffer: Some(commit_run_buffer), |
| 604 | commitLine: Some(commit_line), |
| 605 | } |
| 606 | } |
| 607 | |
| 608 | pub fn from_param(param: &RustRunHandler_Param) -> RustRunHandler { |
| 609 | construct(|rh| unsafe { sb::C_RustRunHandler_construct(rh, param) }) |
| 610 | } |
| 611 | |
| 612 | extern "C" fn begin_line(to: TraitObject) { |
| 613 | to_run_handler(to).begin_line() |
| 614 | } |
| 615 | |
| 616 | extern "C" fn run_info(to: TraitObject, ri: *const SkShaper_RunHandler_RunInfo) { |
| 617 | to_run_handler(to).run_info(&RunInfo::from_native(unsafe { &*ri })); |
| 618 | } |
| 619 | |
| 620 | extern "C" fn commit_run_info(to: TraitObject) { |
| 621 | to_run_handler(to).commit_run_info() |
| 622 | } |
| 623 | |
| 624 | extern "C" fn run_buffer( |
| 625 | to: TraitObject, |
| 626 | ri: *const SkShaper_RunHandler_RunInfo, |
| 627 | ) -> SkShaper_RunHandler_Buffer { |
| 628 | let ri = unsafe { &*ri }; |
| 629 | to_run_handler(to) |
| 630 | .run_buffer(&RunInfo::from_native(ri)) |
| 631 | .native_buffer_mut(ri.glyphCount) |
| 632 | } |
| 633 | |
| 634 | extern "C" fn commit_run_buffer(to: TraitObject, ri: *const SkShaper_RunHandler_RunInfo) { |
| 635 | to_run_handler(to).commit_run_buffer(&RunInfo::from_native(unsafe { &*ri })) |
| 636 | } |
| 637 | |
| 638 | extern "C" fn commit_line(to: TraitObject) { |
| 639 | to_run_handler(to).commit_line() |
| 640 | } |
| 641 | |
| 642 | fn to_run_handler<'a>(to: TraitObject) -> &'a mut dyn RunHandler { |
| 643 | unsafe { mem::transmute(to) } |
| 644 | } |
| 645 | } |
| 646 | |
| 647 | #[repr (transparent)] |
| 648 | #[derive (Debug)] |
| 649 | pub struct TextBlobBuilderRunHandler<'text>(SkTextBlobBuilderRunHandler, PhantomData<&'text str>); |
| 650 | |
| 651 | impl NativeAccess for TextBlobBuilderRunHandler<'_> { |
| 652 | type Native = SkTextBlobBuilderRunHandler; |
| 653 | |
| 654 | fn native(&self) -> &SkTextBlobBuilderRunHandler { |
| 655 | &self.0 |
| 656 | } |
| 657 | fn native_mut(&mut self) -> &mut SkTextBlobBuilderRunHandler { |
| 658 | &mut self.0 |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | impl NativeBase<SkShaper_RunHandler> for SkTextBlobBuilderRunHandler {} |
| 663 | |
| 664 | impl TextBlobBuilderRunHandler<'_> { |
| 665 | pub fn new(text: &str, offset: impl Into<Point>) -> TextBlobBuilderRunHandler { |
| 666 | let ptr = text.as_ptr(); |
| 667 | // we can safely pass a ptr to the utf8 text string to the RunHandler, because it does not |
| 668 | // expect it to be 0 terminated, but this introduces another problem because |
| 669 | // we can never be sure that the RunHandler callbacks refer to that range. For |
| 670 | // now we ensure that by not exposing the RunHandler of a TextBlobBuilder. |
| 671 | let run_handler = construct(|rh| unsafe { |
| 672 | sb::C_SkTextBlobBuilderRunHandler_construct( |
| 673 | rh, |
| 674 | ptr as *const raw::c_char, |
| 675 | offset.into().native(), |
| 676 | ) |
| 677 | }); |
| 678 | TextBlobBuilderRunHandler(run_handler, PhantomData) |
| 679 | } |
| 680 | |
| 681 | pub fn make_blob(&mut self) -> Option<TextBlob> { |
| 682 | TextBlob::from_ptr(unsafe { sb::C_SkTextBlobBuilderRunHandler_makeBlob(self.native_mut()) }) |
| 683 | } |
| 684 | |
| 685 | pub fn end_point(&mut self) -> Point { |
| 686 | Point::from_native_c(unsafe { |
| 687 | sb::C_SkTextBlobBuilderRunHandler_endPoint(self.native_mut()) |
| 688 | }) |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | impl<'a> AsRunHandler<'a> for TextBlobBuilderRunHandler<'_> { |
| 693 | type RunHandler = &'a mut SkShaper_RunHandler; |
| 694 | |
| 695 | fn as_run_handler<'b>(&'b mut self) -> Self::RunHandler |
| 696 | where |
| 697 | 'b: 'a, |
| 698 | { |
| 699 | (*self).as_native_run_handler() |
| 700 | } |
| 701 | } |
| 702 | |
| 703 | impl AsNativeRunHandler for &mut SkShaper_RunHandler { |
| 704 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler { |
| 705 | self |
| 706 | } |
| 707 | } |
| 708 | |
| 709 | impl AsNativeRunHandler for TextBlobBuilderRunHandler<'_> { |
| 710 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler { |
| 711 | self.0.base_mut() |
| 712 | } |
| 713 | } |
| 714 | |
| 715 | impl Shaper { |
| 716 | pub fn shape_text_blob( |
| 717 | &self, |
| 718 | text: &str, |
| 719 | font: &Font, |
| 720 | left_to_right: bool, |
| 721 | width: scalar, |
| 722 | offset: impl Into<Point>, |
| 723 | ) -> Option<(TextBlob, Point)> { |
| 724 | let bytes = text.as_bytes(); |
| 725 | let mut builder = TextBlobBuilderRunHandler::new(text, offset); |
| 726 | unsafe { |
| 727 | sb::C_SkShaper_shape( |
| 728 | self.native(), |
| 729 | bytes.as_ptr() as _, |
| 730 | bytes.len(), |
| 731 | font.native(), |
| 732 | left_to_right, |
| 733 | width, |
| 734 | builder.native_mut().base_mut(), |
| 735 | ) |
| 736 | }; |
| 737 | builder.make_blob().map(|tb| (tb, builder.end_point())) |
| 738 | } |
| 739 | } |
| 740 | |
| 741 | pub(crate) mod shapers { |
| 742 | use super::{BiDiRunIterator, ScriptRunIterator, Shaper}; |
| 743 | |
| 744 | #[deprecated (since = "0.74.0" , note = "use shapers::primitive::primitive_text()" )] |
| 745 | pub fn primitive() -> Shaper { |
| 746 | primitive::primitive_text() |
| 747 | } |
| 748 | |
| 749 | #[deprecated ( |
| 750 | since = "0.74.0" , |
| 751 | note = "use shapers::primitive::trivial_bidi_run_iterator()" |
| 752 | )] |
| 753 | pub fn trivial_bidi_run_iterator(bidi_level: u8, utf8_bytes: usize) -> BiDiRunIterator { |
| 754 | primitive::trivial_bidi_run_iterator(bidi_level, utf8_bytes) |
| 755 | } |
| 756 | |
| 757 | #[deprecated ( |
| 758 | since = "0.74.0" , |
| 759 | note = "use shapers::primitive::trivial_script_run_iterator()" |
| 760 | )] |
| 761 | pub fn trivial_script_run_iterator(bidi_level: u8, utf8_bytes: usize) -> ScriptRunIterator { |
| 762 | primitive::trivial_script_run_iterator(bidi_level, utf8_bytes) |
| 763 | } |
| 764 | |
| 765 | pub mod primitive { |
| 766 | use skia_bindings as sb; |
| 767 | |
| 768 | use crate::shaper::{BiDiRunIterator, ScriptRunIterator, Shaper}; |
| 769 | |
| 770 | pub fn primitive_text() -> Shaper { |
| 771 | Shaper::from_ptr(unsafe { sb::C_SkShapers_Primitive_PrimitiveText() }).unwrap() |
| 772 | } |
| 773 | |
| 774 | pub fn trivial_bidi_run_iterator(bidi_level: u8, utf8_bytes: usize) -> BiDiRunIterator { |
| 775 | BiDiRunIterator::from_ptr(unsafe { |
| 776 | sb::C_SkShapers_Primitive_TrivialBidiRunIterator_new(bidi_level, utf8_bytes) |
| 777 | }) |
| 778 | .unwrap() |
| 779 | } |
| 780 | |
| 781 | pub fn trivial_script_run_iterator(bidi_level: u8, utf8_bytes: usize) -> ScriptRunIterator { |
| 782 | ScriptRunIterator::from_ptr(unsafe { |
| 783 | sb::C_SkShapers_Primitive_TrivialScriptRunIterator_new(bidi_level, utf8_bytes) |
| 784 | }) |
| 785 | .unwrap() |
| 786 | } |
| 787 | } |
| 788 | } |
| 789 | |
| 790 | pub mod icu { |
| 791 | /// On Windows, and if the default feature "embed-icudtl" is _not_ set, this function writes the |
| 792 | /// file `icudtl.dat` into the current executable's directory making sure that it's available |
| 793 | /// when text shaping is used in Skia. |
| 794 | /// |
| 795 | /// If your executable directory can not be written to, make sure that `icudtl.dat` is |
| 796 | /// available. |
| 797 | /// |
| 798 | /// Note that it is currently not possible to load `icudtl.dat` from another location. |
| 799 | /// |
| 800 | /// If the default feature "embed-icudtl" is set, the `icudtl.dat` file is directly used from |
| 801 | /// memory, so no `icudtl.dat` file is needed. |
| 802 | pub fn init() { |
| 803 | skia_bindings::icu::init(); |
| 804 | } |
| 805 | |
| 806 | #[test ] |
| 807 | #[serial_test::serial] |
| 808 | fn test_text_blob_builder_run_handler() { |
| 809 | use crate::{Font, FontMgr, FontStyle}; |
| 810 | init(); |
| 811 | let str = "العربية" ; |
| 812 | let mut text_blob_builder_run_handler = |
| 813 | crate::shaper::TextBlobBuilderRunHandler::new(str, crate::Point::default()); |
| 814 | |
| 815 | let font_mgr = FontMgr::new(); |
| 816 | let default_typeface = font_mgr |
| 817 | .legacy_make_typeface(None, FontStyle::default()) |
| 818 | .unwrap(); |
| 819 | let default_font = Font::new(default_typeface, 10.0); |
| 820 | let shaper = crate::Shaper::new(font_mgr); |
| 821 | shaper.shape( |
| 822 | str, |
| 823 | &default_font, |
| 824 | false, |
| 825 | 10000.0, |
| 826 | &mut text_blob_builder_run_handler, |
| 827 | ); |
| 828 | |
| 829 | let blob = text_blob_builder_run_handler.make_blob().unwrap(); |
| 830 | let bounds = blob.bounds(); |
| 831 | assert!(bounds.width() > 0.0 && bounds.height() > 0.0); |
| 832 | } |
| 833 | |
| 834 | #[test ] |
| 835 | #[serial_test::serial] |
| 836 | fn icu_init_is_idempotent() { |
| 837 | init(); |
| 838 | init(); |
| 839 | } |
| 840 | } |
| 841 | |