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