hb_subset/
common.rs

1use std::{
2    borrow::Borrow,
3    ffi::{c_char, CStr},
4    fmt,
5    ptr::null,
6    str::FromStr,
7};
8
9use crate::{sys, AllocationError};
10
11/// Four byte integers, each byte representing a character.
12///
13/// Tags are used to identify tables, design-variation axes, scripts, languages, font features, and baselines with
14/// human-readable names.
15#[derive(Clone, Copy)]
16pub struct Tag(u32);
17
18impl Tag {
19    /// Constructs a new tag from bytes.
20    ///
21    /// # Example
22    /// ```
23    /// # use hb_subset::*;
24    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
25    /// let mut subset = SubsetInput::new()?;
26    /// // Remove character-to-glyph mapping data. This can be useful in PDF files where
27    /// // the mapping and positioning has already been done.
28    /// subset.drop_table_tag_set().insert(Tag::new(b"cmap"));
29    /// # Ok(())
30    /// # }
31    /// ```
32    pub fn new(tag: impl Borrow<[u8; 4]>) -> Self {
33        Self(u32::from_be_bytes(*tag.borrow()))
34    }
35}
36
37impl From<Tag> for u32 {
38    fn from(tag: Tag) -> Self {
39        tag.0
40    }
41}
42
43impl From<u32> for Tag {
44    fn from(tag: u32) -> Self {
45        Self(tag)
46    }
47}
48
49impl From<Tag> for [u8; 4] {
50    fn from(tag: Tag) -> Self {
51        tag.0.to_be_bytes()
52    }
53}
54
55impl fmt::Debug for Tag {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        struct FieldFormatter([u8; 4]);
58        impl fmt::Debug for FieldFormatter {
59            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60                write!(
61                    f,
62                    "{}{}{}{}",
63                    self.0[0] as char, self.0[1] as char, self.0[2] as char, self.0[3] as char
64                )
65            }
66        }
67        f.debug_tuple("Tag")
68            .field(&FieldFormatter((*self).into()))
69            .finish()
70    }
71}
72
73/// Data type for languages.
74///
75/// Corresponds to a [BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag).
76#[derive(Clone, Copy)]
77pub struct Language(sys::hb_language_t);
78
79impl Language {
80    /// Exposes the raw inner pointer without transferring the ownership.
81    pub fn as_raw(&self) -> sys::hb_language_t {
82        self.0
83    }
84
85    /// Constructs a language from raw [`sys::hb_language_t`] object.
86    ///
87    /// # Safety
88    /// The given `set` pointer must either be constructed by some Harfbuzz function, or be returned from
89    /// [`Self::as_raw`].
90    pub unsafe fn from_raw(lang: sys::hb_language_t) -> Self {
91        Self(lang)
92    }
93}
94
95impl Default for Language {
96    fn default() -> Self {
97        Self(null())
98    }
99}
100
101impl FromStr for Language {
102    type Err = AllocationError;
103
104    fn from_str(s: &str) -> Result<Self, Self::Err> {
105        if s.is_empty() {
106            return Ok(Self(sys::HB_LANGUAGE_INVALID));
107        }
108        let lang =
109            unsafe { sys::hb_language_from_string(s.as_ptr() as *const c_char, s.len() as i32) };
110        if lang.is_null() {
111            return Err(AllocationError);
112        }
113        Ok(Self(lang))
114    }
115}
116
117impl fmt::Display for Language {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        let lang = unsafe { sys::hb_language_to_string(self.0) };
120        if lang.is_null() {
121            return write!(f, "[invalid]");
122        }
123        let lang = unsafe { CStr::from_ptr(lang) };
124        if let Ok(lang) = lang.to_str() {
125            write!(f, "{lang}")
126        } else {
127            write!(f, "[invalid]")
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn tag_debug_is_correct() {
138        assert_eq!(u32::from(Tag::new([b'D', b'S', b'I', b'G'])), 0x44534947u32);
139        assert_eq!(format!("{:?}", Tag::new([b'D', b'S', b'I', b'G'])), "Tag(DSIG)");
140        assert_eq!(format!("{:?}", Tag::new(b"DSIG")), "Tag(DSIG)");
141    }
142
143    #[test]
144    fn language_works() {
145        assert_eq!(Language::from_str("").unwrap().to_string(), "[invalid]");
146        assert_eq!(Language::from_str("en").unwrap().to_string(), "en");
147        assert_eq!(Language::from_str("non-existent").unwrap().to_string(), "non-existent");
148    }
149}