Skip to main content

incular_assets/
lib.rs

1//! Font handles and non-raster resource foundations for Incular.
2//!
3//! Raster image loading deliberately lives in `incular-image`, which keeps
4//! renderer and widget users from depending on unrelated asset categories.
5
6use std::{fmt, sync::Arc};
7
8/// Stable renderer-independent identity for a loaded font asset.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10pub struct FontId(pub u64);
11
12/// Immutable, cheaply cloned font source bytes. The source may be a heap
13/// allocation or a read-only file mapping owned by system font discovery, so
14/// sharing a handle never copies the font file.
15#[derive(Clone)]
16pub struct FontBytes(Arc<dyn AsRef<[u8]> + Send + Sync>);
17
18impl FontBytes {
19    /// Shares an existing byte source without copying it.
20    #[must_use]
21    pub fn from_shared(source: Arc<dyn AsRef<[u8]> + Send + Sync>) -> Self {
22        Self(source)
23    }
24}
25impl std::ops::Deref for FontBytes {
26    type Target = [u8];
27    fn deref(&self) -> &[u8] {
28        (*self.0).as_ref()
29    }
30}
31impl AsRef<[u8]> for FontBytes {
32    fn as_ref(&self) -> &[u8] {
33        self
34    }
35}
36impl PartialEq for FontBytes {
37    fn eq(&self, other: &Self) -> bool {
38        Arc::ptr_eq(&self.0, &other.0) || **self == **other
39    }
40}
41impl Eq for FontBytes {}
42impl From<Arc<[u8]>> for FontBytes {
43    fn from(bytes: Arc<[u8]>) -> Self {
44        Self(Arc::new(bytes))
45    }
46}
47impl From<Vec<u8>> for FontBytes {
48    fn from(bytes: Vec<u8>) -> Self {
49        Self(Arc::new(bytes))
50    }
51}
52impl From<&'static [u8]> for FontBytes {
53    fn from(bytes: &'static [u8]) -> Self {
54        Self(Arc::new(bytes))
55    }
56}
57impl<const N: usize> From<[u8; N]> for FontBytes {
58    fn from(bytes: [u8; N]) -> Self {
59        Self(Arc::new(bytes))
60    }
61}
62
63/// Font bytes and their stable identity. Paths are deliberately not part of
64/// the public identity: system discovery and bundled assets both produce this
65/// same handle.
66#[derive(Clone, PartialEq, Eq)]
67pub struct FontHandle {
68    id: FontId,
69    bytes: FontBytes,
70    face_index: u32,
71}
72impl FontHandle {
73    #[must_use]
74    pub fn new(id: FontId, bytes: impl Into<FontBytes>) -> Self {
75        Self::with_face_index(id, bytes, 0)
76    }
77    /// Creates a handle for a face in an OpenType collection. The caller owns
78    /// the identity: collection index must be included in `id`.
79    #[must_use]
80    pub fn with_face_index(id: FontId, bytes: impl Into<FontBytes>, face_index: u32) -> Self {
81        Self {
82            id,
83            bytes: bytes.into(),
84            face_index,
85        }
86    }
87    #[must_use]
88    pub const fn id(&self) -> FontId {
89        self.id
90    }
91    #[must_use]
92    pub fn bytes(&self) -> &FontBytes {
93        &self.bytes
94    }
95    #[must_use]
96    pub const fn face_index(&self) -> u32 {
97        self.face_index
98    }
99}
100impl fmt::Debug for FontHandle {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        f.debug_struct("FontHandle")
103            .field("id", &self.id)
104            .field("face_index", &self.face_index)
105            .finish_non_exhaustive()
106    }
107}