hayro_syntax/content/
ops.rs

1//! Content stream operators
2
3use crate::content::{OPERANDS_THRESHOLD, Operation, OperatorTrait, Stack};
4use crate::object::Object;
5use crate::object::array::Array;
6use crate::object::name::Name;
7use crate::object::number::Number;
8use crate::object::stream::Stream;
9use crate::object::string;
10use smallvec::{SmallVec, smallvec};
11
12use crate::content::macros::{op_all, op_impl, op0, op1, op2, op3, op4, op6};
13use log::warn;
14
15include!("ops_generated.rs");
16
17// Need to special-case those because they have variable arguments.
18
19#[derive(Debug, Clone, PartialEq)]
20pub struct StrokeColorNamed<'a>(
21    pub SmallVec<[Number; OPERANDS_THRESHOLD]>,
22    pub Option<Name<'a>>,
23);
24
25fn scn_fn<'a>(
26    stack: &Stack<'a>,
27) -> Option<(SmallVec<[Number; OPERANDS_THRESHOLD]>, Option<Name<'a>>)> {
28    let mut nums = smallvec![];
29    let mut name = None;
30
31    for o in &stack.0 {
32        match o {
33            Object::Number(n) => nums.push(*n),
34            Object::Name(n) => name = Some(n.clone()),
35            _ => {
36                warn!("encountered unknown object {:?} when parsing scn/SCN", o);
37
38                return None;
39            }
40        }
41    }
42
43    Some((nums, name))
44}
45
46op_impl!(
47    StrokeColorNamed<'a>,
48    "SCN",
49    u8::MAX as usize,
50    |stack: &Stack<'a>| {
51        let res = scn_fn(stack);
52        res.map(|r| StrokeColorNamed(r.0, r.1))
53    }
54);
55
56#[derive(Debug, PartialEq, Clone)]
57pub struct NonStrokeColorNamed<'a>(
58    pub SmallVec<[Number; OPERANDS_THRESHOLD]>,
59    pub Option<Name<'a>>,
60);
61
62op_impl!(
63    NonStrokeColorNamed<'a>,
64    "scn",
65    u8::MAX as usize,
66    |stack: &Stack<'a>| {
67        let res = scn_fn(stack);
68        res.map(|r| NonStrokeColorNamed(r.0, r.1))
69    }
70);
71
72#[cfg(test)]
73mod tests {
74    use crate::content::ops::{
75        BeginMarkedContentWithProperties, ClosePath, EndMarkedContent, FillPathNonZero, LineTo,
76        MarkedContentPointWithProperties, MoveTo, NonStrokeColorDeviceRgb, NonStrokeColorNamed,
77        SetGraphicsState, StrokeColorNamed, Transform, TypedOperation,
78    };
79    use crate::content::{TypedIter, UntypedIter};
80    use crate::object::Object;
81    use crate::object::dict::Dict;
82    use crate::object::name::Name;
83    use crate::object::number::Number;
84    use crate::reader::Readable;
85    use smallvec::smallvec;
86
87    fn n(num: i32) -> Number {
88        Number::from_i32(num)
89    }
90
91    fn f(num: f32) -> Number {
92        Number::from_f32(num)
93    }
94
95    #[test]
96    fn basic_ops_1() {
97        let input = b"
981 0 0 -1 0 200 cm
99/g0 gs
1001 0 0 rg
101";
102
103        let expected = vec![
104            TypedOperation::Transform(Transform(n(1), n(0), n(0), n(-1), n(0), n(200))),
105            TypedOperation::SetGraphicsState(SetGraphicsState(Name::new(b"g0"))),
106            TypedOperation::NonStrokeColorDeviceRgb(NonStrokeColorDeviceRgb(n(1), n(0), n(0))),
107        ];
108
109        let elements = TypedIter::new(UntypedIter::new(input))
110            .into_iter()
111            .collect::<Vec<_>>();
112        assert_eq!(elements, expected,)
113    }
114
115    #[test]
116    fn basic_ops_2() {
117        let input = b"
11820 20 m
119180 20 l
120180.1 180.1 l
12120 180 l
122h
123f
124";
125
126        let expected = vec![
127            TypedOperation::MoveTo(MoveTo(n(20), n(20))),
128            TypedOperation::LineTo(LineTo(n(180), n(20))),
129            TypedOperation::LineTo(LineTo(f(180.1), f(180.1))),
130            TypedOperation::LineTo(LineTo(n(20), n(180))),
131            TypedOperation::ClosePath(ClosePath),
132            TypedOperation::FillPathNonZero(FillPathNonZero),
133        ];
134
135        let elements = TypedIter::new(UntypedIter::new(input))
136            .into_iter()
137            .collect::<Vec<_>>();
138        assert_eq!(elements, expected,)
139    }
140
141    #[test]
142    fn scn() {
143        let input = b"
1440.0 scn
1450.1 0.2 0.3 /DeviceRgb SCN
146";
147
148        let expected = vec![
149            TypedOperation::NonStrokeColorNamed(NonStrokeColorNamed(
150                smallvec![Number::from_i32(0)],
151                None,
152            )),
153            TypedOperation::StrokeColorNamed(StrokeColorNamed(
154                smallvec![
155                    Number::from_f32(0.1),
156                    Number::from_f32(0.2),
157                    Number::from_f32(0.3)
158                ],
159                Some(Name::new(b"DeviceRgb")),
160            )),
161        ];
162
163        let elements = TypedIter::new(UntypedIter::new(input))
164            .into_iter()
165            .collect::<Vec<_>>();
166
167        assert_eq!(elements, expected);
168    }
169
170    #[test]
171    fn dp() {
172        let input = b"/Attribute<</ShowCenterPoint false >> DP";
173
174        let expected = vec![TypedOperation::MarkedContentPointWithProperties(
175            MarkedContentPointWithProperties(
176                Name::new(b"Attribute"),
177                Object::Dict(Dict::from_bytes(b"<</ShowCenterPoint false >>").unwrap()),
178            ),
179        )];
180
181        let elements = TypedIter::new(UntypedIter::new(input))
182            .into_iter()
183            .collect::<Vec<_>>();
184
185        assert_eq!(elements, expected);
186    }
187
188    #[test]
189    fn bdc_with_dict() {
190        let input = b"/Span << /MCID 0 /Alt (Alt)>> BDC EMC";
191
192        let expected = vec![
193            TypedOperation::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
194                Name::new(b"Span"),
195                Object::Dict(Dict::from_bytes(b"<< /MCID 0 /Alt (Alt)>>").unwrap()),
196            )),
197            TypedOperation::EndMarkedContent(EndMarkedContent),
198        ];
199
200        let elements = TypedIter::new(UntypedIter::new(input))
201            .into_iter()
202            .collect::<Vec<_>>();
203
204        assert_eq!(elements, expected);
205    }
206
207    #[test]
208    fn bdc_with_name() {
209        let input = b"/Span /Name BDC EMC";
210
211        let expected = vec![
212            TypedOperation::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
213                Name::new(b"Span"),
214                Object::Name(Name::new(b"Name")),
215            )),
216            TypedOperation::EndMarkedContent(EndMarkedContent),
217        ];
218
219        let elements = TypedIter::new(UntypedIter::new(input))
220            .into_iter()
221            .collect::<Vec<_>>();
222
223        assert_eq!(elements, expected);
224    }
225}