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::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}