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 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
463pub 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.
471pub trait AsNativeRunHandler {
472    fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler;
473}
474
475impl<'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 = unsafe { rust_run_handler::new_param(self) };
483        rust_run_handler::from_param(&param)
484    }
485}
486
487impl 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
574mod 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)]
649pub struct TextBlobBuilderRunHandler<'text>(SkTextBlobBuilderRunHandler, PhantomData<&'text str>);
650
651impl 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
662impl NativeBase<SkShaper_RunHandler> for SkTextBlobBuilderRunHandler {}
663
664impl 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
692impl<'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
703impl AsNativeRunHandler for &mut SkShaper_RunHandler {
704    fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler {
705        self
706    }
707}
708
709impl AsNativeRunHandler for TextBlobBuilderRunHandler<'_> {
710    fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler {
711        self.0.base_mut()
712    }
713}
714
715impl 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
741pub(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
790pub 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}