1#[cfg(target_os = "macos")]
6use core_foundation::string::CFString;
7#[cfg(target_os = "macos")]
8use core_graphics::font::CGFont;
9use peek_poke::PeekPoke;
10#[cfg(target_os = "macos")]
11use serde::de::{self, Deserialize, Deserializer};
12#[cfg(target_os = "macos")]
13use serde::ser::{Serialize, Serializer};
14use std::cmp::Ordering;
15use std::hash::{Hash, Hasher};
16#[cfg(not(target_os = "macos"))]
17use std::path::PathBuf;
18use std::sync::{Arc, RwLock, RwLockReadGuard};
19use std::collections::HashMap;
20use crate::IdNamespace;
22use crate::channel::Sender;
23use crate::color::ColorU;
24use crate::units::LayoutPoint;
25
26#[repr(C)]
28#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, Deserialize, Serialize)]
29pub struct FontSize(pub f32);
30
31impl Ord for FontSize {
32 fn cmp(&self, other: &FontSize) -> Ordering {
33 self.partial_cmp(other).unwrap_or(Ordering::Equal)
34 }
35}
36
37impl Eq for FontSize {}
38
39impl Hash for FontSize {
40 fn hash<H: Hasher>(&self, state: &mut H) {
41 self.0.to_bits().hash(state);
42 }
43}
44
45impl From<f32> for FontSize {
46 fn from(size: f32) -> Self { FontSize(size) }
47}
48
49impl From<FontSize> for f32 {
50 fn from(size: FontSize) -> Self { size.0 }
51}
52
53impl FontSize {
54 pub fn zero() -> Self { FontSize(0.0) }
55
56 pub fn from_f32_px(size: f32) -> Self { FontSize(size) }
57
58 pub fn to_f32_px(&self) -> f32 { self.0 }
59
60 pub fn from_f64_px(size: f64) -> Self { FontSize(size as f32) }
61
62 pub fn to_f64_px(&self) -> f64 { self.0 as f64 }
63}
64
65#[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd, MallocSizeOf)]
70#[cfg_attr(feature = "serialize", derive(Serialize))]
71#[cfg_attr(feature = "deserialize", derive(Deserialize))]
72pub struct BaseFontInstance {
73 pub instance_key: FontInstanceKey,
75 pub font_key: FontKey,
77 pub size: FontSize,
79 pub bg_color: ColorU,
81 pub render_mode: FontRenderMode,
83 pub flags: FontInstanceFlags,
85 pub synthetic_italics: SyntheticItalics,
87 #[cfg_attr(any(feature = "serialize", feature = "deserialize"), serde(skip))]
89 pub platform_options: Option<FontInstancePlatformOptions>,
90 pub variations: Vec<FontVariation>,
92}
93
94pub type FontInstanceMap = HashMap<FontInstanceKey, Arc<BaseFontInstance>>;
95#[derive(Clone)]
97#[cfg_attr(feature = "serialize", derive(Serialize))]
98#[cfg_attr(feature = "deserialize", derive(Deserialize))]
99pub struct SharedFontInstanceMap {
100 map: Arc<RwLock<FontInstanceMap>>,
101}
102
103impl SharedFontInstanceMap {
104 pub fn new() -> Self {
106 SharedFontInstanceMap {
107 map: Arc::new(RwLock::new(HashMap::default()))
108 }
109 }
110
111 pub fn lock(&mut self) -> Option<RwLockReadGuard<FontInstanceMap>> {
113 self.map.read().ok()
114 }
115
116 pub fn get_font_instance_data(&self, key: FontInstanceKey) -> Option<FontInstanceData> {
118 match self.map.read().unwrap().get(&key) {
119 Some(instance) => Some(FontInstanceData {
120 font_key: instance.font_key,
121 size: instance.size.into(),
122 options: Some(FontInstanceOptions {
123 render_mode: instance.render_mode,
124 flags: instance.flags,
125 bg_color: instance.bg_color,
126 synthetic_italics: instance.synthetic_italics,
127 }),
128 platform_options: instance.platform_options,
129 variations: instance.variations.clone(),
130 }),
131 None => None,
132 }
133 }
134
135 pub fn set(&mut self, map: FontInstanceMap) {
137 *self.map.write().unwrap() = map;
138 }
139
140 pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<Arc<BaseFontInstance>> {
142 let instance_map = self.map.read().unwrap();
143 instance_map.get(&instance_key).map(|instance| { Arc::clone(instance) })
144 }
145
146 pub fn add_font_instance(
148 &mut self,
149 instance_key: FontInstanceKey,
150 font_key: FontKey,
151 size: f32,
152 options: Option<FontInstanceOptions>,
153 platform_options: Option<FontInstancePlatformOptions>,
154 variations: Vec<FontVariation>,
155 ) {
156 let FontInstanceOptions {
157 render_mode,
158 flags,
159 bg_color,
160 synthetic_italics,
161 ..
162 } = options.unwrap_or_default();
163
164 let instance = Arc::new(BaseFontInstance {
165 instance_key,
166 font_key,
167 size: size.into(),
168 bg_color,
169 render_mode,
170 flags,
171 synthetic_italics,
172 platform_options,
173 variations,
174 });
175
176 self.map
177 .write()
178 .unwrap()
179 .insert(instance_key, instance);
180 }
181
182 pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
184 self.map.write().unwrap().remove(&instance_key);
185 }
186
187 pub fn clear_namespace(&mut self, namespace: IdNamespace) {
189 self.map
190 .write()
191 .unwrap()
192 .retain(|key, _| key.0 != namespace);
193 }
194
195 pub fn clone_map(&self) -> FontInstanceMap {
197 self.map.read().unwrap().clone()
198 }
199}
200
201#[cfg(not(target_os = "macos"))]
202#[derive(Clone, Debug, Serialize, Deserialize)]
203pub struct NativeFontHandle {
204 pub path: PathBuf,
205 pub index: u32,
206}
207
208#[cfg(target_os = "macos")]
209#[derive(Clone)]
210pub struct NativeFontHandle(pub CGFont);
211
212#[cfg(target_os = "macos")]
213impl Serialize for NativeFontHandle {
214 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
215 where
216 S: Serializer,
217 {
218 self.0
219 .postscript_name()
220 .to_string()
221 .serialize(serializer)
222 }
223}
224
225#[cfg(target_os = "macos")]
226impl<'de> Deserialize<'de> for NativeFontHandle {
227 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
228 where
229 D: Deserializer<'de>,
230 {
231 let postscript_name: String = Deserialize::deserialize(deserializer)?;
232
233 match CGFont::from_name(&CFString::new(&*postscript_name)) {
234 Ok(font) => Ok(NativeFontHandle(font)),
235 Err(_) => Err(de::Error::custom(
236 "Couldn't find a font with that PostScript name!",
237 )),
238 }
239 }
240}
241
242#[repr(C)]
243#[derive(Copy, Clone, Deserialize, Serialize, Debug)]
244pub struct GlyphDimensions {
245 pub left: i32,
246 pub top: i32,
247 pub width: i32,
248 pub height: i32,
249 pub advance: f32,
250}
251
252pub struct GlyphDimensionRequest {
253 pub key: FontInstanceKey,
254 pub glyph_indices: Vec<GlyphIndex>,
255 pub sender: Sender<Vec<Option<GlyphDimensions>>>,
256}
257
258pub struct GlyphIndexRequest {
259 pub key: FontKey,
260 pub text: String,
261 pub sender: Sender<Vec<Option<u32>>>,
262}
263
264#[repr(C)]
265#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, Ord, PartialOrd)]
266pub struct FontKey(pub IdNamespace, pub u32);
267
268impl FontKey {
269 pub fn new(namespace: IdNamespace, key: u32) -> FontKey {
270 FontKey(namespace, key)
271 }
272}
273
274#[derive(Clone)]
282pub enum FontTemplate {
283 Raw(Arc<Vec<u8>>, u32),
284 Native(NativeFontHandle),
285}
286
287#[repr(u8)]
288#[derive(Debug, Copy, Clone, Hash, Eq, MallocSizeOf, PartialEq, Serialize, Deserialize, Ord, PartialOrd, PeekPoke)]
289pub enum FontRenderMode {
290 Mono = 0,
291 Alpha,
292 Subpixel,
293}
294
295impl Default for FontRenderMode {
296 fn default() -> Self {
297 FontRenderMode::Mono
298 }
299}
300
301impl FontRenderMode {
302 pub fn limit_by(self, other: FontRenderMode) -> FontRenderMode {
304 match (self, other) {
305 (FontRenderMode::Subpixel, _) | (_, FontRenderMode::Mono) => other,
306 _ => self,
307 }
308 }
309}
310
311#[repr(C)]
312#[derive(Clone, Copy, Debug, MallocSizeOf, PartialOrd, Deserialize, Serialize)]
313pub struct FontVariation {
314 pub tag: u32,
315 pub value: f32,
316}
317
318impl Ord for FontVariation {
319 fn cmp(&self, other: &FontVariation) -> Ordering {
320 self.tag.cmp(&other.tag)
321 .then(self.value.to_bits().cmp(&other.value.to_bits()))
322 }
323}
324
325impl PartialEq for FontVariation {
326 fn eq(&self, other: &FontVariation) -> bool {
327 self.tag == other.tag &&
328 self.value.to_bits() == other.value.to_bits()
329 }
330}
331
332impl Eq for FontVariation {}
333
334impl Hash for FontVariation {
335 fn hash<H: Hasher>(&self, state: &mut H) {
336 self.tag.hash(state);
337 self.value.to_bits().hash(state);
338 }
339}
340
341#[repr(C)]
342#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize, PeekPoke)]
343pub struct GlyphOptions {
344 pub render_mode: FontRenderMode,
345 pub flags: FontInstanceFlags,
346}
347
348impl Default for GlyphOptions {
349 fn default() -> Self {
350 GlyphOptions {
351 render_mode: FontRenderMode::Subpixel,
352 flags: FontInstanceFlags::empty(),
353 }
354 }
355}
356
357bitflags! {
358 #[repr(C)]
359 #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)]
360 pub struct FontInstanceFlags: u32 {
361 const SYNTHETIC_BOLD = 1 << 1;
363 const EMBEDDED_BITMAPS = 1 << 2;
364 const SUBPIXEL_BGR = 1 << 3;
365 const TRANSPOSE = 1 << 4;
366 const FLIP_X = 1 << 5;
367 const FLIP_Y = 1 << 6;
368 const SUBPIXEL_POSITION = 1 << 7;
369 const VERTICAL = 1 << 8;
370
371 const TRANSFORM_GLYPHS = 1 << 12;
373 const TEXTURE_PADDING = 1 << 13;
374
375 const FORCE_GDI = 1 << 16;
377 const FORCE_SYMMETRIC = 1 << 17;
378 const NO_SYMMETRIC = 1 << 18;
379
380 const FONT_SMOOTHING = 1 << 16;
382
383 const FORCE_AUTOHINT = 1 << 16;
385 const NO_AUTOHINT = 1 << 17;
386 const VERTICAL_LAYOUT = 1 << 18;
387 const LCD_VERTICAL = 1 << 19;
388 }
389}
390
391impl Default for FontInstanceFlags {
392 #[cfg(target_os = "windows")]
393 fn default() -> FontInstanceFlags {
394 FontInstanceFlags::SUBPIXEL_POSITION
395 }
396
397 #[cfg(target_os = "macos")]
398 fn default() -> FontInstanceFlags {
399 FontInstanceFlags::SUBPIXEL_POSITION |
400 FontInstanceFlags::FONT_SMOOTHING
401 }
402
403 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
404 fn default() -> FontInstanceFlags {
405 FontInstanceFlags::SUBPIXEL_POSITION
406 }
407}
408
409
410#[repr(C)]
411#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
412pub struct SyntheticItalics {
413 pub angle: i16,
415}
416
417impl SyntheticItalics {
418 pub const ANGLE_SCALE: f32 = 256.0;
419
420 pub fn from_degrees(degrees: f32) -> Self {
421 SyntheticItalics { angle: (degrees.max(-89.0).min(89.0) * Self::ANGLE_SCALE) as i16 }
422 }
423
424 pub fn to_degrees(self) -> f32 {
425 self.angle as f32 / Self::ANGLE_SCALE
426 }
427
428 pub fn to_radians(self) -> f32 {
429 self.to_degrees().to_radians()
430 }
431
432 pub fn to_skew(self) -> f32 {
433 self.to_radians().tan()
434 }
435
436 pub fn enabled() -> Self {
437 Self::from_degrees(14.0)
438 }
439
440 pub fn disabled() -> Self {
441 SyntheticItalics { angle: 0 }
442 }
443
444 pub fn is_enabled(self) -> bool {
445 self.angle != 0
446 }
447}
448
449impl Default for SyntheticItalics {
450 fn default() -> Self {
451 SyntheticItalics::disabled()
452 }
453}
454
455#[repr(C)]
456#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
457pub struct FontInstanceOptions {
458 pub render_mode: FontRenderMode,
459 pub flags: FontInstanceFlags,
460 pub bg_color: ColorU,
464 pub synthetic_italics: SyntheticItalics,
465}
466
467impl Default for FontInstanceOptions {
468 fn default() -> FontInstanceOptions {
469 FontInstanceOptions {
470 render_mode: FontRenderMode::Subpixel,
471 flags: Default::default(),
472 bg_color: ColorU::new(0, 0, 0, 0),
473 synthetic_italics: SyntheticItalics::disabled(),
474 }
475 }
476}
477
478#[cfg(target_os = "windows")]
479#[repr(C)]
480#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
481pub struct FontInstancePlatformOptions {
482 pub gamma: u16, pub contrast: u8, pub cleartype_level: u8, }
486
487#[cfg(target_os = "windows")]
488impl Default for FontInstancePlatformOptions {
489 fn default() -> FontInstancePlatformOptions {
490 FontInstancePlatformOptions {
491 gamma: 180, contrast: 100,
493 cleartype_level: 100,
494 }
495 }
496}
497
498#[cfg(target_os = "macos")]
499#[repr(C)]
500#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
501pub struct FontInstancePlatformOptions {
502 pub unused: u32,
503}
504
505#[cfg(target_os = "macos")]
506impl Default for FontInstancePlatformOptions {
507 fn default() -> FontInstancePlatformOptions {
508 FontInstancePlatformOptions {
509 unused: 0,
510 }
511 }
512}
513
514#[cfg(not(any(target_os = "macos", target_os = "windows")))]
515#[repr(u8)]
516#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
517pub enum FontLCDFilter {
518 None,
519 Default,
520 Light,
521 Legacy,
522}
523
524#[cfg(not(any(target_os = "macos", target_os = "windows")))]
525#[repr(u8)]
526#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
527pub enum FontHinting {
528 None,
529 Mono,
530 Light,
531 Normal,
532 LCD,
533}
534
535#[cfg(not(any(target_os = "macos", target_os = "windows")))]
536#[repr(C)]
537#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, MallocSizeOf, PartialEq, PartialOrd, Ord, Serialize)]
538pub struct FontInstancePlatformOptions {
539 pub lcd_filter: FontLCDFilter,
540 pub hinting: FontHinting,
541}
542
543#[cfg(not(any(target_os = "macos", target_os = "windows")))]
544impl Default for FontInstancePlatformOptions {
545 fn default() -> FontInstancePlatformOptions {
546 FontInstancePlatformOptions {
547 lcd_filter: FontLCDFilter::Default,
548 hinting: FontHinting::LCD,
549 }
550 }
551}
552
553#[repr(C)]
554#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Ord, PartialOrd, MallocSizeOf, PeekPoke)]
555#[derive(Deserialize, Serialize)]
556pub struct FontInstanceKey(pub IdNamespace, pub u32);
557
558impl FontInstanceKey {
559 pub fn new(namespace: IdNamespace, key: u32) -> FontInstanceKey {
560 FontInstanceKey(namespace, key)
561 }
562}
563
564#[derive(Clone)]
569pub struct FontInstanceData {
570 pub font_key: FontKey,
571 pub size: f32,
572 pub options: Option<FontInstanceOptions>,
573 pub platform_options: Option<FontInstancePlatformOptions>,
574 pub variations: Vec<FontVariation>,
575}
576
577pub type GlyphIndex = u32;
578
579#[repr(C)]
580#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, PeekPoke)]
581pub struct GlyphInstance {
582 pub index: GlyphIndex,
583 pub point: LayoutPoint,
584}
585
586impl Default for GlyphInstance {
587 fn default() -> Self {
588 GlyphInstance {
589 index: 0,
590 point: LayoutPoint::zero(),
591 }
592 }
593}
594
595impl Eq for GlyphInstance {}
596
597#[cfg_attr(feature = "cargo-clippy", allow(clippy::derive_hash_xor_eq))]
598impl Hash for GlyphInstance {
599 fn hash<H: Hasher>(&self, state: &mut H) {
600 self.index.hash(state);
602 self.point.x.to_bits().hash(state);
603 self.point.y.to_bits().hash(state);
604 }
605}