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(pub SmallVec<[Number; OPERANDS_THRESHOLD]>, pub Option<Name>);
21
22fn scn_fn(stack: &Stack<'_>) -> Option<(SmallVec<[Number; OPERANDS_THRESHOLD]>, Option<Name>)> {
23 let mut nums = smallvec![];
24 let mut name = None;
25
26 for o in &stack.0 {
27 match o {
28 Object::Number(n) => nums.push(*n),
29 Object::Name(n) => name = Some(n.clone()),
30 _ => {
31 warn!("encountered unknown object {o:?} when parsing scn/SCN");
32
33 return None;
34 }
35 }
36 }
37
38 Some((nums, name))
39}
40
41op_impl!(
42 StrokeColorNamed,
43 "SCN",
44 u8::MAX as usize,
45 |stack: &Stack<'_>| {
46 let res = scn_fn(stack);
47 res.map(|r| StrokeColorNamed(r.0, r.1))
48 }
49);
50
51#[derive(Debug, PartialEq, Clone)]
52pub struct NonStrokeColorNamed(pub SmallVec<[Number; OPERANDS_THRESHOLD]>, pub Option<Name>);
53
54op_impl!(
55 NonStrokeColorNamed,
56 "scn",
57 u8::MAX as usize,
58 |stack: &Stack<'_>| {
59 let res = scn_fn(stack);
60 res.map(|r| NonStrokeColorNamed(r.0, r.1))
61 }
62);
63
64#[cfg(test)]
65mod tests {
66 use crate::content::TypedIter;
67 use crate::content::ops::{
68 BeginMarkedContentWithProperties, ClosePath, EndMarkedContent, FillPathNonZero, LineTo,
69 MarkedContentPointWithProperties, MoveTo, NonStrokeColorDeviceRgb, NonStrokeColorNamed,
70 SetGraphicsState, StrokeColorNamed, Transform, TypedInstruction,
71 };
72 use crate::object::Name;
73 use crate::object::Number;
74 use crate::object::Object;
75 use crate::object::{Dict, FromBytes};
76 use smallvec::smallvec;
77
78 fn n(num: i32) -> Number {
79 Number::from_i32(num)
80 }
81
82 #[test]
83 fn basic_ops_1() {
84 let input = b"
851 0 0 -1 0 200 cm
86/g0 gs
871 0 0 rg
88";
89
90 let expected = vec![
91 TypedInstruction::Transform(Transform(n(1), n(0), n(0), n(-1), n(0), n(200))),
92 TypedInstruction::SetGraphicsState(SetGraphicsState(Name::new(b"g0"))),
93 TypedInstruction::NonStrokeColorDeviceRgb(NonStrokeColorDeviceRgb(n(1), n(0), n(0))),
94 ];
95
96 let elements = TypedIter::new(input).collect::<Vec<_>>();
97 assert_eq!(elements, expected,);
98 }
99
100 #[test]
101 fn basic_ops_2() {
102 let input = b"
10320 20 m
104180 20 l
105180 180 l
10620 180 l
107h
108f
109";
110
111 let expected = vec![
112 TypedInstruction::MoveTo(MoveTo(n(20), n(20))),
113 TypedInstruction::LineTo(LineTo(n(180), n(20))),
114 TypedInstruction::LineTo(LineTo(n(180), n(180))),
115 TypedInstruction::LineTo(LineTo(n(20), n(180))),
116 TypedInstruction::ClosePath(ClosePath),
117 TypedInstruction::FillPathNonZero(FillPathNonZero),
118 ];
119
120 let elements = TypedIter::new(input).collect::<Vec<_>>();
121 assert_eq!(elements, expected,);
122 }
123
124 #[test]
125 fn scn() {
126 let input = b"
1270.0 scn
1281.0 1.0 1.0 /DeviceRgb SCN
129";
130
131 let expected = vec![
132 TypedInstruction::NonStrokeColorNamed(NonStrokeColorNamed(
133 smallvec![Number::from_f32(0.0)],
134 None,
135 )),
136 TypedInstruction::StrokeColorNamed(StrokeColorNamed(
137 smallvec![
138 Number::from_f32(1.0),
139 Number::from_f32(1.0),
140 Number::from_f32(1.0)
141 ],
142 Some(Name::new(b"DeviceRgb")),
143 )),
144 ];
145
146 let elements = TypedIter::new(input).collect::<Vec<_>>();
147
148 assert_eq!(elements, expected);
149 }
150
151 #[test]
152 fn dp() {
153 let input = b"/Attribute<</ShowCenterPoint false >> DP";
154
155 let expected = vec![TypedInstruction::MarkedContentPointWithProperties(
156 MarkedContentPointWithProperties(
157 Name::new(b"Attribute"),
158 Object::Dict(Dict::from_bytes(b"<</ShowCenterPoint false >>").unwrap()),
159 ),
160 )];
161
162 let elements = TypedIter::new(input).collect::<Vec<_>>();
163
164 assert_eq!(elements, expected);
165 }
166
167 #[test]
168 fn bdc_with_dict() {
169 let input = b"/Span << /MCID 0 /Alt (Alt)>> BDC EMC";
170
171 let expected = vec![
172 TypedInstruction::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
173 Name::new(b"Span"),
174 Object::Dict(Dict::from_bytes(b"<< /MCID 0 /Alt (Alt)>>").unwrap()),
175 )),
176 TypedInstruction::EndMarkedContent(EndMarkedContent),
177 ];
178
179 let elements = TypedIter::new(input).collect::<Vec<_>>();
180
181 assert_eq!(elements, expected);
182 }
183
184 #[test]
185 fn bdc_with_name() {
186 let input = b"/Span /Name BDC EMC";
187
188 let expected = vec![
189 TypedInstruction::BeginMarkedContentWithProperties(BeginMarkedContentWithProperties(
190 Name::new(b"Span"),
191 Object::Name(Name::new(b"Name")),
192 )),
193 TypedInstruction::EndMarkedContent(EndMarkedContent),
194 ];
195
196 let elements = TypedIter::new(input).collect::<Vec<_>>();
197
198 assert_eq!(elements, expected);
199 }
200}