docvortex_core/
form_events.rs1pub type FormEvent = (u8, Vec<(usize, usize)>);
5pub type FormProgramEvent = (u8, Vec<f64>, Option<(usize, usize)>);
7
8fn white(b: u8) -> bool {
10 matches!(b, 0 | 9 | 10 | 12 | 13 | 32)
11}
12fn delimiter(b: u8) -> bool {
14 white(b) || b"()<>[]{}/%".contains(&b)
15}
16
17fn skip_space(data: &[u8], cursor: &mut usize) {
19 while *cursor < data.len() {
20 if white(data[*cursor]) {
21 *cursor += 1;
22 } else if data[*cursor] == b'%' {
23 while *cursor < data.len() && !matches!(data[*cursor], 10 | 13) {
24 *cursor += 1;
25 }
26 } else {
27 break;
28 }
29 }
30}
31
32fn object_token(data: &[u8], cursor: &mut usize, depth: usize) -> Option<(u8, usize, usize)> {
34 let value = token(data, cursor, depth)?;
35 if value.0 == 1 && data[value.1..value.2].iter().all(u8::is_ascii_digit) {
36 let mut probe = *cursor;
37 if let Some((1, a, b)) = token(data, &mut probe, depth) {
38 if data[a..b].iter().all(u8::is_ascii_digit) {
39 if let Some((3, start, end)) = token(data, &mut probe, depth) {
40 if &data[start..end] == b"R" {
41 return None;
42 }
43 }
44 }
45 }
46 }
47 Some(value)
48}
49
50fn token(data: &[u8], cursor: &mut usize, depth: usize) -> Option<(u8, usize, usize)> {
52 if depth > 64 {
53 return None;
54 }
55 while *cursor < data.len() {
56 if white(data[*cursor]) {
57 *cursor += 1;
58 } else if data[*cursor] == b'%' {
59 while *cursor < data.len() && !matches!(data[*cursor], 10 | 13) {
60 *cursor += 1;
61 }
62 } else {
63 break;
64 }
65 }
66 let start = *cursor;
67 let first = *data.get(start)?;
68 *cursor += 1;
69 let kind = match first {
70 b'(' => {
71 let mut nesting = 1;
72 while nesting > 0 {
73 let b = *data.get(*cursor)?;
74 *cursor += 1;
75 match b {
76 b'\\' => {
77 data.get(*cursor)?;
78 *cursor += 1;
79 }
80 b'(' => nesting += 1,
81 b')' => nesting -= 1,
82 _ => {}
83 }
84 }
85 0
86 }
87 b'[' => {
88 loop {
89 let (kind, _, _) = object_token(data, cursor, depth + 1)?;
90 if kind == 4 {
91 break;
92 }
93 if kind == 3 {
94 return None;
95 }
96 }
97 0
98 }
99 b']' => 4,
100 b'<' => {
101 if data.get(*cursor) == Some(&b'<') {
102 *cursor += 1;
103 loop {
104 skip_space(data, cursor);
105 if data.get(*cursor..*cursor + 2) == Some(b">>") {
106 *cursor += 2;
107 break;
108 }
109 let (kind, _, _) = token(data, cursor, depth + 1)?;
110 if kind != 2 {
111 return None;
112 }
113 let (kind, _, _) = object_token(data, cursor, depth + 1)?;
114 if kind >= 3 {
115 return None;
116 }
117 }
118 return Some((0, start, *cursor));
119 }
120 loop {
121 let b = *data.get(*cursor)?;
122 *cursor += 1;
123 if b == b'>' {
124 break;
125 }
126 if !white(b) && !b.is_ascii_hexdigit() {
127 return None;
128 }
129 }
130 0
131 }
132 b'/' => {
133 while *cursor < data.len() && !delimiter(data[*cursor]) {
134 *cursor += 1;
135 }
136 let mut index = start + 1;
138 while index < *cursor {
139 if data[index] == b'#' {
140 if index + 2 >= *cursor
141 || !data[index + 1].is_ascii_hexdigit()
142 || !data[index + 2].is_ascii_hexdigit()
143 {
144 return None;
145 }
146 index += 3;
147 } else {
148 index += 1;
149 }
150 }
151 2
152 }
153 b'>' | b')' | b'{' | b'}' => return None,
154 _ => {
155 while *cursor < data.len() && !delimiter(data[*cursor]) {
156 *cursor += 1;
157 }
158 let word = &data[start..*cursor];
159 if [b"true".as_slice(), b"false", b"null"].contains(&word) {
160 return Some((0, start, *cursor));
161 }
162 if first.is_ascii_digit() || matches!(first, b'+' | b'-' | b'.') {
163 if word.len() >= 48
165 || !word
166 .iter()
167 .all(|b| b.is_ascii_digit() || matches!(b, b'+' | b'-' | b'.'))
168 || std::str::from_utf8(word)
169 .ok()?
170 .parse::<f64>()
171 .ok()
172 .is_none_or(|v| !v.is_finite())
173 {
174 return None;
175 }
176 1
177 } else if first.is_ascii_alphabetic() || matches!(first, b'\'' | b'"') {
178 3
179 } else {
180 return None;
181 }
182 }
183 };
184 Some((kind, start, *cursor))
185}
186
187pub fn form_state_events(data: &[u8]) -> Option<Vec<FormEvent>> {
189 let mut cursor = 0;
190 let mut operands = Vec::new();
191 let mut events = Vec::new();
192 while cursor < data.len() {
193 if white(data[cursor]) {
195 cursor += 1;
196 continue;
197 }
198 if data[cursor] == b'%' {
199 while cursor < data.len() && !matches!(data[cursor], 10 | 13) {
200 cursor += 1;
201 }
202 continue;
203 }
204 let (kind, start, end) = token(data, &mut cursor, 0)?;
205 if kind == 4 {
206 return None;
207 }
208 if kind != 3 {
209 operands.push((kind, start, end));
210 continue;
211 }
212 let op = &data[start..end];
213 if op == b"BI" {
214 return None;
215 }
216 let event = match op {
217 b"q" => Some(0),
218 b"Q" => Some(1),
219 b"cm" if operands.len() == 6 && operands.iter().all(|o| o.0 == 1) => Some(2),
220 b"Do" if operands.len() == 1 && operands[0].0 == 2 => Some(3),
221 b"cm" | b"Do" => return None,
222 _ => None,
223 };
224 if let Some(kind) = event {
225 events.push((
226 kind,
227 if kind < 2 {
228 vec![]
229 } else {
230 operands.iter().map(|o| (o.1, o.2)).collect()
231 },
232 ));
233 }
234 operands.clear();
235 }
236 Some(events)
237}
238
239pub fn form_state_program(data: &[u8]) -> Option<Vec<FormProgramEvent>> {
241 form_state_events(data)?
242 .into_iter()
243 .map(|(kind, intervals)| {
244 if kind == 2 {
245 let values = intervals
246 .into_iter()
247 .map(|(a, b)| {
248 let word = &data[a..b];
249 let value = std::str::from_utf8(word).ok()?.parse::<f64>().ok()?;
250 Some(if value == 0.0 && !word.contains(&b'.') {
251 0.0
252 } else {
253 value
254 })
255 })
256 .collect::<Option<Vec<_>>>()?;
257 Some((kind, values, None))
258 } else {
259 Some((kind, Vec::new(), intervals.first().copied()))
260 }
261 })
262 .collect()
263}