hayro_syntax/object/
name.rs

1//! Name objects.
2
3use crate::object::Object;
4use crate::object::macros::object;
5use crate::reader::{Readable, Reader, Skippable};
6use crate::trivia::is_regular_character;
7use crate::xref::XRef;
8use std::fmt::Debug;
9use std::hash::Hash;
10use std::ops::Deref;
11
12#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13enum Cow<'a> {
14    Borrowed(&'a [u8]),
15    Owned(Vec<u8>),
16}
17
18/// A PDF name.
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub struct Name<'a>(Cow<'a>);
21
22impl<'a> AsRef<Name<'a>> for Name<'a> {
23    fn as_ref(&self) -> &Name<'a> {
24        self
25    }
26}
27
28impl Deref for Name<'_> {
29    type Target = [u8];
30
31    fn deref(&self) -> &Self::Target {
32        match &self.0 {
33            Cow::Borrowed(a) => a,
34            Cow::Owned(v) => v.as_ref(),
35        }
36    }
37}
38
39impl<'a> Name<'a> {
40    /// Create a new name from a sequence of bytes.
41    pub fn new(data: &'a [u8]) -> Name<'a> {
42        fn convert_hex(c: u8) -> u8 {
43            match c {
44                b'A'..=b'F' => c - b'A' + 10,
45                b'a'..=b'f' => c - b'a' + 10,
46                b'0'..=b'9' => c - b'0',
47                _ => unreachable!(),
48            }
49        }
50
51        let data = if !data.iter().any(|c| *c == b'#') {
52            Cow::Borrowed(data)
53        } else {
54            let mut cleaned = vec![];
55
56            let mut r = Reader::new(data);
57
58            while let Some(b) = r.read_byte() {
59                if b == b'#' {
60                    // We already verified when skipping that it's a valid hex sequence.
61                    let hex = r.read_bytes(2).unwrap();
62                    cleaned.push(convert_hex(hex[0]) << 4 | convert_hex(hex[1]));
63                } else {
64                    cleaned.push(b);
65                }
66            }
67
68            Cow::Owned(cleaned)
69        };
70
71        Self(data)
72    }
73
74    pub(crate) const fn from_unescaped(data: &'a [u8]) -> Name<'a> {
75        Self(Cow::Borrowed(data))
76    }
77
78    /// Return a string representation of the name.
79    pub fn as_str(&self) -> &str {
80        std::str::from_utf8(&self.deref()).unwrap_or("{non-ascii key}")
81    }
82}
83
84object!(Name<'a>, Name);
85
86impl Skippable for Name<'_> {
87    fn skip<const PLAIN: bool>(r: &mut Reader<'_>) -> Option<()> {
88        skip_name_like(r, true).map(|_| ())
89    }
90}
91
92impl<'a> Readable<'a> for Name<'a> {
93    fn read<const PLAIN: bool>(r: &mut Reader<'a>, _: &'a XRef) -> Option<Self> {
94        let data = {
95            let start = r.offset();
96            skip_name_like(r, true)?;
97            let end = r.offset();
98
99            r.range(start + 1..end).unwrap()
100        };
101
102        Some(Self::new(data))
103    }
104}
105
106// This method is shared by `Name` and the parser for content stream operators (which behave like
107// names, except that they aren't preceded by a solidus.
108pub(crate) fn skip_name_like(r: &mut Reader, solidus: bool) -> Option<()> {
109    if solidus {
110        r.forward_tag(b"/")?;
111    }
112
113    while let Some(b) = r.eat(|n| is_regular_character(n)) {
114        match b {
115            b'#' => {
116                r.eat(|n| n.is_ascii_hexdigit())?;
117                r.eat(|n| n.is_ascii_hexdigit())?;
118            }
119            _ => {}
120        }
121    }
122
123    Some(())
124}
125
126#[cfg(test)]
127mod tests {
128    use crate::object::name::Name;
129    use crate::reader::Reader;
130    use std::ops::Deref;
131
132    #[test]
133    fn name_1() {
134        assert_eq!(
135            Reader::new("/".as_bytes())
136                .read_without_xref::<Name>()
137                .unwrap()
138                .deref(),
139            b""
140        );
141    }
142
143    #[test]
144    fn name_2() {
145        assert!(
146            Reader::new("dfg".as_bytes())
147                .read_without_xref::<Name>()
148                .is_none()
149        );
150    }
151
152    #[test]
153    fn name_3() {
154        assert!(
155            Reader::new("/AB#FG".as_bytes())
156                .read_without_xref::<Name>()
157                .is_none()
158        );
159    }
160
161    #[test]
162    fn name_4() {
163        assert_eq!(
164            Reader::new("/Name1".as_bytes())
165                .read_without_xref::<Name>()
166                .unwrap()
167                .deref(),
168            b"Name1"
169        );
170    }
171
172    #[test]
173    fn name_5() {
174        assert_eq!(
175            Reader::new("/ASomewhatLongerName".as_bytes())
176                .read_without_xref::<Name>()
177                .unwrap()
178                .deref(),
179            b"ASomewhatLongerName"
180        );
181    }
182
183    #[test]
184    fn name_6() {
185        assert_eq!(
186            Reader::new("/A;Name_With-Various***Characters?".as_bytes())
187                .read_without_xref::<Name>()
188                .unwrap()
189                .deref(),
190            b"A;Name_With-Various***Characters?"
191        );
192    }
193
194    #[test]
195    fn name_7() {
196        assert_eq!(
197            Reader::new("/1.2".as_bytes())
198                .read_without_xref::<Name>()
199                .unwrap()
200                .deref(),
201            b"1.2"
202        );
203    }
204
205    #[test]
206    fn name_8() {
207        assert_eq!(
208            Reader::new("/$$".as_bytes())
209                .read_without_xref::<Name>()
210                .unwrap()
211                .deref(),
212            b"$$"
213        );
214    }
215
216    #[test]
217    fn name_9() {
218        assert_eq!(
219            Reader::new("/@pattern".as_bytes())
220                .read_without_xref::<Name>()
221                .unwrap()
222                .deref(),
223            b"@pattern"
224        );
225    }
226
227    #[test]
228    fn name_10() {
229        assert_eq!(
230            Reader::new("/.notdef".as_bytes())
231                .read_without_xref::<Name>()
232                .unwrap()
233                .deref(),
234            b".notdef"
235        );
236    }
237
238    #[test]
239    fn name_11() {
240        assert_eq!(
241            Reader::new("/lime#20Green".as_bytes())
242                .read_without_xref::<Name>()
243                .unwrap()
244                .deref(),
245            b"lime Green"
246        );
247    }
248
249    #[test]
250    fn name_12() {
251        assert_eq!(
252            Reader::new("/paired#28#29parentheses".as_bytes())
253                .read_without_xref::<Name>()
254                .unwrap()
255                .deref(),
256            b"paired()parentheses"
257        );
258    }
259
260    #[test]
261    fn name_13() {
262        assert_eq!(
263            Reader::new("/The_Key_of_F#23_Minor".as_bytes())
264                .read_without_xref::<Name>()
265                .unwrap()
266                .deref(),
267            b"The_Key_of_F#_Minor"
268        );
269    }
270
271    #[test]
272    fn name_14() {
273        assert_eq!(
274            Reader::new("/A#42".as_bytes())
275                .read_without_xref::<Name>()
276                .unwrap()
277                .deref(),
278            b"AB"
279        );
280    }
281
282    #[test]
283    fn name_15() {
284        assert_eq!(
285            Reader::new("/A#3b".as_bytes())
286                .read_without_xref::<Name>()
287                .unwrap()
288                .deref(),
289            b"A;"
290        );
291    }
292
293    #[test]
294    fn name_16() {
295        assert_eq!(
296            Reader::new("/A#3B".as_bytes())
297                .read_without_xref::<Name>()
298                .unwrap()
299                .deref(),
300            b"A;"
301        );
302    }
303
304    #[test]
305    fn name_17() {
306        assert_eq!(
307            Reader::new("/k1  ".as_bytes())
308                .read_without_xref::<Name>()
309                .unwrap()
310                .deref(),
311            b"k1"
312        );
313    }
314}