1use 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#[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::Name;
81 use crate::object::Number;
82 use crate::object::Object;
83 use crate::object::{Dict, FromBytes};
84 use smallvec::smallvec;
85
86 fn n(num: i32) -> Number {
87 Number::from_i32(num)
88 }
89
90 #[test]
91 fn basic_ops_1() {
92 let input = b"
931 0 0 -1 0 200 cm
94/g0 gs
951 0 0 rg
96";
97
98 let expected = vec![
99 TypedInstruction::Transform(Transform(n(1), n(0), n(0), n(-1), n(0), n(200))),
100 TypedInstruction::SetGraphicsState(SetGraphicsState(Name::new(b"g0"))),
101 TypedInstruction::NonStrokeColorDeviceRgb(NonStrokeColorDeviceRgb(n(1), n(0), n(0))),
102 ];
103
104 let elements = TypedIter::new(input).collect::<Vec<_>>();
105 assert_eq!(elements, expected,)
106 }
107
108 #[test]
109 fn basic_ops_2() {
110 let input = b"
11120 20 m
112180 20 l
113180 180 l
11420 180 l
115h
116f
117";
118
119 let expected = vec![
120 TypedInstruction::MoveTo(MoveTo(n(20), n(20))),
121 TypedInstruction::LineTo(LineTo(n(180), n(20))),
122 TypedInstruction::LineTo(LineTo(n(180), n(180))),
123 TypedInstruction::LineTo(LineTo(n(20), n(180))),
124 TypedInstruction::ClosePath(ClosePath),
125 TypedInstruction::FillPathNonZero(FillPathNonZero),
126 ];
127
128 let elements = TypedIter::new(input).collect::<Vec<_>>();
129 assert_eq!(elements, expected,)
130 }
131
132 #[test]
133 fn scn() {
134 let input = b"
1350.0 scn
1361.0 1.0 1.0 /DeviceRgb SCN
137";
138
139 let expected = vec![
140 TypedInstruction::NonStrokeColorNamed(NonStrokeColorNamed(
141 smallvec![Number::from_i32(0)],
142 None,
143 )),
144 TypedInstruction::StrokeColorNamed(StrokeColorNamed(
145 smallvec![
146 Number::from_i32(1),
147 Number::from_i32(1),
148 Number::from_i32(1)
149 ],
150 Some(Name::new(b"DeviceRgb")),
151 )),
152 ];
153
154 let elements = TypedIter::new(input).collect::<Vec<_>>();
155
156 assert_eq!(elements, expected);
157 }
158
159 #[test]
160 fn dp() {
161 let input = b"/Attribute<</ShowCenterPoint false >> DP";
162
163 let expected = vec![TypedInstruction::MarkedContentPointWithProperties(
164 MarkedContentPointWithProperties(
165 Name::new(b"Attribute"),
166 Object::Dict(Dict::from_bytes(b"<</ShowCenterPoint false >>").unwrap()),
167 ),
168 )];
169
170 let elements = TypedIter::new(input).collect::<Vec<_>>();
171
172 assert_eq!(elements, expected);
173 }
174
175 #[test]
176 fn bdc_with_dict() {
177 let input = b"/Span << /MCID 0 /Alt (Alt)>> BDC EMC";
178
179 let expected = vec![
180 TypedInstruction::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
181 Name::new(b"Span"),
182 Object::Dict(Dict::from_bytes(b"<< /MCID 0 /Alt (Alt)>>").unwrap()),
183 )),
184 TypedInstruction::EndMarkedContent(EndMarkedContent),
185 ];
186
187 let elements = TypedIter::new(input).collect::<Vec<_>>();
188
189 assert_eq!(elements, expected);
190 }
191
192 #[test]
193 fn bdc_with_name() {
194 let input = b"/Span /Name BDC EMC";
195
196 let expected = vec![
197 TypedInstruction::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
198 Name::new(b"Span"),
199 Object::Name(Name::new(b"Name")),
200 )),
201 TypedInstruction::EndMarkedContent(EndMarkedContent),
202 ];
203
204 let elements = TypedIter::new(input).collect::<Vec<_>>();
205
206 assert_eq!(elements, expected);
207 }
208}