skia_safe/modules/
shaper.rs

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::{prelude::*, scalar, Font, FontMgr, FourByteTag, Point, TextBlob};
15
16// The following three are re-exported in `modules.rs` via `mod shapers {}`.
17pub(crate) mod core_text;
18pub(crate) mod harfbuzz;
19pub(crate) mod unicode;
20
21pub use run_handler::RunHandler;
22
23pub type Shaper = RefHandle<SkShaper>;
24unsafe_send_sync!(Shaper);
25
26impl NativeDrop for SkShaper {
27    fn drop(&mut self) {
28        unsafe { sb::C_SkShaper_delete(self) }
29    }
30}
31
32impl Default for Shaper {
33    fn default() -> Self {
34        Self::new(None)
35    }
36}
37
38impl fmt::Debug for Shaper {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        f.debug_struct("Shaper").finish()
41    }
42}
43
44impl 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
93pub use skia_bindings::SkShaper_Feature as Feature;
94
95pub trait RunIterator {
96    fn consume(&mut self);
97    fn end_of_current_run(&self) -> usize;
98    fn at_end(&self) -> bool;
99}
100
101impl<T> RunIterator for RefHandle<T>
102where
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
119pub type FontRunIterator = RefHandle<SkShaper_FontRunIterator>;
120
121impl NativeBase<SkShaper_RunIterator> for SkShaper_FontRunIterator {}
122
123impl NativeDrop for SkShaper_FontRunIterator {
124    fn drop(&mut self) {
125        unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) }
126    }
127}
128
129impl fmt::Debug for FontRunIterator {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        f.debug_struct("FontRunIterator")
132            .field("current_font", &self.current_font())
133            .finish()
134    }
135}
136
137impl 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
145impl 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
175pub type BiDiRunIterator = RefHandle<SkShaper_BiDiRunIterator>;
176
177impl NativeBase<SkShaper_RunIterator> for SkShaper_BiDiRunIterator {}
178
179impl NativeDrop for SkShaper_BiDiRunIterator {
180    fn drop(&mut self) {
181        unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) }
182    }
183}
184
185impl fmt::Debug for BiDiRunIterator {
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        f.debug_struct("BiDiRunIterator")
188            .field("current_level", &self.current_level())
189            .finish()
190    }
191}
192
193impl BiDiRunIterator {
194    pub fn current_level(&self) -> u8 {
195        unsafe { sb::C_SkShaper_BiDiRunIterator_currentLevel(self.native()) }
196    }
197}
198
199impl 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
225pub type ScriptRunIterator = RefHandle<SkShaper_ScriptRunIterator>;
226
227impl NativeBase<SkShaper_RunIterator> for SkShaper_ScriptRunIterator {}
228
229impl NativeDrop for SkShaper_ScriptRunIterator {
230    fn drop(&mut self) {
231        unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) }
232    }
233}
234
235impl fmt::Debug for ScriptRunIterator {
236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237        f.debug_struct("ScriptRunIterator")
238            .field("current_script", &self.current_script())
239            .finish()
240    }
241}
242
243impl ScriptRunIterator {
244    pub fn current_script(&self) -> FourByteTag {
245        FourByteTag::from_native_c(unsafe {
246            sb::C_SkShaper_ScriptRunIterator_currentScript(self.native())
247        })
248    }
249}
250
251impl 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
285pub type LanguageRunIterator = RefHandle<SkShaper_LanguageRunIterator>;
286
287impl NativeBase<SkShaper_RunIterator> for SkShaper_LanguageRunIterator {}
288
289impl NativeDrop for SkShaper_LanguageRunIterator {
290    fn drop(&mut self) {
291        unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) }
292    }
293}
294
295impl fmt::Debug for LanguageRunIterator {
296    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297        f.debug_struct("LanguageRunIterator")
298            .field("current_language", &self.current_language())
299            .finish()
300    }
301}
302
303impl 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
313impl 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 = utf8.as_bytes();
318        LanguageRunIterator::from_ptr(unsafe {
319            sb::C_SkShaper_MakeStdLanguageRunIterator(bytes.as_ptr() as _, 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::new(language.as_ref()).unwrap();
328        LanguageRunIterator::from_ptr(unsafe {
329            sb::C_SkShaper_TrivialLanguageRunIterator_new(language.as_ptr(), utf8_bytes)
330        })
331        .unwrap()
332    }
333}
334
335pub mod run_handler {
336    use std::{ffi::CStr, ops::Range, slice};
337
338    use skia_bindings::{SkShaper_RunHandler_Buffer, SkShaper_RunHandler_RunInfo};
339
340    use crate::{prelude::*, Font, FourByteTag, GlyphId, Point, Vector};
341
342    pub trait RunHandler {
343        fn begin_line(&mut self);
344        fn run_info(&mut self, info: &RunInfo);
345        fn commit_run_info(&mut self);
346        fn run_buffer(&mut self, info: &RunInfo) -> Buffer;
347        fn commit_run_buffer(&mut self, info: &RunInfo);
348        fn commit_line(&mut self);
349    }
350
351    #[derive(Debug)]
352    pub struct RunInfo<'a> {
353        pub font: &'a Font,
354        pub bidi_level: u8,
355        pub script: FourByteTag,
356        pub language: Option<&'a str>,
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            let utf8_range = ri.utf8Range;
365            RunInfo {
366                font: Font::from_native_ref(unsafe { &*ri.fFont }),
367                bidi_level: ri.fBidiLevel,
368                script: ri.fScript.into(),
369                language: unsafe {
370                    if ri.fLanguage.is_null() {
371                        None
372                    } else {
373                        CStr::from_ptr(ri.fLanguage).to_str().ok()
374                    }
375                },
376                advance: Vector::from_native_c(ri.fAdvance),
377                glyph_count: ri.glyphCount,
378                utf8_range: utf8_range.fBegin..utf8_range.fBegin + utf8_range.fSize,
379            }
380        }
381    }
382
383    #[derive(Debug)]
384    pub struct Buffer<'a> {
385        pub glyphs: &'a mut [GlyphId],
386        pub positions: &'a mut [Point],
387        pub offsets: Option<&'a mut [Point]>,
388        pub clusters: Option<&'a mut [u32]>,
389        pub point: Point,
390    }
391
392    impl<'a> Buffer<'a> {
393        pub fn new(
394            glyphs: &'a mut [GlyphId],
395            positions: &'a mut [Point],
396            point: impl Into<Option<Point>>,
397        ) -> Self {
398            Buffer {
399                glyphs,
400                positions,
401                offsets: None,
402                clusters: None,
403                point: point.into().unwrap_or_default(),
404            }
405        }
406
407        #[allow(unused)]
408        pub(crate) unsafe fn from_native(
409            buffer: &SkShaper_RunHandler_Buffer,
410            glyph_count: usize,
411        ) -> Buffer {
412            let offsets = buffer.offsets.into_non_null().map(|mut offsets| {
413                slice::from_raw_parts_mut(Point::from_native_ref_mut(offsets.as_mut()), glyph_count)
414            });
415
416            let clusters = buffer
417                .clusters
418                .into_non_null()
419                .map(|clusters| slice::from_raw_parts_mut(clusters.as_ptr(), glyph_count));
420
421            Buffer {
422                glyphs: safer::from_raw_parts_mut(buffer.glyphs, glyph_count),
423                positions: safer::from_raw_parts_mut(
424                    Point::from_native_ptr_mut(buffer.positions),
425                    glyph_count,
426                ),
427                offsets,
428                clusters,
429                point: Point::from_native_c(buffer.point),
430            }
431        }
432
433        pub(crate) fn native_buffer_mut(
434            &mut self,
435            glyph_count: usize,
436        ) -> SkShaper_RunHandler_Buffer {
437            assert_eq!(self.glyphs.len(), glyph_count);
438            assert_eq!(self.positions.len(), glyph_count);
439            if let Some(offsets) = &self.offsets {
440                assert_eq!(offsets.len(), glyph_count)
441            }
442            if let Some(clusters) = &self.clusters {
443                assert_eq!(clusters.len(), glyph_count)
444            }
445            SkShaper_RunHandler_Buffer {
446                glyphs: self.glyphs.as_mut_ptr(),
447                positions: self.positions.native_mut().as_mut_ptr(),
448                offsets: self.offsets.native_mut().as_ptr_or_null_mut(),
449                clusters: self.clusters.as_ptr_or_null_mut(),
450                point: self.point.into_native(),
451            }
452        }
453    }
454}
455
456pub trait AsRunHandler<'a> {
457    type RunHandler: AsNativeRunHandler + 'a;
458    fn as_run_handler<'b>(&'b mut self) -> Self::RunHandler
459    where
460        'b: 'a;
461}
462
463/// A trait for accessing the native run handler instance used in the shape_native* functions.
464pub trait AsNativeRunHandler {
465    fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler;
466}
467
468impl<'a, T: RunHandler> AsRunHandler<'a> for T {
469    type RunHandler = RustRunHandler;
470
471    fn as_run_handler<'b>(&'b mut self) -> Self::RunHandler
472    where
473        'b: 'a,
474    {
475        let param = unsafe { rust_run_handler::new_param(self) };
476        rust_run_handler::from_param(&param)
477    }
478}
479
480impl Shaper {
481    pub fn shape<'a, 'b: 'a>(
482        &self,
483        utf8: &str,
484        font: &Font,
485        left_to_right: bool,
486        width: scalar,
487        run_handler: &'b mut impl AsRunHandler<'a>,
488    ) {
489        let bytes = utf8.as_bytes();
490        let mut run_handler = run_handler.as_run_handler();
491
492        unsafe {
493            sb::C_SkShaper_shape(
494                self.native(),
495                bytes.as_ptr() as _,
496                bytes.len(),
497                font.native(),
498                left_to_right,
499                width,
500                run_handler.as_native_run_handler(),
501            )
502        }
503    }
504
505    #[allow(clippy::too_many_arguments)]
506    pub fn shape_with_iterators<'a, 'b: 'a>(
507        &self,
508        utf8: &str,
509        font_run_iterator: &mut FontRunIterator,
510        bidi_run_iterator: &mut BiDiRunIterator,
511        script_run_iterator: &mut ScriptRunIterator,
512        language_run_iterator: &mut LanguageRunIterator,
513        width: scalar,
514        run_handler: &'b mut impl AsRunHandler<'a>,
515    ) {
516        let mut run_handler = run_handler.as_run_handler();
517
518        let bytes = utf8.as_bytes();
519        unsafe {
520            sb::C_SkShaper_shape2(
521                self.native(),
522                bytes.as_ptr() as _,
523                bytes.len(),
524                font_run_iterator.native_mut(),
525                bidi_run_iterator.native_mut(),
526                script_run_iterator.native_mut(),
527                language_run_iterator.native_mut(),
528                width,
529                run_handler.as_native_run_handler(),
530            )
531        }
532    }
533
534    #[allow(clippy::too_many_arguments)]
535    pub fn shape_with_iterators_and_features<'a, 'b: 'a>(
536        &self,
537        utf8: &str,
538        font_run_iterator: &mut FontRunIterator,
539        bidi_run_iterator: &mut BiDiRunIterator,
540        script_run_iterator: &mut ScriptRunIterator,
541        language_run_iterator: &mut LanguageRunIterator,
542        features: &[Feature],
543        width: scalar,
544        run_handler: &'b mut impl AsRunHandler<'a>,
545    ) {
546        let mut run_handler = run_handler.as_run_handler();
547
548        let bytes = utf8.as_bytes();
549        unsafe {
550            sb::C_SkShaper_shape3(
551                self.native(),
552                bytes.as_ptr() as _,
553                bytes.len(),
554                font_run_iterator.native_mut(),
555                bidi_run_iterator.native_mut(),
556                script_run_iterator.native_mut(),
557                language_run_iterator.native_mut(),
558                features.as_ptr(),
559                features.len(),
560                width,
561                run_handler.as_native_run_handler(),
562            )
563        }
564    }
565}
566
567mod rust_run_handler {
568    use std::mem;
569
570    use skia_bindings as sb;
571    use skia_bindings::{
572        RustRunHandler, RustRunHandler_Param, SkShaper_RunHandler, SkShaper_RunHandler_Buffer,
573        SkShaper_RunHandler_RunInfo, TraitObject,
574    };
575
576    use crate::{
577        prelude::*,
578        shaper::{run_handler::RunInfo, AsNativeRunHandler, RunHandler},
579    };
580
581    impl NativeBase<SkShaper_RunHandler> for RustRunHandler {}
582
583    impl AsNativeRunHandler for RustRunHandler {
584        fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler {
585            self.base_mut()
586        }
587    }
588
589    pub unsafe fn new_param(run_handler: &mut dyn RunHandler) -> RustRunHandler_Param {
590        RustRunHandler_Param {
591            trait_: mem::transmute::<&mut dyn RunHandler, TraitObject>(run_handler),
592            beginLine: Some(begin_line),
593            runInfo: Some(run_info),
594            commitRunInfo: Some(commit_run_info),
595            runBuffer: Some(run_buffer),
596            commitRunBuffer: Some(commit_run_buffer),
597            commitLine: Some(commit_line),
598        }
599    }
600
601    pub fn from_param(param: &RustRunHandler_Param) -> RustRunHandler {
602        construct(|rh| unsafe { sb::C_RustRunHandler_construct(rh, param) })
603    }
604
605    extern "C" fn begin_line(to: TraitObject) {
606        to_run_handler(to).begin_line()
607    }
608
609    extern "C" fn run_info(to: TraitObject, ri: *const SkShaper_RunHandler_RunInfo) {
610        to_run_handler(to).run_info(&RunInfo::from_native(unsafe { &*ri }));
611    }
612
613    extern "C" fn commit_run_info(to: TraitObject) {
614        to_run_handler(to).commit_run_info()
615    }
616
617    extern "C" fn run_buffer(
618        to: TraitObject,
619        ri: *const SkShaper_RunHandler_RunInfo,
620    ) -> SkShaper_RunHandler_Buffer {
621        let ri = unsafe { &*ri };
622        to_run_handler(to)
623            .run_buffer(&RunInfo::from_native(ri))
624            .native_buffer_mut(ri.glyphCount)
625    }
626
627    extern "C" fn commit_run_buffer(to: TraitObject, ri: *const SkShaper_RunHandler_RunInfo) {
628        to_run_handler(to).commit_run_buffer(&RunInfo::from_native(unsafe { &*ri }))
629    }
630
631    extern "C" fn commit_line(to: TraitObject) {
632        to_run_handler(to).commit_line()
633    }
634
635    fn to_run_handler<'a>(to: TraitObject) -> &'a mut dyn RunHandler {
636        unsafe { mem::transmute(to) }
637    }
638}
639
640#[repr(transparent)]
641#[derive(Debug)]
642pub struct TextBlobBuilderRunHandler<'text>(SkTextBlobBuilderRunHandler, PhantomData<&'text str>);
643
644impl NativeAccess for TextBlobBuilderRunHandler<'_> {
645    type Native = SkTextBlobBuilderRunHandler;
646
647    fn native(&self) -> &SkTextBlobBuilderRunHandler {
648        &self.0
649    }
650    fn native_mut(&mut self) -> &mut SkTextBlobBuilderRunHandler {
651        &mut self.0
652    }
653}
654
655impl NativeBase<SkShaper_RunHandler> for SkTextBlobBuilderRunHandler {}
656
657impl TextBlobBuilderRunHandler<'_> {
658    pub fn new(text: &str, offset: impl Into<Point>) -> TextBlobBuilderRunHandler {
659        let ptr = text.as_ptr();
660        // we can safely pass a ptr to the utf8 text string to the RunHandler, because it does not
661        // expect it to be 0 terminated, but this introduces another problem because
662        // we can never be sure that the RunHandler callbacks refer to that range. For
663        // now we ensure that by not exposing the RunHandler of a TextBlobBuilder.
664        let run_handler = construct(|rh| unsafe {
665            sb::C_SkTextBlobBuilderRunHandler_construct(
666                rh,
667                ptr as *const raw::c_char,
668                offset.into().native(),
669            )
670        });
671        TextBlobBuilderRunHandler(run_handler, PhantomData)
672    }
673
674    pub fn make_blob(&mut self) -> Option<TextBlob> {
675        TextBlob::from_ptr(unsafe { sb::C_SkTextBlobBuilderRunHandler_makeBlob(self.native_mut()) })
676    }
677
678    pub fn end_point(&mut self) -> Point {
679        Point::from_native_c(unsafe {
680            sb::C_SkTextBlobBuilderRunHandler_endPoint(self.native_mut())
681        })
682    }
683}
684
685impl<'a> AsRunHandler<'a> for TextBlobBuilderRunHandler<'_> {
686    type RunHandler = &'a mut SkShaper_RunHandler;
687
688    fn as_run_handler<'b>(&'b mut self) -> Self::RunHandler
689    where
690        'b: 'a,
691    {
692        (*self).as_native_run_handler()
693    }
694}
695
696impl AsNativeRunHandler for &mut SkShaper_RunHandler {
697    fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler {
698        self
699    }
700}
701
702impl AsNativeRunHandler for TextBlobBuilderRunHandler<'_> {
703    fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler {
704        self.0.base_mut()
705    }
706}
707
708impl Shaper {
709    pub fn shape_text_blob(
710        &self,
711        text: &str,
712        font: &Font,
713        left_to_right: bool,
714        width: scalar,
715        offset: impl Into<Point>,
716    ) -> Option<(TextBlob, Point)> {
717        let bytes = text.as_bytes();
718        let mut builder = TextBlobBuilderRunHandler::new(text, offset);
719        unsafe {
720            sb::C_SkShaper_shape(
721                self.native(),
722                bytes.as_ptr() as _,
723                bytes.len(),
724                font.native(),
725                left_to_right,
726                width,
727                builder.native_mut().base_mut(),
728            )
729        };
730        builder.make_blob().map(|tb| (tb, builder.end_point()))
731    }
732}
733
734pub(crate) mod shapers {
735    use super::{BiDiRunIterator, ScriptRunIterator, Shaper};
736
737    #[deprecated(since = "0.74.0", note = "use shapers::primitive::primitive_text()")]
738    pub fn primitive() -> Shaper {
739        primitive::primitive_text()
740    }
741
742    #[deprecated(
743        since = "0.74.0",
744        note = "use shapers::primitive::trivial_bidi_run_iterator()"
745    )]
746    pub fn trivial_bidi_run_iterator(bidi_level: u8, utf8_bytes: usize) -> BiDiRunIterator {
747        primitive::trivial_bidi_run_iterator(bidi_level, utf8_bytes)
748    }
749
750    #[deprecated(
751        since = "0.74.0",
752        note = "use shapers::primitive::trivial_script_run_iterator()"
753    )]
754    pub fn trivial_script_run_iterator(bidi_level: u8, utf8_bytes: usize) -> ScriptRunIterator {
755        primitive::trivial_script_run_iterator(bidi_level, utf8_bytes)
756    }
757
758    pub mod primitive {
759        use skia_bindings as sb;
760
761        use crate::shaper::{BiDiRunIterator, ScriptRunIterator, Shaper};
762
763        pub fn primitive_text() -> Shaper {
764            Shaper::from_ptr(unsafe { sb::C_SkShapers_Primitive_PrimitiveText() }).unwrap()
765        }
766
767        pub fn trivial_bidi_run_iterator(bidi_level: u8, utf8_bytes: usize) -> BiDiRunIterator {
768            BiDiRunIterator::from_ptr(unsafe {
769                sb::C_SkShapers_Primitive_TrivialBidiRunIterator_new(bidi_level, utf8_bytes)
770            })
771            .unwrap()
772        }
773
774        pub fn trivial_script_run_iterator(bidi_level: u8, utf8_bytes: usize) -> ScriptRunIterator {
775            ScriptRunIterator::from_ptr(unsafe {
776                sb::C_SkShapers_Primitive_TrivialScriptRunIterator_new(bidi_level, utf8_bytes)
777            })
778            .unwrap()
779        }
780    }
781}
782
783pub mod icu {
784    /// On Windows, and if the default feature "embed-icudtl" is _not_ set, this function writes the
785    /// file `icudtl.dat` into the current executable's directory making sure that it's available
786    /// when text shaping is used in Skia.
787    ///
788    /// If your executable directory can not be written to, make sure that `icudtl.dat` is
789    /// available.
790    ///
791    /// Note that it is currently not possible to load `icudtl.dat` from another location.
792    ///
793    /// If the default feature "embed-icudtl" is set, the `icudtl.dat` file is directly used from
794    /// memory, so no `icudtl.dat` file is needed.
795    pub fn init() {
796        skia_bindings::icu::init();
797    }
798
799    #[test]
800    #[serial_test::serial]
801    fn test_text_blob_builder_run_handler() {
802        use crate::{Font, FontMgr, FontStyle};
803        init();
804        let str = "العربية";
805        let mut text_blob_builder_run_handler =
806            crate::shaper::TextBlobBuilderRunHandler::new(str, crate::Point::default());
807
808        let font_mgr = FontMgr::new();
809        let default_typeface = font_mgr
810            .legacy_make_typeface(None, FontStyle::default())
811            .unwrap();
812        let default_font = Font::new(default_typeface, 10.0);
813        let shaper = crate::Shaper::new(font_mgr);
814        shaper.shape(
815            str,
816            &default_font,
817            false,
818            10000.0,
819            &mut text_blob_builder_run_handler,
820        );
821
822        let blob = text_blob_builder_run_handler.make_blob().unwrap();
823        let bounds = blob.bounds();
824        assert!(bounds.width() > 0.0 && bounds.height() > 0.0);
825    }
826
827    #[test]
828    #[serial_test::serial]
829    fn icu_init_is_idempotent() {
830        init();
831        init();
832    }
833}