Skip to main content

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::{Font, FontMgr, FourByteTag, Point, TextBlob, prelude::*, scalar};
15
16// The following three are re-exported in `modules.rs` via `mod shapers {}`.
17#[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    // TODO: m79: wrap MakeFontMgrRunIterator with requestName (borrowed), requestStyle and
167    //       a LanguageRunIterator.
168
169    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    // TODO: wrap MakeSkUnicodeHbScriptRunIterator (m88: uses type SkUnicode defined in src/).
268
269    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        // a LanguageRunIterator never accesses the UTF8 string, so it's safe to
318        // not borrow the string.
319        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
466/// A trait for accessing the native run handler instance used in the shape_native* functions.
467pub 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(&param)
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        // we can safely pass a ptr to the utf8 text string to the RunHandler, because it does not
664        // expect it to be 0 terminated, but this introduces another problem because
665        // we can never be sure that the RunHandler callbacks refer to that range. For
666        // now we ensure that by not exposing the RunHandler of a TextBlobBuilder.
667        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    /// On Windows, and if the default feature "embed-icudtl" is _not_ set, this function writes the
788    /// file `icudtl.dat` into the current executable's directory making sure that it's available
789    /// when text shaping is used in Skia.
790    ///
791    /// If your executable directory can not be written to, make sure that `icudtl.dat` is
792    /// available.
793    ///
794    /// Note that it is currently not possible to load `icudtl.dat` from another location.
795    ///
796    /// If the default feature "embed-icudtl" is set, the `icudtl.dat` file is directly used from
797    /// memory, so no `icudtl.dat` file is needed.
798    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}