1use lopdf::{Dictionary, Document, Object};
6
7use super::objects::{deref, num, nums};
8
9#[derive(Debug, Clone)]
10pub enum Function {
11 Sampled {
12 domain: Vec<f64>,
13 range: Vec<f64>,
14 size: Vec<usize>,
15 bps: u32,
16 encode: Vec<f64>,
17 decode: Vec<f64>,
18 samples: Vec<u8>,
19 n_out: usize,
20 },
21 Exponential {
22 domain: Vec<f64>,
23 c0: Vec<f64>,
24 c1: Vec<f64>,
25 n: f64,
26 },
27 Stitching {
28 domain: Vec<f64>,
29 functions: Vec<Function>,
30 bounds: Vec<f64>,
31 encode: Vec<f64>,
32 },
33 PostScript {
34 domain: Vec<f64>,
35 range: Vec<f64>,
36 program: Vec<PsOp>,
37 },
38 Array(Vec<Function>),
40 Identity,
43}
44
45#[derive(Debug, Clone)]
47pub enum PsOp {
48 Num(f64),
49 Op(&'static str),
50 Block(usize, usize),
52}
53
54impl Function {
55 pub fn parse(doc: &Document, obj: &Object) -> Option<Function> {
57 Self::parse_depth(doc, obj, 0)
58 }
59
60 fn parse_depth(doc: &Document, obj: &Object, depth: usize) -> Option<Function> {
61 if depth > 8 {
62 return None;
63 }
64 let obj = deref(doc, obj);
65 if let Object::Array(a) = obj {
66 let fns: Vec<Function> = a
67 .iter()
68 .filter_map(|o| Self::parse_depth(doc, o, depth + 1))
69 .collect();
70 if fns.is_empty() {
71 return None;
72 }
73 return Some(Function::Array(fns));
74 }
75 let (dict, stream) = match obj {
76 Object::Dictionary(d) => (d, None),
77 Object::Stream(s) => (&s.dict, Some(s)),
78 _ => return None,
79 };
80 let get = |k: &[u8]| dict.get(k).ok().map(|o| deref(doc, o));
81 let getnums = |k: &[u8]| get(k).and_then(|o| nums(doc, o));
82 let ftype = get(b"FunctionType").and_then(|o| o.as_i64().ok())?;
83 let domain = getnums(b"Domain").unwrap_or_else(|| vec![0.0, 1.0]);
84 match ftype {
85 0 => {
86 let s = stream?;
87 let samples = s.decompressed_content().ok()?;
88 let size: Vec<usize> = getnums(b"Size")?
89 .iter()
90 .map(|v| (*v as i64).max(1) as usize)
91 .collect();
92 let bps = get(b"BitsPerSample").and_then(|o| o.as_i64().ok())? as u32;
93 if !matches!(bps, 1 | 2 | 4 | 8 | 12 | 16 | 24 | 32) {
94 return None;
95 }
96 let range = getnums(b"Range")?;
97 let n_out = range.len() / 2;
98 let m = size.len();
99 let mut encode = getnums(b"Encode").unwrap_or_default();
100 if encode.len() < 2 * m {
101 encode = size
102 .iter()
103 .flat_map(|&s| [0.0, (s as f64 - 1.0).max(0.0)])
104 .collect();
105 }
106 let mut decode = getnums(b"Decode").unwrap_or_default();
107 if decode.len() < 2 * n_out {
108 decode = range.clone();
109 }
110 Some(Function::Sampled {
111 domain,
112 range,
113 size,
114 bps,
115 encode,
116 decode,
117 samples,
118 n_out,
119 })
120 }
121 2 => {
122 let c0 = getnums(b"C0").unwrap_or_else(|| vec![0.0]);
123 let c1 = getnums(b"C1").unwrap_or_else(|| vec![1.0]);
124 let n = get(b"N").and_then(num).unwrap_or(1.0);
125 Some(Function::Exponential { domain, c0, c1, n })
126 }
127 3 => {
128 let functions: Vec<Function> = get(b"Functions")
129 .and_then(|o| o.as_array().ok())?
130 .iter()
131 .map(|o| Self::parse_depth(doc, o, depth + 1).unwrap_or(Function::Identity))
132 .collect();
133 let bounds = getnums(b"Bounds").unwrap_or_default();
134 let encode = getnums(b"Encode").unwrap_or_default();
135 Some(Function::Stitching {
136 domain,
137 functions,
138 bounds,
139 encode,
140 })
141 }
142 4 => {
143 let s = stream?;
144 let src = s.decompressed_content().ok()?;
145 let program = parse_postscript(&src)?;
146 let range = getnums(b"Range").unwrap_or_default();
147 Some(Function::PostScript {
148 domain,
149 range,
150 program,
151 })
152 }
153 _ => None,
154 }
155 }
156
157 pub fn eval(&self, inputs: &[f64]) -> Vec<f64> {
160 match self {
161 Function::Identity => inputs.to_vec(),
162 Function::Array(fns) => fns
163 .iter()
164 .flat_map(|f| f.eval(inputs).into_iter().take(1))
165 .collect(),
166 Function::Exponential { domain, c0, c1, n } => {
167 let x = clamp_domain(inputs.first().copied().unwrap_or(0.0), domain, 0);
168 let t = if *n == 1.0 {
169 x
170 } else {
171 x.abs().powf(*n) * x.signum()
172 };
173 c0.iter()
174 .zip(c1.iter().chain(std::iter::repeat(&1.0)))
175 .map(|(a, b)| a + t * (b - a))
176 .collect()
177 }
178 Function::Stitching {
179 domain,
180 functions,
181 bounds,
182 encode,
183 } => {
184 let x = clamp_domain(inputs.first().copied().unwrap_or(0.0), domain, 0);
185 let k = functions.len();
186 if k == 0 {
187 return vec![0.0];
188 }
189 let mut i = 0;
190 while i < bounds.len() && i + 1 < k && x >= bounds[i] {
191 i += 1;
192 }
193 let lo = if i == 0 { domain[0] } else { bounds[i - 1] };
194 let hi = if i >= bounds.len() {
195 domain.get(1).copied().unwrap_or(1.0)
196 } else {
197 bounds[i]
198 };
199 let e0 = encode.get(2 * i).copied().unwrap_or(0.0);
200 let e1 = encode.get(2 * i + 1).copied().unwrap_or(1.0);
201 let t = if hi > lo {
202 e0 + (x - lo) / (hi - lo) * (e1 - e0)
203 } else {
204 e0
205 };
206 functions[i].eval(&[t])
207 }
208 Function::Sampled {
209 domain,
210 range,
211 size,
212 bps,
213 encode,
214 decode,
215 samples,
216 n_out,
217 } => eval_sampled(
218 inputs, domain, range, size, *bps, encode, decode, samples, *n_out,
219 ),
220 Function::PostScript {
221 domain,
222 range,
223 program,
224 } => {
225 let mut stack: Vec<f64> = inputs
226 .iter()
227 .enumerate()
228 .map(|(i, &v)| clamp_domain(v, domain, i))
229 .collect();
230 exec_postscript(program, 0, program.len(), &mut stack, 0);
231 let n_out = range.len() / 2;
232 if n_out > 0 {
233 let start = stack.len().saturating_sub(n_out);
234 let mut out: Vec<f64> = stack[start..].to_vec();
235 while out.len() < n_out {
236 out.insert(0, 0.0);
237 }
238 for (i, v) in out.iter_mut().enumerate() {
239 *v = v.clamp(
240 range[2 * i].min(range[2 * i + 1]),
241 range[2 * i].max(range[2 * i + 1]),
242 );
243 }
244 out
245 } else {
246 stack
247 }
248 }
249 }
250 }
251}
252
253fn clamp_domain(x: f64, domain: &[f64], i: usize) -> f64 {
254 match (domain.get(2 * i), domain.get(2 * i + 1)) {
255 (Some(&lo), Some(&hi)) if hi >= lo => x.clamp(lo, hi),
256 _ => x,
257 }
258}
259
260#[allow(clippy::too_many_arguments)]
261fn eval_sampled(
262 inputs: &[f64],
263 domain: &[f64],
264 range: &[f64],
265 size: &[usize],
266 bps: u32,
267 encode: &[f64],
268 decode: &[f64],
269 samples: &[u8],
270 n_out: usize,
271) -> Vec<f64> {
272 let m = size.len();
273 if m == 0 || n_out == 0 {
274 return vec![0.0; n_out.max(1)];
275 }
276 let max = ((1u64 << bps) - 1) as f64;
277 let sample_at = |idx: usize, j: usize| -> f64 {
278 let bit = (idx * n_out + j) as u64 * u64::from(bps);
279 let byte = (bit / 8) as usize;
280 let v: u64 = match bps {
281 8 => u64::from(*samples.get(byte).unwrap_or(&0)),
282 16 => {
283 let b = |k: usize| u64::from(*samples.get(byte + k).unwrap_or(&0));
284 (b(0) << 8) | b(1)
285 }
286 24 => {
287 let b = |k: usize| u64::from(*samples.get(byte + k).unwrap_or(&0));
288 (b(0) << 16) | (b(1) << 8) | b(2)
289 }
290 32 => {
291 let b = |k: usize| u64::from(*samples.get(byte + k).unwrap_or(&0));
292 (b(0) << 24) | (b(1) << 16) | (b(2) << 8) | b(3)
293 }
294 _ => {
295 let mut v = 0u64;
297 for k in 0..bps as u64 {
298 let p = bit + k;
299 let byte = *samples.get((p / 8) as usize).unwrap_or(&0);
300 v = (v << 1) | u64::from((byte >> (7 - p % 8)) & 1);
301 }
302 v
303 }
304 };
305 v as f64 / max
306 };
307 let mut idx0 = 0usize;
311 let mut stride = 1usize;
312 let mut frac0 = 0.0;
313 let mut stride0 = 1usize;
314 for i in 0..m {
315 let x = clamp_domain(inputs.get(i).copied().unwrap_or(0.0), domain, i);
316 let (d0, d1) = (domain[2 * i], domain[2 * i + 1]);
317 let (e0, e1) = (encode[2 * i], encode[2 * i + 1]);
318 let e = if d1 > d0 {
319 e0 + (x - d0) * (e1 - e0) / (d1 - d0)
320 } else {
321 e0
322 };
323 let e = e.clamp(0.0, (size[i] as f64 - 1.0).max(0.0));
324 let fl = e.floor() as usize;
325 if i == 0 {
326 frac0 = e - fl as f64;
327 stride0 = stride;
328 }
329 idx0 += fl.min(size[i] - 1) * stride;
330 stride *= size[i];
331 }
332 let mut out = Vec::with_capacity(n_out);
333 for j in 0..n_out {
334 let s0 = sample_at(idx0, j);
335 let s = if frac0 > 0.0 && (idx0 / stride0) % size[0] + 1 < size[0] {
336 let s1 = sample_at(idx0 + stride0, j);
337 s0 + (s1 - s0) * frac0
338 } else {
339 s0
340 };
341 let (dmin, dmax) = (decode[2 * j], decode[2 * j + 1]);
342 let mut v = dmin + s * (dmax - dmin);
343 if let (Some(&r0), Some(&r1)) = (range.get(2 * j), range.get(2 * j + 1)) {
344 v = v.clamp(r0.min(r1), r0.max(r1));
345 }
346 out.push(v);
347 }
348 out
349}
350
351fn parse_postscript(src: &[u8]) -> Option<Vec<PsOp>> {
354 let text = String::from_utf8_lossy(src);
355 let mut toks: Vec<String> = Vec::new();
356 let mut cur = String::new();
357 for ch in text.chars() {
358 match ch {
359 '{' | '}' => {
360 if !cur.is_empty() {
361 toks.push(std::mem::take(&mut cur));
362 }
363 toks.push(ch.to_string());
364 }
365 c if c.is_whitespace() => {
366 if !cur.is_empty() {
367 toks.push(std::mem::take(&mut cur));
368 }
369 }
370 c => cur.push(c),
371 }
372 }
373 if !cur.is_empty() {
374 toks.push(cur);
375 }
376 let mut out = Vec::new();
381 let mut pos = 0usize;
382 if toks.first().map(String::as_str) == Some("{") {
384 pos = 1;
385 }
386 parse_ps_seq(&toks, &mut pos, &mut out, 0)?;
387 Some(out)
388}
389
390fn parse_ps_seq(toks: &[String], pos: &mut usize, out: &mut Vec<PsOp>, depth: usize) -> Option<()> {
391 if depth > 64 {
392 return None;
393 }
394 while *pos < toks.len() {
395 let t = toks[*pos].as_str();
396 *pos += 1;
397 match t {
398 "}" => return Some(()),
399 "{" => {
400 let marker = out.len();
401 out.push(PsOp::Block(0, 0));
402 parse_ps_seq(toks, pos, out, depth + 1)?;
403 let end = out.len();
404 out[marker] = PsOp::Block(marker + 1, end);
405 }
406 _ => {
407 if let Ok(v) = t.parse::<f64>() {
408 out.push(PsOp::Num(v));
409 } else {
410 out.push(PsOp::Op(ps_operator(t)?));
411 }
412 }
413 }
414 }
415 Some(())
416}
417
418fn ps_operator(t: &str) -> Option<&'static str> {
419 const OPS: &[&str] = &[
420 "abs", "add", "atan", "ceiling", "cos", "cvi", "cvr", "div", "exp", "floor", "idiv", "ln",
421 "log", "mod", "mul", "neg", "round", "sin", "sqrt", "sub", "truncate", "and", "bitshift",
422 "eq", "false", "ge", "gt", "le", "lt", "ne", "not", "or", "true", "xor", "if", "ifelse",
423 "copy", "dup", "exch", "index", "pop", "roll",
424 ];
425 OPS.iter().copied().find(|o| *o == t)
426}
427
428fn exec_postscript(program: &[PsOp], start: usize, end: usize, stack: &mut Vec<f64>, depth: usize) {
430 if depth > 64 {
431 return;
432 }
433 let mut i = start;
434 let mut blocks: Vec<(usize, usize)> = Vec::new();
436 let pop = |s: &mut Vec<f64>| s.pop().unwrap_or(0.0);
437 while i < end {
438 if stack.len() > 1000 {
439 return;
440 }
441 match &program[i] {
442 PsOp::Num(v) => stack.push(*v),
443 PsOp::Block(s, e) => {
444 blocks.push((*s, *e));
445 i = *e;
446 continue;
447 }
448 PsOp::Op(op) => match *op {
449 "if" => {
450 let cond = pop(stack) != 0.0;
451 if let Some((s, e)) = blocks.pop() {
452 if cond {
453 exec_postscript(program, s, e, stack, depth + 1);
454 }
455 }
456 blocks.clear();
457 }
458 "ifelse" => {
459 let cond = pop(stack) != 0.0;
460 let b2 = blocks.pop();
461 let b1 = blocks.pop();
462 if let (Some((s1, e1)), Some((s2, e2))) = (b1, b2) {
463 if cond {
464 exec_postscript(program, s1, e1, stack, depth + 1);
465 } else {
466 exec_postscript(program, s2, e2, stack, depth + 1);
467 }
468 }
469 blocks.clear();
470 }
471 "abs" => {
472 let a = pop(stack);
473 stack.push(a.abs());
474 }
475 "add" => {
476 let b = pop(stack);
477 let a = pop(stack);
478 stack.push(a + b);
479 }
480 "sub" => {
481 let b = pop(stack);
482 let a = pop(stack);
483 stack.push(a - b);
484 }
485 "mul" => {
486 let b = pop(stack);
487 let a = pop(stack);
488 stack.push(a * b);
489 }
490 "div" => {
491 let b = pop(stack);
492 let a = pop(stack);
493 stack.push(if b != 0.0 { a / b } else { 0.0 });
494 }
495 "idiv" => {
496 let b = pop(stack) as i64;
497 let a = pop(stack) as i64;
498 stack.push(if b != 0 { (a / b) as f64 } else { 0.0 });
499 }
500 "mod" => {
501 let b = pop(stack) as i64;
502 let a = pop(stack) as i64;
503 stack.push(if b != 0 { (a % b) as f64 } else { 0.0 });
504 }
505 "neg" => {
506 let a = pop(stack);
507 stack.push(-a);
508 }
509 "atan" => {
510 let den = pop(stack);
511 let numr = pop(stack);
512 let mut deg = numr.atan2(den).to_degrees();
513 if deg < 0.0 {
514 deg += 360.0;
515 }
516 stack.push(deg);
517 }
518 "ceiling" => {
519 let a = pop(stack);
520 stack.push(a.ceil());
521 }
522 "floor" => {
523 let a = pop(stack);
524 stack.push(a.floor());
525 }
526 "round" => {
527 let a = pop(stack);
528 stack.push(a.round());
529 }
530 "truncate" => {
531 let a = pop(stack);
532 stack.push(a.trunc());
533 }
534 "cos" => {
535 let a = pop(stack);
536 stack.push(a.to_radians().cos());
537 }
538 "sin" => {
539 let a = pop(stack);
540 stack.push(a.to_radians().sin());
541 }
542 "sqrt" => {
543 let a = pop(stack);
544 stack.push(a.max(0.0).sqrt());
545 }
546 "exp" => {
547 let b = pop(stack);
548 let a = pop(stack);
549 stack.push(a.powf(b));
550 }
551 "ln" => {
552 let a = pop(stack);
553 stack.push(if a > 0.0 { a.ln() } else { 0.0 });
554 }
555 "log" => {
556 let a = pop(stack);
557 stack.push(if a > 0.0 { a.log10() } else { 0.0 });
558 }
559 "cvi" => {
560 let a = pop(stack);
561 stack.push(a.trunc());
562 }
563 "cvr" => {}
564 "dup" => {
565 let a = stack.last().copied().unwrap_or(0.0);
566 stack.push(a);
567 }
568 "pop" => {
569 stack.pop();
570 }
571 "exch" => {
572 let b = pop(stack);
573 let a = pop(stack);
574 stack.push(b);
575 stack.push(a);
576 }
577 "copy" => {
578 let n = pop(stack).max(0.0) as usize;
579 let len = stack.len();
580 if n <= len {
581 for k in 0..n {
582 stack.push(stack[len - n + k]);
583 }
584 }
585 }
586 "index" => {
587 let n = pop(stack).max(0.0) as usize;
588 let len = stack.len();
589 let v = if n < len { stack[len - 1 - n] } else { 0.0 };
590 stack.push(v);
591 }
592 "roll" => {
593 let j = pop(stack) as i64;
594 let n = pop(stack).max(0.0) as usize;
595 let len = stack.len();
596 if n > 0 && n <= len {
597 let s = &mut stack[len - n..];
598 let j = j.rem_euclid(n as i64) as usize;
599 s.rotate_right(j);
600 }
601 }
602 "eq" | "ne" | "gt" | "ge" | "lt" | "le" => {
603 let b = pop(stack);
604 let a = pop(stack);
605 let r = match *op {
606 "eq" => a == b,
607 "ne" => a != b,
608 "gt" => a > b,
609 "ge" => a >= b,
610 "lt" => a < b,
611 _ => a <= b,
612 };
613 stack.push(if r { 1.0 } else { 0.0 });
614 }
615 "and" | "or" | "xor" => {
616 let b = pop(stack) as i64;
617 let a = pop(stack) as i64;
618 let r = match *op {
619 "and" => a & b,
620 "or" => a | b,
621 _ => a ^ b,
622 };
623 stack.push(r as f64);
624 }
625 "not" => {
626 let a = pop(stack);
627 stack.push(if a == 0.0 {
629 1.0
630 } else if a == 1.0 {
631 0.0
632 } else {
633 !(a as i64) as f64
634 });
635 }
636 "bitshift" => {
637 let s = pop(stack) as i64;
638 let a = pop(stack) as i64;
639 stack.push(if s >= 0 {
640 a.checked_shl(s.min(63) as u32).unwrap_or(0) as f64
641 } else {
642 (a >> (-s).min(63)) as f64
643 });
644 }
645 "true" => stack.push(1.0),
646 "false" => stack.push(0.0),
647 _ => {}
648 },
649 }
650 i += 1;
651 }
652}
653
654pub fn eval_n(f: &Function, t: f64, n: usize) -> Vec<f64> {
657 let mut v = f.eval(&[t]);
658 v.resize(n, 0.0);
659 v
660}
661
662pub fn function_of(doc: &Document, dict: &Dictionary, key: &[u8]) -> Option<Function> {
664 dict.get(key).ok().and_then(|o| Function::parse(doc, o))
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670
671 fn ps(src: &str, inputs: &[f64], range: &[f64]) -> Vec<f64> {
672 let program = parse_postscript(src.as_bytes()).unwrap();
673 let f = Function::PostScript {
674 domain: vec![0.0, 1.0, 0.0, 1.0],
675 range: range.to_vec(),
676 program,
677 };
678 f.eval(inputs)
679 }
680
681 #[test]
682 fn postscript_calculator() {
683 assert_eq!(ps("{ add 2 div }", &[0.2, 0.6], &[0.0, 1.0]), vec![0.4]);
684 assert_eq!(
685 ps(
686 "{ dup 0.5 gt { pop 1 } { pop 0 } ifelse }",
687 &[0.7],
688 &[0.0, 1.0]
689 ),
690 vec![1.0]
691 );
692 assert_eq!(
693 ps(
694 "{ dup 0.5 gt { pop 1 } { pop 0 } ifelse }",
695 &[0.2],
696 &[0.0, 1.0]
697 ),
698 vec![0.0]
699 );
700 assert_eq!(ps("{ 1 exch sub }", &[0.25], &[0.0, 1.0]), vec![0.75]);
701 assert_eq!(
702 ps(
703 "{ 3 1 roll }",
704 &[0.1, 0.2, 0.3],
705 &[0.0, 1.0, 0.0, 1.0, 0.0, 1.0]
706 ),
707 vec![0.3, 0.1, 0.2]
708 );
709 }
710
711 #[test]
712 fn exponential_and_stitching() {
713 let f = Function::Exponential {
714 domain: vec![0.0, 1.0],
715 c0: vec![0.0, 0.0, 1.0],
716 c1: vec![1.0, 0.0, 0.0],
717 n: 1.0,
718 };
719 assert_eq!(f.eval(&[0.5]), vec![0.5, 0.0, 0.5]);
720 let st = Function::Stitching {
721 domain: vec![0.0, 1.0],
722 functions: vec![
723 Function::Exponential {
724 domain: vec![0.0, 1.0],
725 c0: vec![0.0],
726 c1: vec![1.0],
727 n: 1.0,
728 },
729 Function::Exponential {
730 domain: vec![0.0, 1.0],
731 c0: vec![1.0],
732 c1: vec![0.0],
733 n: 1.0,
734 },
735 ],
736 bounds: vec![0.5],
737 encode: vec![0.0, 1.0, 0.0, 1.0],
738 };
739 assert!((st.eval(&[0.25])[0] - 0.5).abs() < 1e-9);
740 assert!((st.eval(&[0.75])[0] - 0.5).abs() < 1e-9);
741 assert!((st.eval(&[0.5])[0] - 1.0).abs() < 1e-9);
742 }
743
744 #[test]
745 fn sampled_interpolates() {
746 let f = Function::Sampled {
747 domain: vec![0.0, 1.0],
748 range: vec![0.0, 1.0],
749 size: vec![3],
750 bps: 8,
751 encode: vec![0.0, 2.0],
752 decode: vec![0.0, 1.0],
753 samples: vec![0, 255, 0],
754 n_out: 1,
755 };
756 assert!((f.eval(&[0.25])[0] - 0.5).abs() < 0.01);
757 assert!((f.eval(&[0.5])[0] - 1.0).abs() < 0.01);
758 }
759}