1use std::{ffi, mem};
2
3use skia_bindings::SkTextEncoding;
4
5use crate::GlyphId;
6
7#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
8#[repr(i32)]
9pub enum TextEncoding {
10 #[default]
11 UTF8 = SkTextEncoding::kUTF8 as _,
12 UTF16 = SkTextEncoding::kUTF16 as _,
13 UTF32 = SkTextEncoding::kUTF32 as _,
14 // TODO: enum rewriter: ID -> Id
15 GlyphId = SkTextEncoding::kGlyphID as _,
16}
17
18native_transmutable!(SkTextEncoding, TextEncoding, text_encoding_layout);
19
20/// Trait representing encoded text.
21///
22/// Functions that expect `EncodedText` may be passed `String`, `&String``, `&str` representing
23/// UTF-8 encoded text. In addition to that, &[u16], [u16], or &[GlyphId], [GlyphId], are
24/// interpreted as `GlyphId` slices.
25///
26/// To use UTF16 or UTF32 encodings, use [`as_utf16_unchecked`] or
27/// [`as_utf32_unchecked`].
28pub trait EncodedText {
29 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding);
30}
31
32/// Treat a `&[u16]` as UTF16 encoded text.
33///
34/// # Safety
35/// The slice may not represent a UTF16 encoded string.
36pub unsafe fn as_utf16_unchecked(slice: &[u16]) -> impl EncodedText + use<'_> {
37 UTF16Slice(slice)
38}
39
40/// Treat a `&[u32]` as UTF32 encoded text.
41///
42/// # Safety
43/// The slice may not represent an actual UTF32 encoded string.
44pub unsafe fn as_utf32_unchecked(slice: &[u32]) -> impl EncodedText + use<'_> {
45 UTF32Slice(slice)
46}
47
48struct UTF16Slice<'a>(&'a [u16]);
49
50impl EncodedText for UTF16Slice<'_> {
51 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) {
52 let slice: &[u16] = self.0;
53 let size: usize = mem::size_of_val(slice);
54 (slice.as_ptr() as _, size, TextEncoding::UTF16)
55 }
56}
57
58struct UTF32Slice<'a>(&'a [u32]);
59
60impl EncodedText for UTF32Slice<'_> {
61 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) {
62 let slice: &[u32] = self.0;
63 let size: usize = mem::size_of_val(slice);
64 (slice.as_ptr() as _, size, TextEncoding::UTF32)
65 }
66}
67
68impl EncodedText for String {
69 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) {
70 self.as_str().as_raw()
71 }
72}
73
74impl EncodedText for &str {
75 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) {
76 let bytes: &[u8] = self.as_bytes();
77 (bytes.as_ptr() as _, bytes.len(), TextEncoding::UTF8)
78 }
79}
80
81impl EncodedText for &[GlyphId] {
82 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) {
83 (
84 self.as_ptr() as _,
85 mem::size_of_val(*self),
86 TextEncoding::GlyphId,
87 )
88 }
89}
90
91impl<const N: usize> EncodedText for [GlyphId; N] {
92 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) {
93 (
94 self.as_ptr() as _,
95 mem::size_of_val(self),
96 TextEncoding::GlyphId,
97 )
98 }
99}
100
101impl<T: EncodedText> EncodedText for &T {
102 fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) {
103 (**self).as_raw()
104 }
105}
106
107pub use skia_bindings::SkFontHinting as FontHinting;
108variant_name!(FontHinting::Full);
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn glyph_id_size() {
116 let glyphs = [0u16, 1u16];
117 assert_eq!(glyphs.as_raw().1, 4);
118
119 let glyphs = [0u16, 1u16].as_slice();
120 assert_eq!(glyphs.as_raw().1, 4);
121 }
122
123 #[test]
124 fn utf16_size() {
125 let utf16 = unsafe { as_utf16_unchecked(&[0u16, 1u16]) };
126 assert_eq!(utf16.as_raw().1, 4);
127 }
128
129 #[test]
130 fn utf32_size() {
131 let utf16 = unsafe { as_utf32_unchecked(&[0u32, 1u32]) };
132 assert_eq!(utf16.as_raw().1, 8);
133 }
134
135 #[test]
136 fn usage() {
137 test("Hello");
138 test("Hello".to_string());
139 let x = &"Hello".to_string();
140 test(x);
141 test(unsafe { as_utf16_unchecked(&[1, 2]) });
142 test(unsafe { as_utf32_unchecked(&[1, 2]) });
143 // glyph Ids
144 test([10u16, 11u16]);
145 let x = &[10u16, 11u16];
146 test(x);
147 test([10u16, 11u16].as_slice());
148
149 fn test(_et: impl EncodedText) {}
150 }
151}
152