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