1 | use crate::{prelude::*, scalar, Font, FontMgr, FourByteTag, Point, TextBlob}; |
2 | use skia_bindings::{ |
3 | self as sb, RustRunHandler, SkShaper, SkShaper_BiDiRunIterator, SkShaper_FontRunIterator, |
4 | SkShaper_LanguageRunIterator, SkShaper_RunHandler, SkShaper_RunIterator, |
5 | SkShaper_ScriptRunIterator, SkTextBlobBuilderRunHandler, |
6 | }; |
7 | use std::{ffi::CStr, fmt, marker::PhantomData, os::raw}; |
8 | |
9 | pub use run_handler::RunHandler; |
10 | |
11 | pub type Shaper = RefHandle<SkShaper>; |
12 | unsafe_send_sync!(Shaper); |
13 | |
14 | impl NativeDrop for SkShaper { |
15 | fn drop(&mut self) { |
16 | unsafe { sb::C_SkShaper_delete(self) } |
17 | } |
18 | } |
19 | |
20 | impl Default for Shaper { |
21 | fn default() -> Self { |
22 | Self::new(font_mgr:None) |
23 | } |
24 | } |
25 | |
26 | impl fmt::Debug for Shaper { |
27 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
28 | f.debug_struct(name:"Shaper" ).finish() |
29 | } |
30 | } |
31 | |
32 | impl 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 | |
87 | pub use skia_bindings::SkShaper_Feature as Feature; |
88 | |
89 | pub trait RunIterator { |
90 | fn consume(&mut self); |
91 | fn end_of_current_run(&self) -> usize; |
92 | fn at_end(&self) -> bool; |
93 | } |
94 | |
95 | impl<T> RunIterator for RefHandle<T> |
96 | where |
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 | |
113 | pub type FontRunIterator = RefHandle<SkShaper_FontRunIterator>; |
114 | |
115 | impl NativeBase<SkShaper_RunIterator> for SkShaper_FontRunIterator {} |
116 | |
117 | impl NativeDrop for SkShaper_FontRunIterator { |
118 | fn drop(&mut self) { |
119 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
120 | } |
121 | } |
122 | |
123 | impl 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 | |
131 | impl 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 | |
139 | impl 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 | |
169 | pub type BiDiRunIterator = RefHandle<SkShaper_BiDiRunIterator>; |
170 | |
171 | impl NativeBase<SkShaper_RunIterator> for SkShaper_BiDiRunIterator {} |
172 | |
173 | impl NativeDrop for SkShaper_BiDiRunIterator { |
174 | fn drop(&mut self) { |
175 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
176 | } |
177 | } |
178 | |
179 | impl 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 | |
187 | impl BiDiRunIterator { |
188 | pub fn current_level(&self) -> u8 { |
189 | unsafe { sb::C_SkShaper_BiDiRunIterator_currentLevel(self.native()) } |
190 | } |
191 | } |
192 | |
193 | impl 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 | |
218 | pub type ScriptRunIterator = RefHandle<SkShaper_ScriptRunIterator>; |
219 | |
220 | impl NativeBase<SkShaper_RunIterator> for SkShaper_ScriptRunIterator {} |
221 | |
222 | impl NativeDrop for SkShaper_ScriptRunIterator { |
223 | fn drop(&mut self) { |
224 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
225 | } |
226 | } |
227 | |
228 | impl 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 | |
236 | impl 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 | |
244 | impl 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 | |
277 | pub type LanguageRunIterator = RefHandle<SkShaper_LanguageRunIterator>; |
278 | |
279 | impl NativeBase<SkShaper_RunIterator> for SkShaper_LanguageRunIterator {} |
280 | |
281 | impl NativeDrop for SkShaper_LanguageRunIterator { |
282 | fn drop(&mut self) { |
283 | unsafe { sb::C_SkShaper_RunIterator_delete(self.base_mut()) } |
284 | } |
285 | } |
286 | |
287 | impl 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 | |
295 | impl 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 | |
305 | impl 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 | |
327 | pub 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 | |
455 | pub 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. |
463 | pub trait AsNativeRunHandler { |
464 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler; |
465 | } |
466 | |
467 | impl<'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(¶m) |
476 | } |
477 | } |
478 | |
479 | impl 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 | |
566 | mod 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)] |
638 | pub struct TextBlobBuilderRunHandler<'text>(SkTextBlobBuilderRunHandler, PhantomData<&'text str>); |
639 | |
640 | impl 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 | |
651 | impl NativeBase<SkShaper_RunHandler> for SkTextBlobBuilderRunHandler {} |
652 | |
653 | impl 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 | |
681 | impl<'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 | |
692 | impl AsNativeRunHandler for &mut SkShaper_RunHandler { |
693 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler { |
694 | self |
695 | } |
696 | } |
697 | |
698 | impl AsNativeRunHandler for TextBlobBuilderRunHandler<'_> { |
699 | fn as_native_run_handler(&mut self) -> &mut SkShaper_RunHandler { |
700 | self.0.base_mut() |
701 | } |
702 | } |
703 | |
704 | impl 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 | |
730 | pub 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 | |