| 1 | use std::{ffi, mem}; |
| 2 | |
| 3 | use skia_bindings::SkTextEncoding; |
| 4 | |
| 5 | use crate::GlyphId; |
| 6 | |
| 7 | #[derive (Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] |
| 8 | #[repr (i32)] |
| 9 | pub 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 | |
| 18 | native_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`]. |
| 28 | pub 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. |
| 36 | pub 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. |
| 44 | pub unsafe fn as_utf32_unchecked(slice: &[u32]) -> impl EncodedText + use<'_> { |
| 45 | UTF32Slice(slice) |
| 46 | } |
| 47 | |
| 48 | struct UTF16Slice<'a>(&'a [u16]); |
| 49 | |
| 50 | impl 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 | |
| 58 | struct UTF32Slice<'a>(&'a [u32]); |
| 59 | |
| 60 | impl 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 | |
| 68 | impl EncodedText for String { |
| 69 | fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) { |
| 70 | self.as_str().as_raw() |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | impl 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 | |
| 81 | impl 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 | |
| 91 | impl<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 | |
| 101 | impl<T: EncodedText> EncodedText for &T { |
| 102 | fn as_raw(&self) -> (*const ffi::c_void, usize, TextEncoding) { |
| 103 | (**self).as_raw() |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | pub use skia_bindings::SkFontHinting as FontHinting; |
| 108 | variant_name!(FontHinting::Full); |
| 109 | |
| 110 | #[cfg (test)] |
| 111 | mod 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 | |