1 | use std::{ |
2 | ffi::{CStr, CString}, |
3 | fmt, |
4 | marker::PhantomData, |
5 | os::raw, |
6 | }; |
7 | |
8 | use 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 | |
14 | use crate::{prelude::*, scalar, Font, FontMgr, FourByteTag, Point, TextBlob}; |
15 | |
16 | // The following three are re-exported in `modules.rs` via `mod shapers {}`. |
17 | pub(crate) mod core_text; |
18 | pub(crate) mod harfbuzz; |
19 | pub(crate) mod unicode; |
20 | |
21 | pub use run_handler::RunHandler; |
22 | |
23 | pub type Shaper = RefHandle<SkShaper>; |
24 | unsafe_send_sync!(Shaper); |
25 | |
26 | impl NativeDrop for SkShaper { |
27 | fn drop(&mut self) { |
28 | unsafe { sb::C_SkShaper_delete(self) } |
29 | } |
30 | } |
31 | |
32 | impl Default for Shaper { |
33 | fn default() -> Self { |
34 | Self::new(font_mgr:None) |
35 | } |
36 | } |
37 | |
38 | impl fmt::Debug for Shaper { |
39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
40 | f.debug_struct(name:"Shaper" ).finish() |
41 | } |
42 | } |
43 | |
44 | impl 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 | |
93 | pub use skia_bindings::SkShaper_Feature as Feature; |
94 | |
95 | pub trait RunIterator { |
96 | fn consume(&mut self); |
97 | fn end_of_current_run(&self) -> usize; |
98 | fn at_end(&self) -> bool; |
99 | } |
100 | |
101 | impl<T> RunIterator for RefHandle<T> |
102 | where |
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 | |
119 | pub type FontRunIterator = RefHandle<SkShaper_FontRunIterator>; |
120 | |
121 | impl NativeBase<SkShaper_RunIterator> for SkShaper_FontRunIterator {} |
122 | |
123 | impl NativeDrop for SkShaper_FontRunIterator { |
124 | fn drop(&mut self) { |
125 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
126 | } |
127 | } |
128 | |
129 | impl fmt::Debug for FontRunIterator { |
130 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
131 | f&mut DebugStruct<'_, '_>.debug_struct("FontRunIterator" ) |
132 | .field(name:"current_font" , &self.current_font()) |
133 | .finish() |
134 | } |
135 | } |
136 | |
137 | impl 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 | |
145 | impl 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 | |
175 | pub type BiDiRunIterator = RefHandle<SkShaper_BiDiRunIterator>; |
176 | |
177 | impl NativeBase<SkShaper_RunIterator> for SkShaper_BiDiRunIterator {} |
178 | |
179 | impl NativeDrop for SkShaper_BiDiRunIterator { |
180 | fn drop(&mut self) { |
181 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
182 | } |
183 | } |
184 | |
185 | impl fmt::Debug for BiDiRunIterator { |
186 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
187 | f&mut DebugStruct<'_, '_>.debug_struct("BiDiRunIterator" ) |
188 | .field(name:"current_level" , &self.current_level()) |
189 | .finish() |
190 | } |
191 | } |
192 | |
193 | impl BiDiRunIterator { |
194 | pub fn current_level(&self) -> u8 { |
195 | unsafe { sb::C_SkShaper_BiDiRunIterator_currentLevel(self.native()) } |
196 | } |
197 | } |
198 | |
199 | impl 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 | |
225 | pub type ScriptRunIterator = RefHandle<SkShaper_ScriptRunIterator>; |
226 | |
227 | impl NativeBase<SkShaper_RunIterator> for SkShaper_ScriptRunIterator {} |
228 | |
229 | impl NativeDrop for SkShaper_ScriptRunIterator { |
230 | fn drop(&mut self) { |
231 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
232 | } |
233 | } |
234 | |
235 | impl fmt::Debug for ScriptRunIterator { |
236 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
237 | f&mut DebugStruct<'_, '_>.debug_struct("ScriptRunIterator" ) |
238 | .field(name:"current_script" , &self.current_script()) |
239 | .finish() |
240 | } |
241 | } |
242 | |
243 | impl ScriptRunIterator { |
244 | pub fn current_script(&self) -> FourByteTag { |
245 | FourByteTag::from_native_c(nt:unsafe { |
246 | sb::C_SkShaper_ScriptRunIterator_currentScript(self.native()) |
247 | }) |
248 | } |
249 | } |
250 | |
251 | impl 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 | |
285 | pub type LanguageRunIterator = RefHandle<SkShaper_LanguageRunIterator>; |
286 | |
287 | impl NativeBase<SkShaper_RunIterator> for SkShaper_LanguageRunIterator {} |
288 | |
289 | impl NativeDrop for SkShaper_LanguageRunIterator { |
290 | fn drop(&mut self) { |
291 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
292 | } |
293 | } |
294 | |
295 | impl fmt::Debug for LanguageRunIterator { |
296 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
297 | f&mut DebugStruct<'_, '_>.debug_struct("LanguageRunIterator" ) |
298 | .field(name:"current_language" , &self.current_language()) |
299 | .finish() |
300 | } |
301 | } |
302 | |
303 | impl 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 | |
313 | impl 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: &[u8] = utf8.as_bytes(); |
318 | LanguageRunIterator::from_ptr(unsafe { |
319 | sb::C_SkShaper_MakeStdLanguageRunIterator(utf8:bytes.as_ptr() as _, utf8Bytes: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 = CString::new(language.as_ref()).unwrap(); |
328 | LanguageRunIteratorOption>::from_ptr(unsafe { |
329 | sb::C_SkShaper_TrivialLanguageRunIterator_new(language.as_ptr(), utf8Bytes:utf8_bytes) |
330 | }) |
331 | .unwrap() |
332 | } |
333 | } |
334 | |
335 | pub 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 | |
463 | pub 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. |
471 | pub trait AsNativeRunHandler { |
472 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler; |
473 | } |
474 | |
475 | impl<'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: RustRunHandler_Param = unsafe { rust_run_handler::new_param(self) }; |
483 | rust_run_handler::from_param(¶m) |
484 | } |
485 | } |
486 | |
487 | impl 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 | |
574 | mod 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)] |
649 | pub struct TextBlobBuilderRunHandler<'text>(SkTextBlobBuilderRunHandler, PhantomData<&'text str>); |
650 | |
651 | impl 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 | |
662 | impl NativeBase<SkShaper_RunHandler> for SkTextBlobBuilderRunHandler {} |
663 | |
664 | impl 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 | |
692 | impl<'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 | |
703 | impl AsNativeRunHandler for &mut SkShaper_RunHandler { |
704 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler { |
705 | self |
706 | } |
707 | } |
708 | |
709 | impl AsNativeRunHandler for TextBlobBuilderRunHandler<'_> { |
710 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler { |
711 | self.0.base_mut() |
712 | } |
713 | } |
714 | |
715 | impl 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 | |
741 | pub(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 | |
790 | pub 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 | } |
841 | |