hayro_syntax/content/
ops.rs

1//! Content stream operators.
2
3use crate::content::{Instruction, OPERANDS_THRESHOLD, OperatorTrait, Stack};
4use crate::object;
5use crate::object::Array;
6use crate::object::Name;
7use crate::object::Number;
8use crate::object::Object;
9use crate::object::Stream;
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 {o:?} when parsing scn/SCN");
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::TypedIter;
75    use crate::content::ops::{
76        BeginMarkedContentWithProperties, ClosePath, EndMarkedContent, FillPathNonZero, LineTo,
77        MarkedContentPointWithProperties, MoveTo, NonStrokeColorDeviceRgb, NonStrokeColorNamed,
78        SetGraphicsState, StrokeColorNamed, Transform, TypedInstruction,
79    };
80    use crate::object::Dict;
81    use crate::object::Name;
82    use crate::object::Number;
83    use crate::object::Object;
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            TypedInstruction::Transform(Transform(n(1), n(0), n(0), n(-1), n(0), n(200))),
105            TypedInstruction::SetGraphicsState(SetGraphicsState(Name::new(b"g0"))),
106            TypedInstruction::NonStrokeColorDeviceRgb(NonStrokeColorDeviceRgb(n(1), n(0), n(0))),
107        ];
108
109        let elements = TypedIter::new(input).collect::<Vec<_>>();
110        assert_eq!(elements, expected,)
111    }
112
113    #[test]
114    fn basic_ops_2() {
115        let input = b"
11620 20 m
117180 20 l
118180.1 180.1 l
11920 180 l
120h
121f
122";
123
124        let expected = vec![
125            TypedInstruction::MoveTo(MoveTo(n(20), n(20))),
126            TypedInstruction::LineTo(LineTo(n(180), n(20))),
127            TypedInstruction::LineTo(LineTo(f(180.1), f(180.1))),
128            TypedInstruction::LineTo(LineTo(n(20), n(180))),
129            TypedInstruction::ClosePath(ClosePath),
130            TypedInstruction::FillPathNonZero(FillPathNonZero),
131        ];
132
133        let elements = TypedIter::new(input).collect::<Vec<_>>();
134        assert_eq!(elements, expected,)
135    }
136
137    #[test]
138    fn scn() {
139        let input = b"
1400.0 scn
1410.1 0.2 0.3 /DeviceRgb SCN
142";
143
144        let expected = vec![
145            TypedInstruction::NonStrokeColorNamed(NonStrokeColorNamed(
146                smallvec![Number::from_i32(0)],
147                None,
148            )),
149            TypedInstruction::StrokeColorNamed(StrokeColorNamed(
150                smallvec![
151                    Number::from_f32(0.1),
152                    Number::from_f32(0.2),
153                    Number::from_f32(0.3)
154                ],
155                Some(Name::new(b"DeviceRgb")),
156            )),
157        ];
158
159        let elements = TypedIter::new(input).collect::<Vec<_>>();
160
161        assert_eq!(elements, expected);
162    }
163
164    #[test]
165    fn dp() {
166        let input = b"/Attribute<</ShowCenterPoint false >> DP";
167
168        let expected = vec![TypedInstruction::MarkedContentPointWithProperties(
169            MarkedContentPointWithProperties(
170                Name::new(b"Attribute"),
171                Object::Dict(Dict::from_bytes(b"<</ShowCenterPoint false >>").unwrap()),
172            ),
173        )];
174
175        let elements = TypedIter::new(input).collect::<Vec<_>>();
176
177        assert_eq!(elements, expected);
178    }
179
180    #[test]
181    fn bdc_with_dict() {
182        let input = b"/Span << /MCID 0 /Alt (Alt)>> BDC EMC";
183
184        let expected = vec![
185            TypedInstruction::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
186                Name::new(b"Span"),
187                Object::Dict(Dict::from_bytes(b"<< /MCID 0 /Alt (Alt)>>").unwrap()),
188            )),
189            TypedInstruction::EndMarkedContent(EndMarkedContent),
190        ];
191
192        let elements = TypedIter::new(input).collect::<Vec<_>>();
193
194        assert_eq!(elements, expected);
195    }
196
197    #[test]
198    fn bdc_with_name() {
199        let input = b"/Span /Name BDC EMC";
200
201        let expected = vec![
202            TypedInstruction::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
203                Name::new(b"Span"),
204                Object::Name(Name::new(b"Name")),
205            )),
206            TypedInstruction::EndMarkedContent(EndMarkedContent),
207        ];
208
209        let elements = TypedIter::new(input).collect::<Vec<_>>();
210
211        assert_eq!(elements, expected);
212    }
213}