1use std::{fmt, marker::PhantomData, mem};
2
3use skia_bindings::{
4 self as sb, SkFontArguments, SkFontArguments_Palette, SkFontArguments_VariationPosition,
5};
6
7use crate::prelude::*;
8
9#[repr(C)]
11pub struct FontArguments<'vp, 'p> {
12 args: SkFontArguments,
13 pd_vp: PhantomData<&'vp [variation_position::Coordinate]>,
14 pd_p: PhantomData<&'p [palette::Override]>,
15}
16
17native_transmutable!(SkFontArguments, FontArguments<'_, '_>);
18
19#[derive(Clone, Debug)]
24pub struct VariationPosition<'a> {
25 pub coordinates: &'a [variation_position::Coordinate],
26}
27
28pub mod variation_position {
29 use crate::FourByteTag;
30 use skia_bindings::SkFontArguments_VariationPosition_Coordinate;
31
32 #[derive(Copy, Clone, PartialEq, Default, Debug)]
34 #[repr(C)]
35 pub struct Coordinate {
36 pub axis: FourByteTag,
37 pub value: f32,
38 }
39
40 native_transmutable!(SkFontArguments_VariationPosition_Coordinate, Coordinate);
41
42 #[allow(non_upper_case_globals)]
43 impl Coordinate {
44 pub const wght: FourByteTag = FourByteTag::from_chars('w', 'g', 'h', 't');
45 pub const wdth: FourByteTag = FourByteTag::from_chars('w', 'd', 't', 'h');
46 pub const slnt: FourByteTag = FourByteTag::from_chars('s', 'l', 'n', 't');
47 pub const ital: FourByteTag = FourByteTag::from_chars('i', 't', 'a', 'l');
48 pub const opsz: FourByteTag = FourByteTag::from_chars('o', 'p', 's', 'z');
49 }
50}
51
52#[derive(Clone, Debug)]
60pub struct Palette<'a> {
61 pub index: i32,
62 pub overrides: &'a [palette::Override],
63}
64
65pub mod palette {
66 use crate::Color;
67 use skia_bindings::SkFontArguments_Palette_Override;
68
69 #[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
71 #[repr(C)]
72 pub struct Override {
73 pub index: u16,
74 pub color: Color,
75 }
76
77 native_transmutable!(SkFontArguments_Palette_Override, Override);
78}
79
80impl Drop for FontArguments<'_, '_> {
81 fn drop(&mut self) {
82 unsafe { sb::C_SkFontArguments_destruct(self.native_mut()) }
83 }
84}
85
86impl Default for FontArguments<'_, '_> {
87 fn default() -> Self {
88 FontArguments::new()
89 }
90}
91
92impl fmt::Debug for FontArguments<'_, '_> {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 f.debug_struct("FontArguments")
95 .field("collection_index", &self.collection_index())
96 .field(
97 "variation_design_position",
98 &self.variation_design_position(),
99 )
100 .field("palette", &self.palette())
101 .field("synthetic_bold", &self.synthetic_bold())
102 .field("synthetic_oblique", &self.synthetic_oblique())
103 .finish()
104 }
105}
106
107impl FontArguments<'_, '_> {
108 pub fn new() -> Self {
110 Self::construct(|fa| unsafe {
111 sb::C_SkFontArguments_construct(fa);
112 })
113 }
114
115 pub fn set_collection_index(&mut self, collection_index: usize) -> &mut Self {
122 self.native_mut().fCollectionIndex = collection_index.try_into().unwrap();
123 self
124 }
125
126 pub fn set_variation_design_position(mut self, position: VariationPosition) -> FontArguments {
140 let position = SkFontArguments_VariationPosition {
141 coordinates: position.coordinates.native().as_ptr(),
142 coordinateCount: position.coordinates.len().try_into().unwrap(),
143 };
144 unsafe {
145 sb::C_SkFontArguments_setVariationDesignPosition(self.native_mut(), position);
146 mem::transmute(self)
149 }
150 }
151
152 pub fn collection_index(&self) -> usize {
154 self.native().fCollectionIndex.try_into().unwrap()
155 }
156
157 pub fn variation_design_position(&self) -> VariationPosition {
159 unsafe {
160 let position = sb::C_SkFontArguments_getVariationDesignPosition(self.native());
161 VariationPosition {
162 coordinates: safer::from_raw_parts(
163 position.coordinates as *const _,
164 position.coordinateCount.try_into().unwrap(),
165 ),
166 }
167 }
168 }
169
170 pub fn set_palette(mut self, palette: Palette) -> FontArguments {
179 let palette = SkFontArguments_Palette {
180 index: palette.index,
181 overrides: palette.overrides.native().as_ptr(),
182 overrideCount: palette.overrides.len().try_into().unwrap(),
183 };
184 unsafe {
185 sb::C_SkFontArguments_setPalette(self.native_mut(), palette);
186 mem::transmute(self)
187 }
188 }
189
190 pub fn palette(&self) -> Palette {
192 unsafe {
193 let palette = sb::C_SkFontArguments_getPalette(self.native());
194 Palette {
195 index: palette.index,
196 overrides: safer::from_raw_parts(
197 palette.overrides as *const _,
198 palette.overrideCount.try_into().unwrap(),
199 ),
200 }
201 }
202 }
203
204 pub fn set_synthetic_bold(&mut self, synthetic_bold: impl Into<Option<bool>>) -> &mut Self {
209 unsafe {
210 sb::C_SkFontArguments_setSyntheticBold(
211 self.native_mut(),
212 option_bool_to_ffi(synthetic_bold.into()),
213 );
214 }
215 self
216 }
217
218 pub fn synthetic_bold(&self) -> Option<bool> {
220 ffi_to_option_bool(unsafe { sb::C_SkFontArguments_getSyntheticBold(self.native()) })
221 }
222
223 pub fn set_synthetic_oblique(
228 &mut self,
229 synthetic_oblique: impl Into<Option<bool>>,
230 ) -> &mut Self {
231 unsafe {
232 sb::C_SkFontArguments_setSyntheticOblique(
233 self.native_mut(),
234 option_bool_to_ffi(synthetic_oblique.into()),
235 );
236 }
237 self
238 }
239
240 pub fn synthetic_oblique(&self) -> Option<bool> {
242 ffi_to_option_bool(unsafe { sb::C_SkFontArguments_getSyntheticOblique(self.native()) })
243 }
244}
245
246fn option_bool_to_ffi(value: Option<bool>) -> i32 {
247 match value {
248 Some(true) => 1,
249 Some(false) => 0,
250 None => -1,
251 }
252}
253
254fn ffi_to_option_bool(value: i32) -> Option<bool> {
255 match value {
256 1 => Some(true),
257 0 => Some(false),
258 _ => None,
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
267 fn test_font_arguments_with_no_coordinates() {
268 let fa = FontArguments::new();
269 let coordinates = fa.variation_design_position();
270 assert_eq!(coordinates.coordinates, []);
271 }
272
273 #[test]
274 #[allow(clippy::float_cmp)]
275 fn access_coordinates() {
276 let coordinates = Box::new([variation_position::Coordinate {
277 axis: 0.into(),
278 value: 1.0,
279 }]);
280 let args = FontArguments::new();
281 let pos = VariationPosition {
282 coordinates: coordinates.as_ref(),
283 };
284 let args = args.set_variation_design_position(pos);
285 assert_eq!(args.variation_design_position().coordinates[0].value, 1.0);
286 drop(args);
287 }
288
289 #[test]
290 fn synthetic_style_flags_roundtrip() {
291 let mut args = FontArguments::new();
292
293 assert_eq!(args.synthetic_bold(), None);
294 assert_eq!(args.synthetic_oblique(), None);
295
296 args.set_synthetic_bold(Some(true));
297 assert_eq!(args.synthetic_bold(), Some(true));
298
299 args.set_synthetic_bold(Some(false));
300 assert_eq!(args.synthetic_bold(), Some(false));
301
302 args.set_synthetic_bold(None);
303 assert_eq!(args.synthetic_bold(), None);
304
305 args.set_synthetic_oblique(Some(true));
306 assert_eq!(args.synthetic_oblique(), Some(true));
307
308 args.set_synthetic_oblique(Some(false));
309 assert_eq!(args.synthetic_oblique(), Some(false));
310
311 args.set_synthetic_oblique(None);
312 assert_eq!(args.synthetic_oblique(), None);
313 }
314}