Skip to main content

hayro_postscript/
name.rs

1use alloc::vec::Vec;
2
3use crate::error::{Error, Result};
4use crate::reader::{Reader, is_regular};
5use crate::string::ascii_hex::decode_hex_digit;
6
7/// A PostScript name object.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct Name<'a> {
10    data: &'a [u8],
11    literal: bool,
12}
13
14impl<'a> Name<'a> {
15    pub(crate) fn new(data: &'a [u8], literal: bool) -> Self {
16        Self { data, literal }
17    }
18
19    /// Returns `true` if this is a literal name.
20    pub fn is_literal(&self) -> bool {
21        self.literal
22    }
23
24    /// Returns the name as a string if it is valid UTF-8, or
25    /// `None` if it contains non-ASCII bytes.
26    pub fn as_str(&self) -> Option<&'a str> {
27        core::str::from_utf8(self.data).ok()
28    }
29
30    /// Decode the name into `out`, replacing any previous contents.
31    pub fn decode_into(&self, out: &mut Vec<u8>) -> Result<()> {
32        out.clear();
33
34        // Fast path: no escape sequences.
35        if !self.data.contains(&b'#') {
36            out.extend_from_slice(self.data);
37            return Ok(());
38        }
39
40        // Slow path: Process escape sequences.
41        let mut inner = Reader::new(self.data);
42
43        while let Some(b) = inner.read_byte() {
44            if b == b'#' {
45                let hex = inner.read_bytes(2).ok_or(Error::SyntaxError)?;
46                let hi = decode_hex_digit(hex[0]).ok_or(Error::SyntaxError)?;
47                let lo = decode_hex_digit(hex[1]).ok_or(Error::SyntaxError)?;
48                out.push(hi << 4 | lo);
49            } else {
50                out.push(b);
51            }
52        }
53
54        Ok(())
55    }
56
57    /// Decode the name.
58    pub fn decode(&self) -> Result<Vec<u8>> {
59        let mut out = Vec::new();
60        self.decode_into(&mut out)?;
61        Ok(out)
62    }
63}
64
65pub(crate) fn parse_literal<'a>(r: &mut Reader<'a>) -> Option<&'a [u8]> {
66    r.forward_tag(b"/")?;
67    let start = r.offset();
68    while r.eat(is_regular).is_some() {}
69    r.range(start..r.offset())
70}
71
72pub(crate) fn parse_executable<'a>(r: &mut Reader<'a>) -> Option<&'a [u8]> {
73    let start = r.offset();
74    r.forward_while(is_regular);
75    if r.offset() == start {
76        return None;
77    }
78    r.range(start..r.offset())
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    fn read_literal(input: &[u8]) -> Option<Name<'_>> {
86        let mut r = Reader::new(input);
87        parse_literal(&mut r).map(|d| Name::new(d, true))
88    }
89
90    fn read_executable(input: &[u8]) -> Option<Name<'_>> {
91        let mut r = Reader::new(input);
92        parse_executable(&mut r).map(|d| Name::new(d, false))
93    }
94
95    #[test]
96    fn literal_simple() {
97        let n = read_literal(b"/Name1").unwrap();
98        assert_eq!(n.as_str().unwrap(), "Name1");
99        assert!(n.is_literal());
100    }
101
102    #[test]
103    fn literal_empty_name() {
104        let n = read_literal(b"/").unwrap();
105        assert_eq!(n.as_str().unwrap(), "");
106        assert!(n.is_literal());
107    }
108
109    #[test]
110    fn literal_with_hex_escape() {
111        let n = read_literal(b"/lime#20Green").unwrap();
112        assert_eq!(n.as_str().unwrap(), "lime#20Green");
113        assert_eq!(n.decode().unwrap(), b"lime Green");
114    }
115
116    #[test]
117    fn literal_multiple_hex_escapes() {
118        let n = read_literal(b"/paired#28#29parentheses").unwrap();
119        assert_eq!(n.decode().unwrap(), b"paired()parentheses");
120    }
121
122    #[test]
123    fn literal_special_chars() {
124        let n = read_literal(b"/A;Name_With-Various***Characters?").unwrap();
125        assert_eq!(n.as_str().unwrap(), "A;Name_With-Various***Characters?");
126    }
127
128    #[test]
129    fn literal_stops_at_delimiter() {
130        let mut r = Reader::new(b"/Name(rest");
131        let data = parse_literal(&mut r).unwrap();
132        assert_eq!(data, b"Name");
133        assert_eq!(r.peek_byte(), Some(b'('));
134    }
135
136    #[test]
137    fn literal_stops_at_whitespace() {
138        let mut r = Reader::new(b"/Name rest");
139        let data = parse_literal(&mut r).unwrap();
140        assert_eq!(data, b"Name");
141        assert_eq!(r.peek_byte(), Some(b' '));
142    }
143
144    #[test]
145    fn literal_not_a_name() {
146        assert!(read_literal(b"Name").is_none());
147    }
148
149    #[test]
150    fn executable_simple() {
151        let n = read_executable(b"beginbfchar ").unwrap();
152        assert_eq!(n.as_str().unwrap(), "beginbfchar");
153        assert!(!n.is_literal());
154    }
155
156    #[test]
157    fn executable_stops_at_delimiter() {
158        let mut r = Reader::new(b"def/name");
159        let data = parse_executable(&mut r).unwrap();
160        assert_eq!(data, b"def");
161        assert_eq!(r.peek_byte(), Some(b'/'));
162    }
163
164    #[test]
165    fn executable_at_eof() {
166        let n = read_executable(b"endcmap").unwrap();
167        assert_eq!(n.as_str().unwrap(), "endcmap");
168    }
169
170    #[test]
171    fn executable_empty() {
172        assert!(read_executable(b"").is_none());
173    }
174
175    #[test]
176    fn executable_starts_at_delimiter() {
177        assert!(read_executable(b"(foo)").is_none());
178    }
179
180    #[test]
181    fn decode_no_escapes() {
182        let n = Name::new(b"simple", true);
183        assert_eq!(n.decode().unwrap(), b"simple");
184    }
185
186    #[test]
187    fn decode_with_escapes() {
188        let n = Name::new(b"lime#20Green", true);
189        assert_eq!(n.decode().unwrap(), b"lime Green");
190    }
191}