1use std::fmt;
4
5pub use pdfrum_cmap::{CharCode, Cid};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
21pub struct Gid(pub u16);
22
23impl From<u16> for Gid {
24 fn from(g: u16) -> Self {
25 Self(g)
26 }
27}
28
29impl From<Gid> for u16 {
30 fn from(g: Gid) -> Self {
31 g.0
32 }
33}
34
35impl From<pdfrum_type1::Gid> for Gid {
36 fn from(g: pdfrum_type1::Gid) -> Self {
37 Self(g.0)
38 }
39}
40
41impl From<Gid> for pdfrum_type1::Gid {
42 fn from(g: Gid) -> Self {
43 Self(g.0)
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
53pub struct FontId(pub u64);
54
55#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
62pub struct GlyphName(Box<[u8]>);
63
64impl GlyphName {
65 #[must_use]
67 pub fn new(bytes: impl Into<Box<[u8]>>) -> Self {
68 Self(bytes.into())
69 }
70
71 #[must_use]
73 pub fn as_bytes(&self) -> &[u8] {
74 &self.0
75 }
76
77 #[must_use]
80 pub fn as_str(&self) -> Option<&str> {
81 std::str::from_utf8(&self.0).ok()
82 }
83}
84
85impl fmt::Debug for GlyphName {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self.as_str() {
88 Some(s) => write!(f, "GlyphName({s:?})"),
89 None => write!(f, "GlyphName({:?})", self.0),
90 }
91 }
92}
93
94impl AsRef<[u8]> for GlyphName {
95 fn as_ref(&self) -> &[u8] {
96 self.as_bytes()
97 }
98}
99
100impl From<&str> for GlyphName {
101 fn from(s: &str) -> Self {
102 Self::new(s.as_bytes().to_vec())
103 }
104}
105
106impl From<&[u8]> for GlyphName {
107 fn from(bytes: &[u8]) -> Self {
108 Self::new(bytes.to_vec())
109 }
110}
111
112impl From<Vec<u8>> for GlyphName {
113 fn from(bytes: Vec<u8>) -> Self {
114 Self::new(bytes)
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
138pub struct FontFlags(u32);
139
140impl FontFlags {
141 pub const FIXED_PITCH: Self = Self(1 << 0);
143 pub const SERIF: Self = Self(1 << 1);
145 pub const SYMBOLIC: Self = Self(1 << 2);
147 pub const SCRIPT: Self = Self(1 << 3);
149 pub const NON_SYMBOLIC: Self = Self(1 << 5);
151 pub const ITALIC: Self = Self(1 << 6);
153 pub const ALL_CAP: Self = Self(1 << 16);
155 pub const SMALL_CAP: Self = Self(1 << 17);
157 pub const FORCE_BOLD: Self = Self(1 << 18);
159 pub const USE_EXTERN_ATTR: Self = Self(1 << 19);
163
164 pub const NONE: Self = Self(0);
166
167 pub const DEFAULT: Self = Self::NON_SYMBOLIC;
169
170 #[must_use]
172 pub const fn bits(self) -> u32 {
173 self.0
174 }
175
176 #[must_use]
179 pub const fn from_bits(bits: u32) -> Self {
180 Self(bits)
181 }
182
183 #[must_use]
188 pub const fn contains(self, other: Self) -> bool {
189 self.0 & other.0 == other.0
190 }
191
192 #[must_use]
194 pub const fn union(self, other: Self) -> Self {
195 Self(self.0 | other.0)
196 }
197
198 #[must_use]
200 pub const fn with(self, other: Self) -> Self {
201 self.union(other)
202 }
203
204 #[must_use]
206 pub const fn without(self, other: Self) -> Self {
207 Self(self.0 & !other.0)
208 }
209
210 #[must_use]
212 pub const fn is_empty(self) -> bool {
213 self.0 == 0
214 }
215
216 #[must_use]
218 pub const fn is_symbolic(self) -> bool {
219 self.contains(Self::SYMBOLIC)
220 }
221
222 #[must_use]
224 pub const fn is_non_symbolic(self) -> bool {
225 self.contains(Self::NON_SYMBOLIC)
226 }
227
228 #[must_use]
230 pub const fn is_italic(self) -> bool {
231 self.contains(Self::ITALIC)
232 }
233
234 #[must_use]
236 pub const fn is_fixed_pitch(self) -> bool {
237 self.contains(Self::FIXED_PITCH)
238 }
239
240 #[must_use]
242 pub const fn uses_extern_attr(self) -> bool {
243 self.contains(Self::USE_EXTERN_ATTR)
244 }
245
246 #[must_use]
248 pub const fn is_all_cap(self) -> bool {
249 self.contains(Self::ALL_CAP)
250 }
251}
252
253impl std::ops::BitOr for FontFlags {
254 type Output = Self;
255
256 fn bitor(self, rhs: Self) -> Self {
257 self.union(rhs)
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn flag_predicates_read_the_right_bits() {
267 let f = FontFlags::SYMBOLIC | FontFlags::ITALIC;
268 assert!(f.is_symbolic());
269 assert!(f.is_italic());
270 assert!(!f.is_non_symbolic());
271 assert!(!f.uses_extern_attr());
272 assert_eq!(FontFlags::DEFAULT.bits(), 32);
273 }
274
275 #[test]
276 fn with_and_without_are_inverses() {
277 let f = FontFlags::NONE.with(FontFlags::ALL_CAP);
278 assert!(f.is_all_cap());
279 assert!(!f.without(FontFlags::ALL_CAP).is_all_cap());
280 }
281
282 #[test]
283 fn unknown_bits_round_trip() {
284 let reserved = 1 << 30;
286 let f = FontFlags::from_bits(reserved | FontFlags::SERIF.bits());
287 assert_eq!(f.bits(), reserved | FontFlags::SERIF.bits());
288 assert!(f.contains(FontFlags::SERIF));
289 assert!(!f.contains(FontFlags::ITALIC));
290 }
291
292 #[test]
293 fn symbolic_and_non_symbolic_co_occur() {
294 let f = FontFlags::SYMBOLIC | FontFlags::NON_SYMBOLIC;
297 assert!(f.is_symbolic());
298 assert!(f.is_non_symbolic());
299 assert_eq!(f.bits(), (1 << 2) | (1 << 5));
300 }
301
302 #[test]
303 fn contains_holds_for_a_subset_and_the_empty_set() {
304 let f = FontFlags::SERIF | FontFlags::ITALIC | FontFlags::ALL_CAP;
305 assert!(f.contains(FontFlags::SERIF | FontFlags::ALL_CAP));
306 assert!(f.contains(FontFlags::NONE));
307 assert!(!f.contains(FontFlags::SERIF | FontFlags::SMALL_CAP));
308 assert!(FontFlags::NONE.is_empty());
309 assert!(!f.is_empty());
310 }
311
312 #[test]
313 fn glyph_names_keep_their_bytes() {
314 let n = GlyphName::from("quotesingle");
315 assert_eq!(n.as_bytes(), b"quotesingle");
316 assert_eq!(n.as_ref(), b"quotesingle");
317 assert_eq!(n.as_str(), Some("quotesingle"));
318 assert_eq!(
319 GlyphName::from(&b"quotesingle"[..]).as_bytes(),
320 b"quotesingle"
321 );
322 let raw = GlyphName::from(vec![0xff, 0xfe]);
324 assert_eq!(raw.as_str(), None);
325 }
326
327 #[test]
328 fn gid_round_trips_through_the_type1_newtype() {
329 let g = Gid::from(42u16);
330 assert_eq!(u16::from(g), 42);
331 assert_eq!(Gid::from(pdfrum_type1::Gid::from(g)), g);
332 assert_eq!(Gid::from(pdfrum_type1::Gid::from(42u16)), Gid(42));
333 }
334}