1use std::fmt::{self, Display};
3use std::cmp::Ordering;
4use itertools::Itertools;
5use istring::SmallString;
6use datasize::DataSize;
7use std::sync::Arc;
8
9use crate::error::*;
10use crate::object::*;
11use crate::parser::{Lexer, parse_with_lexer, ParseFlags};
12use crate::primitive::*;
13use crate::enc::StreamFilter;
14use crate as pdf;
15
16#[derive(Debug, Clone, DeepClone, DataSize)]
18pub struct Content {
19 pub parts: Vec<RcRef<Stream<()>>>,
21}
22
23impl Content {
24 pub fn operations(&self, resolve: &impl Resolve) -> Result<Vec<Op>> {
25 let mut data = vec![];
26 for part in self.parts.iter() {
27 data.extend_from_slice(&t!(part.data(resolve)));
28 }
29 parse_ops(&data, resolve)
30 }
31}
32
33pub fn parse_ops(data: &[u8], resolve: &impl Resolve) -> Result<Vec<Op>> {
34 let mut ops = OpBuilder::new();
35 ops.parse(data, resolve)?;
36 Ok(ops.ops)
37}
38
39macro_rules! names {
40 ($args:ident, $($x:ident),*) => (
41 $(
42 let $x = name(&mut $args)?;
43 )*
44 )
45}
46macro_rules! numbers {
47 ($args:ident, $($x:ident),*) => (
48 $(
49 let $x = number(&mut $args)?;
50 )*
51 )
52}
53macro_rules! points {
54 ($args:ident, $($point:ident),*) => (
55 $(
56 let $point = point(&mut $args)?;
57 )*
58 )
59}
60fn name(args: &mut impl Iterator<Item=Primitive>) -> Result<Name> {
61 args.next().ok_or(PdfError::NoOpArg)?.into_name()
62}
63fn number(args: &mut impl Iterator<Item=Primitive>) -> Result<f32> {
64 args.next().ok_or(PdfError::NoOpArg)?.as_number()
65}
66fn string(args: &mut impl Iterator<Item=Primitive>) -> Result<PdfString> {
67 args.next().ok_or(PdfError::NoOpArg)?.into_string()
68}
69fn point(args: &mut impl Iterator<Item=Primitive>) -> Result<Point> {
70 let x = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
71 let y = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
72 Ok(Point { x, y })
73}
74fn rect(args: &mut impl Iterator<Item=Primitive>) -> Result<ViewRect> {
75 let x = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
76 let y = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
77 let width = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
78 let height = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
79 Ok(ViewRect { x, y, width, height })
80}
81fn rgb(args: &mut impl Iterator<Item=Primitive>) -> Result<Rgb> {
82 let red = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
83 let green = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
84 let blue = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
85 Ok(Rgb { red, green, blue })
86}
87fn cmyk(args: &mut impl Iterator<Item=Primitive>) -> Result<Cmyk> {
88 let cyan = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
89 let magenta = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
90 let yellow = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
91 let key = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
92 Ok(Cmyk { cyan, magenta, yellow, key })
93}
94fn matrix(args: &mut impl Iterator<Item=Primitive>) -> Result<Matrix> {
95 Ok(Matrix {
96 a: number(args)?,
97 b: number(args)?,
98 c: number(args)?,
99 d: number(args)?,
100 e: number(args)?,
101 f: number(args)?,
102 })
103}
104fn array(args: &mut impl Iterator<Item=Primitive>) -> Result<Vec<Primitive>> {
105 match args.next() {
106 Some(Primitive::Array(arr)) => Ok(arr),
107 None => Ok(vec![]),
108 _ => Err(PdfError::NoOpArg)
109 }
110}
111
112fn expand_abbr_name(name: SmallString, alt: &[(&str, &str)]) -> SmallString {
113 for &(p, r) in alt {
114 if name == p {
115 return r.into();
116 }
117 }
118 name
119}
120fn expand_abbr(p: Primitive, alt: &[(&str, &str)]) -> Primitive {
121 match p {
122 Primitive::Name(name) => Primitive::Name(expand_abbr_name(name, alt)),
123 Primitive::Array(items) => Primitive::Array(items.into_iter().map(|p| expand_abbr(p, alt)).collect()),
124 p => p
125 }
126}
127
128fn inline_image(lexer: &mut Lexer, resolve: &impl Resolve) -> Result<Arc<ImageXObject>> {
129 let mut dict = Dictionary::new();
130 loop {
131 let backup_pos = lexer.get_pos();
132 let obj = parse_with_lexer(lexer, &NoResolve, ParseFlags::ANY);
133 let key = match obj {
134 Ok(Primitive::Name(key)) => key,
135 Err(e) if e.is_eof() => return Err(e),
136 Err(_) => {
137 lexer.set_pos(backup_pos);
138 break;
139 }
140 Ok(_) => bail!("invalid key type")
141 };
142 let key = expand_abbr_name(key, &[
143 ("BPC", "BitsPerComponent"),
144 ("CS", "ColorSpace"),
145 ("D", "Decode"),
146 ("DP", "DecodeParms"),
147 ("F", "Filter"),
148 ("H", "Height"),
149 ("IM", "ImageMask"),
150 ("I", "Interpolate"),
151 ("W", "Width"),
152 ]);
153 let val = parse_with_lexer(lexer, &NoResolve, ParseFlags::ANY)?;
154 dict.insert(key, val);
155 }
156 lexer.next_expect("ID")?;
157 let data_start = lexer.get_pos() + 1;
158
159 if lexer.seek_substr("\nEI").is_none() {
161 bail!("inline image exceeds expected data range");
162 }
163 let data_end = lexer.get_pos() - 3;
164
165 let bits_per_component = dict.get("BitsPerComponent").map(|p| p.as_integer()).transpose()?;
167 let color_space = dict.get("ColorSpace").map(|p| ColorSpace::from_primitive(expand_abbr(p.clone(),
168 &[
169 ("G", "DeviceGray"),
170 ("RGB", "DeviceRGB"),
171 ("CMYK", "DeviceCMYK"),
172 ("I", "Indexed")
173 ]
174 ), resolve)).transpose()?;
175 let decode = dict.get("Decode").map(|p| Object::from_primitive(p.clone(), resolve)).transpose()?;
176 let decode_parms = dict.get("DecodeParms").map(|p| p.clone().resolve(resolve)?.into_dictionary()).transpose()?.unwrap_or_default();
177 let filter = dict.remove("Filter").map(|p| expand_abbr(p,
178 &[
179 ("AHx", "ASCIIHexDecode"),
180 ("A85", "ASCII85Decode"),
181 ("LZW", "LZWDecode"),
182 ("Fl", "FlateDecode"),
183 ("RL", "RunLengthDecode"),
184 ("CCF", "CCITTFaxDecode"),
185 ("DCT", "DCTDecode"),
186 ]
187 ));
188 let filters = match filter {
189 Some(Primitive::Array(parts)) => parts.into_iter()
190 .map(|p| p.as_name().and_then(|kind| StreamFilter::from_kind_and_params(kind, decode_parms.clone(), resolve)))
191 .collect::<Result<_>>()?,
192 Some(Primitive::Name(kind)) => vec![StreamFilter::from_kind_and_params(&kind, decode_parms, resolve)?],
193 None => vec![],
194 _ => bail!("invalid filter")
195 };
196
197 let height = dict.require("InlineImage", "Height")?.as_u32()?;
198 let image_mask = dict.get("ImageMask").map(|p| p.as_bool()).transpose()?.unwrap_or(false);
199 let intent = dict.remove("Intent").map(|p| RenderingIntent::from_primitive(p, &NoResolve)).transpose()?;
200 let interpolate = dict.get("Interpolate").map(|p| p.as_bool()).transpose()?.unwrap_or(false);
201 let width = dict.require("InlineImage", "Width")?.as_u32()?;
202
203 let image_dict = ImageDict {
204 width,
205 height,
206 color_space,
207 bits_per_component,
208 intent,
209 image_mask,
210 mask: None,
211 decode,
212 interpolate,
213 struct_parent: None,
214 id: None,
215 smask: None,
216 other: dict,
217 };
218
219 let data = lexer.new_substr(data_start .. data_end).to_vec();
220
221 Ok(Arc::new(ImageXObject { inner: Stream::from_compressed(image_dict, data, filters) }))
222}
223
224struct OpBuilder {
225 last: Point,
226 compability_section: bool,
227 ops: Vec<Op>
228}
229impl OpBuilder {
230 fn new() -> Self {
231 OpBuilder {
232 last: Point { x: 0., y: 0. },
233 compability_section: false,
234 ops: Vec::new()
235 }
236 }
237 fn parse(&mut self, data: &[u8], resolve: &impl Resolve) -> Result<()> {
238 let mut lexer = Lexer::new(data);
239 let mut buffer = Vec::with_capacity(5);
240
241 loop {
242 let backup_pos = lexer.get_pos();
243 let obj = parse_with_lexer(&mut lexer, resolve, ParseFlags::ANY);
244 match obj {
245 Ok(obj) => {
246 buffer.push(obj)
248 }
249 Err(e) => {
250 if e.is_eof() {
251 break;
252 }
253 lexer.set_pos(backup_pos);
255 let op = t!(lexer.next());
256 let operator = t!(op.as_str(), op);
257 match self.add(operator, buffer.drain(..), &mut lexer, resolve) {
258 Ok(()) => {},
259 Err(e) if resolve.options().allow_invalid_ops => {
260 warn!("OP Err: {:?}", e);
261 },
262 Err(e) => return Err(e),
263 }
264 }
265 }
266 match lexer.get_pos().cmp(&data.len()) {
267 Ordering::Greater => err!(PdfError::ContentReadPastBoundary),
268 Ordering::Less => (),
269 Ordering::Equal => break
270 }
271 }
272 Ok(())
273 }
274 fn add(&mut self, op: &str, mut args: impl Iterator<Item=Primitive>, lexer: &mut Lexer, resolve: &impl Resolve) -> Result<()> {
275 use Winding::*;
276
277 let ops = &mut self.ops;
278 let mut push = move |op| ops.push(op);
279
280 match op {
281 "b" => {
282 push(Op::Close);
283 push(Op::FillAndStroke { winding: NonZero });
284 },
285 "B" => push(Op::FillAndStroke { winding: NonZero }),
286 "b*" => {
287 push(Op::Close);
288 push(Op::FillAndStroke { winding: EvenOdd });
289 }
290 "B*" => push(Op::FillAndStroke { winding: EvenOdd }),
291 "BDC" => push(Op::BeginMarkedContent {
292 tag: name(&mut args)?,
293 properties: Some(args.next().ok_or(PdfError::NoOpArg)?)
294 }),
295 "BI" => push(Op::InlineImage { image: inline_image(lexer, resolve)? }),
296 "BMC" => push(Op::BeginMarkedContent {
297 tag: name(&mut args)?,
298 properties: None
299 }),
300 "BT" => push(Op::BeginText),
301 "BX" => self.compability_section = true,
302 "c" => {
303 points!(args, c1, c2, p);
304 push(Op::CurveTo { c1, c2, p });
305 self.last = p;
306 }
307 "cm" => {
308 numbers!(args, a, b, c, d, e, f);
309 push(Op::Transform { matrix: Matrix { a, b, c, d, e, f }});
310 }
311 "CS" => {
312 names!(args, name);
313 push(Op::StrokeColorSpace { name });
314 }
315 "cs" => {
316 names!(args, name);
317 push(Op::FillColorSpace { name });
318 }
319 "d" => {
320 let p = args.next().ok_or(PdfError::NoOpArg)?;
321 let pattern = p.as_array()?.iter().map(|p| p.as_number()).collect::<Result<Vec<f32>, PdfError>>()?;
322 let phase = args.next().ok_or(PdfError::NoOpArg)?.as_number()?;
323 push(Op::Dash { pattern, phase });
324 }
325 "d0" => {}
326 "d1" => {}
327 "Do" | "Do0" => {
328 names!(args, name);
329 push(Op::XObject { name });
330 }
331 "DP" => push(Op::MarkedContentPoint {
332 tag: name(&mut args)?,
333 properties: Some(args.next().ok_or(PdfError::NoOpArg)?)
334 }),
335 "EI" => bail!("Parse Error. Unexpected 'EI'"),
336 "EMC" => push(Op::EndMarkedContent),
337 "ET" => push(Op::EndText),
338 "EX" => self.compability_section = false,
339 "f" |
340 "F" => push(Op::Fill { winding: NonZero }),
341 "f*" => push(Op::Fill { winding: EvenOdd }),
342 "G" => push(Op::StrokeColor { color: Color::Gray(number(&mut args)?) }),
343 "g" => push(Op::FillColor { color: Color::Gray(number(&mut args)?) }),
344 "gs" => push(Op::GraphicsState { name: name(&mut args)? }),
345 "h" => push(Op::Close),
346 "i" => push(Op::Flatness { tolerance: number(&mut args)? }),
347 "ID" => bail!("Parse Error. Unexpected 'ID'"),
348 "j" => {
349 let n = args.next().ok_or(PdfError::NoOpArg)?.as_integer()?;
350 let join = match n {
351 0 => LineJoin::Miter,
352 1 => LineJoin::Round,
353 2 => LineJoin::Bevel,
354 _ => bail!("invalid line join {}", n)
355 };
356 push(Op::LineJoin { join });
357 }
358 "J" => {
359 let n = args.next().ok_or(PdfError::NoOpArg)?.as_integer()?;
360 let cap = match n {
361 0 => LineCap::Butt,
362 1 => LineCap::Round,
363 2 => LineCap::Square,
364 _ => bail!("invalid line cap {}", n)
365 };
366 push(Op::LineCap { cap });
367 }
368 "K" => {
369 let color = Color::Cmyk(cmyk(&mut args)?);
370 push(Op::StrokeColor { color });
371 }
372 "k" => {
373 let color = Color::Cmyk(cmyk(&mut args)?);
374 push(Op::FillColor { color });
375 }
376 "l" => {
377 let p = point(&mut args)?;
378 push(Op::LineTo { p });
379 self.last = p;
380 }
381 "m" => {
382 let p = point(&mut args)?;
383 push(Op::MoveTo { p });
384 self.last = p;
385 }
386 "M" => push(Op::MiterLimit { limit: number(&mut args)? }),
387 "MP" => push(Op::MarkedContentPoint { tag: name(&mut args)?, properties: None }),
388 "n" => push(Op::EndPath),
389 "q" => push(Op::Save),
390 "Q" => push(Op::Restore),
391 "re" => push(Op::Rect { rect: rect(&mut args)? }),
392 "RG" => push(Op::StrokeColor { color: Color::Rgb(rgb(&mut args)?) }),
393 "rg" => push(Op::FillColor { color: Color::Rgb(rgb(&mut args)?) }),
394 "ri" => {
395 let s = name(&mut args)?;
396 let intent = RenderingIntent::from_str(&s)
397 .ok_or_else(|| PdfError::Other { msg: format!("invalid rendering intent {}", s) })?;
398 push(Op::RenderingIntent { intent });
399 },
400 "s" => {
401 push(Op::Close);
402 push(Op::Stroke);
403 }
404 "S" => push(Op::Stroke),
405 "SC" | "SCN" => {
406 push(Op::StrokeColor { color: Color::Other(args.collect()) });
407 }
408 "sc" | "scn" => {
409 push(Op::FillColor { color: Color::Other(args.collect()) });
410 }
411 "sh" => {
412
413 }
414 "T*" => push(Op::TextNewline),
415 "Tc" => push(Op::CharSpacing { char_space: number(&mut args)? }),
416 "Td" => push(Op::MoveTextPosition { translation: point(&mut args)? }),
417 "TD" => {
418 let translation = point(&mut args)?;
419 push(Op::Leading { leading: -translation.y });
420 push(Op::MoveTextPosition { translation });
421 }
422 "Tf" => push(Op::TextFont { name: name(&mut args)?, size: number(&mut args)? }),
423 "Tj" => push(Op::TextDraw { text: string(&mut args)? }),
424 "TJ" => {
425 let mut result = Vec::<TextDrawAdjusted>::new();
426
427 for spacing_or_text in array(&mut args)?.into_iter() {
428 let spacing_or_text = match spacing_or_text {
429 Primitive::Integer(i) => TextDrawAdjusted::Spacing(i as f32),
430 Primitive::Number(f) => TextDrawAdjusted::Spacing(f),
431 Primitive::String(text) => TextDrawAdjusted::Text(text),
432 p => bail!("invalid primitive in TJ operator: {:?}", p)
433 };
434
435 result.push(spacing_or_text);
436 }
437
438 push(Op::TextDrawAdjusted { array: result })
439 }
440 "TL" => push(Op::Leading { leading: number(&mut args)? }),
441 "Tm" => push(Op::SetTextMatrix { matrix: matrix(&mut args)? }),
442 "Tr" => {
443 use TextMode::*;
444
445 let n = args.next().ok_or(PdfError::NoOpArg)?.as_integer()?;
446 let mode = match n {
447 0 => Fill,
448 1 => Stroke,
449 2 => FillThenStroke,
450 3 => Invisible,
451 4 => FillAndClip,
452 5 => StrokeAndClip,
453 _ => {
454 bail!("Invalid text render mode: {}", n);
455 }
456 };
457 push(Op::TextRenderMode { mode });
458 }
459 "Ts" => push(Op::TextRise { rise: number(&mut args)? }),
460 "Tw" => push(Op::WordSpacing { word_space: number(&mut args)? }),
461 "Tz" => push(Op::TextScaling { horiz_scale: number(&mut args)? }),
462 "v" => {
463 points!(args, c2, p);
464 push(Op::CurveTo { c1: self.last, c2, p });
465 self.last = p;
466 }
467 "w" => push(Op::LineWidth { width: number(&mut args)? }),
468 "W" => push(Op::Clip { winding: NonZero }),
469 "W*" => push(Op::Clip { winding: EvenOdd }),
470 "y" => {
471 points!(args, c1, p);
472 push(Op::CurveTo { c1, c2: p, p });
473 self.last = p;
474 }
475 "'" => {
476 push(Op::TextNewline);
477 push(Op::TextDraw { text: string(&mut args)? });
478 }
479 "\"" => {
480 push(Op::WordSpacing { word_space: number(&mut args)? });
481 push(Op::CharSpacing { char_space: number(&mut args)? });
482 push(Op::TextNewline);
483 push(Op::TextDraw { text: string(&mut args)? });
484 }
485 o if !self.compability_section => {
486 bail!("invalid operator {}", o)
487 },
488 _ => {}
489 }
490 Ok(())
491 }
492}
493
494impl Object for Content {
495 fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
497 type ContentStream = Stream<()>;
498 let mut parts: Vec<RcRef<ContentStream>> = vec![];
499
500 match p {
501 Primitive::Array(arr) => {
502 for p in arr {
503 let part = t!(RcRef::from_primitive(p, resolve));
504 parts.push(part);
505 }
506 }
507 Primitive::Reference(_) => {
508 parts.push(RcRef::from_primitive(p, resolve)?);
509 }
510 p => {
511 return Err(PdfError::UnexpectedPrimitive { expected: "list of references or reference", found: p.get_debug_name() });
512 }
513 }
514
515 Ok(Content { parts })
516 }
517}
518
519#[derive(Debug, DataSize, DeepClone, Clone)]
520pub struct FormXObject {
521 pub stream: Stream<FormDict>,
522}
523impl FormXObject {
524 pub fn dict(&self) -> &FormDict {
525 &self.stream.info.info
526 }
527 pub fn operations(&self, resolve: &impl Resolve) -> Result<Vec<Op>> {
528 let mut ops = OpBuilder::new();
529 let data = self.stream.data(resolve)?;
530 t!(ops.parse(&data, resolve));
531 Ok(ops.ops)
532 }
533}
534impl Object for FormXObject {
535 fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
537 let stream = t!(Stream::<FormDict>::from_primitive(p, resolve));
538 Ok(FormXObject {
539 stream,
540 })
541 }
542}
543impl ObjectWrite for FormXObject {
544 fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
545 let mut stream = self.stream.to_pdf_stream(update)?;
546 stream.info.insert("Subtype", Name::from("Form"));
547 Ok(stream.into())
548 }
549}
550
551#[allow(clippy::float_cmp)] pub fn serialize_ops(mut ops: &[Op]) -> Result<Vec<u8>> {
553 use std::io::Write;
554
555 let mut data = Vec::new();
556 let mut current_point = None;
557 let f = &mut data;
558
559 while ops.len() > 0 {
560 let mut advance = 1;
561 match ops[0] {
562 Op::BeginMarkedContent { ref tag, properties: Some(ref name) } => {
563 serialize_name(tag, f)?;
564 write!(f, " ")?;
565 name.serialize(f)?;
566 writeln!(f, " BDC")?;
567 }
568 Op::BeginMarkedContent { ref tag, properties: None } => {
569 serialize_name(tag, f)?;
570 writeln!(f, " BMC")?;
571 }
572 Op::MarkedContentPoint { ref tag, properties: Some(ref name) } => {
573 serialize_name(tag, f)?;
574 write!(f, " ")?;
575 name.serialize(f)?;
576 writeln!(f, " DP")?;
577 }
578 Op::MarkedContentPoint { ref tag, properties: None } => {
579 serialize_name(tag, f)?;
580 writeln!(f, " MP")?;
581 }
582 Op::EndMarkedContent => writeln!(f, "EMC")?,
583 Op::Close => match ops.get(1) {
584 Some(Op::Stroke) => {
585 writeln!(f, "s")?;
586 advance += 1;
587 }
588 Some(Op::FillAndStroke { winding: Winding::NonZero }) => {
589 writeln!(f, "b")?;
590 advance += 1;
591 }
592 Some(Op::FillAndStroke { winding: Winding::EvenOdd }) => {
593 writeln!(f, "b*")?;
594 advance += 1;
595 }
596 _ => writeln!(f, "h")?,
597 }
598 Op::MoveTo { p } => {
599 writeln!(f, "{} m", p)?;
600 current_point = Some(p);
601 }
602 Op::LineTo { p } => {
603 writeln!(f, "{} l", p)?;
604 current_point = Some(p);
605 },
606 Op::CurveTo { c1, c2, p } => {
607 if Some(c1) == current_point {
608 writeln!(f, "{} {} v", c2, p)?;
609 } else if c2 == p {
610 writeln!(f, "{} {} y", c1, p)?;
611 } else {
612 writeln!(f, "{} {} {} c", c1, c2, p)?;
613 }
614 current_point = Some(p);
615 },
616 Op::Rect { rect } => writeln!(f, "{} re", rect)?,
617 Op::EndPath => writeln!(f, "n")?,
618 Op::Stroke => writeln!(f, "S")?,
619 Op::FillAndStroke { winding: Winding::NonZero } => writeln!(f, "B")?,
620 Op::FillAndStroke { winding: Winding::EvenOdd } => writeln!(f, "B*")?,
621 Op::Fill { winding: Winding::NonZero } => writeln!(f, "f")?,
622 Op::Fill { winding: Winding::EvenOdd } => writeln!(f, "f*")?,
623 Op::Shade { ref name } => {
624 serialize_name(name, f)?;
625 writeln!(f, " sh")?;
626 },
627 Op::Clip { winding: Winding::NonZero } => writeln!(f, "W")?,
628 Op::Clip { winding: Winding::EvenOdd } => writeln!(f, "W*")?,
629 Op::Save => writeln!(f, "q")?,
630 Op::Restore => writeln!(f, "Q")?,
631 Op::Transform { matrix } => writeln!(f, "{} cm", matrix)?,
632 Op::LineWidth { width } => writeln!(f, "{} w", width)?,
633 Op::Dash { ref pattern, phase } => writeln!(f, "[{}] {} d", pattern.iter().format(" "), phase)?,
634 Op::LineJoin { join } => writeln!(f, "{} j", join as u8)?,
635 Op::LineCap { cap } => writeln!(f, "{} J", cap as u8)?,
636 Op::MiterLimit { limit } => writeln!(f, "{} M", limit)?,
637 Op::Flatness { tolerance } => writeln!(f, "{} i", tolerance)?,
638 Op::GraphicsState { ref name } => {
639 serialize_name(name, f)?;
640 writeln!(f, " gs")?;
641 },
642 Op::StrokeColor { color: Color::Gray(g) } => writeln!(f, "{} G", g)?,
643 Op::StrokeColor { color: Color::Rgb(rgb) } => writeln!(f, "{} RG", rgb)?,
644 Op::StrokeColor { color: Color::Cmyk(cmyk) } => writeln!(f, "{} K", cmyk)?,
645 Op::StrokeColor { color: Color::Other(ref args) } => {
646 for p in args {
647 p.serialize(f)?;
648 write!(f, " ")?;
649 }
650 writeln!(f, "SCN")?;
651 }
652 Op::FillColor { color: Color::Gray(g) } => writeln!(f, "{} g", g)?,
653 Op::FillColor { color: Color::Rgb(rgb) } => writeln!(f, "{} rg", rgb)?,
654 Op::FillColor { color: Color::Cmyk(cmyk) } => writeln!(f, "{} k", cmyk)?,
655 Op::FillColor { color: Color::Other(ref args) } => {
656 for p in args {
657 p.serialize(f)?;
658 write!(f, " ")?;
659 }
660 writeln!(f, "scn")?;
661 }
662 Op::FillColorSpace { ref name } => {
663 serialize_name(name, f)?;
664 writeln!(f, " cs")?;
665 },
666 Op::StrokeColorSpace { ref name } => {
667 serialize_name(name, f)?;
668 writeln!(f, " CS")?;
669 },
670
671 Op::RenderingIntent { intent } => writeln!(f, "{} ri", intent.to_str())?,
672 Op::BeginText => writeln!(f, "BT")?,
673 Op::EndText => writeln!(f, "ET")?,
674 Op::CharSpacing { char_space } => writeln!(f, "{} Tc", char_space)?,
675 Op::WordSpacing { word_space } => {
676 if let [
677 Op::CharSpacing { char_space },
678 Op::TextNewline,
679 Op::TextDraw { ref text },
680 ..
681 ] = ops[1..] {
682 write!(f, "{} {} ", word_space, char_space)?;
683 text.serialize(f)?;
684 writeln!(f, " \"")?;
685 advance += 3;
686 } else {
687 writeln!(f, "{} Tw", word_space)?;
688 }
689 }
690 Op::TextScaling { horiz_scale } => writeln!(f, "{} Tz", horiz_scale)?,
691 Op::Leading { leading } => match ops[1..] {
692 [Op::MoveTextPosition { translation }, ..] if leading == -translation.x => {
693 writeln!(f, "{} {} TD", translation.x, translation.y)?;
694 advance += 1;
695 }
696 _ => {
697 writeln!(f, "{} TL", leading)?;
698 }
699 }
700 Op::TextFont { ref name, ref size } => {
701 serialize_name(name, f)?;
702 writeln!(f, " {} Tf", size)?;
703 },
704 Op::TextRenderMode { mode } => writeln!(f, "{} Tr", mode as u8)?,
705 Op::TextRise { rise } => writeln!(f, "{} Ts", rise)?,
706 Op::MoveTextPosition { translation } => writeln!(f, "{} {} Td", translation.x, translation.y)?,
707 Op::SetTextMatrix { matrix } => writeln!(f, "{} Tm", matrix)?,
708 Op::TextNewline => {
709 if let [Op::TextDraw { ref text }, ..] = ops[1..] {
710 text.serialize(f)?;
711 writeln!(f, " '")?;
712 advance += 1;
713 } else {
714 writeln!(f, "T*")?;
715 }
716 },
717 Op::TextDraw { ref text } => {
718 text.serialize(f)?;
719 writeln!(f, " Tj")?;
720 },
721 Op::TextDrawAdjusted { ref array } => {
722 write!(f, "[")?;
723 for (i, val) in array.iter().enumerate() {
724 if i > 0 {
725 write!(f, " ")?;
726 }
727 match val {
728 TextDrawAdjusted::Spacing(s) => write!(f, "{s}")?,
729 TextDrawAdjusted::Text(data) => data.serialize(f)?,
730 }
731 }
732 writeln!(f, "] TJ")?;
733 },
734 Op::InlineImage { image: _ } => unimplemented!(),
735 Op::XObject { ref name } => {
736 serialize_name(name, f)?;
737 writeln!(f, " Do")?;
738 },
739 }
740 ops = &ops[advance..];
741 }
742 Ok(data)
743}
744
745impl Content {
746 pub fn from_ops(operations: Vec<Op>, update: &mut impl Updater) -> Result<Self> {
747 let data = serialize_ops(&operations).unwrap();
748 Ok(Content {
749 parts: vec![update.create(Stream::new((), &data)?)?]
750 })
751 }
752}
753
754impl ObjectWrite for Content {
755 fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
756 if self.parts.len() == 1 {
757 self.parts[0].to_primitive(update)
758 } else {
759 self.parts.to_primitive(update)
760 }
761 }
762}
763
764#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
765pub enum Winding {
766 EvenOdd,
767 NonZero
768}
769
770#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
771pub enum LineCap {
772 Butt = 0,
773 Round = 1,
774 Square = 2,
775}
776
777#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
778pub enum LineJoin {
779 Miter = 0,
780 Round = 1,
781 Bevel = 2,
782}
783
784#[cfg(feature = "euclid")]
785pub struct PdfSpace();
786
787#[derive(Debug, Copy, Clone, PartialEq, Default, DataSize)]
788#[repr(C, align(8))]
789pub struct Point {
790 pub x: f32,
791 pub y: f32
792}
793impl Display for Point {
794 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
795 write!(f, "{} {}", self.x, self.y)
796 }
797}
798#[cfg(feature = "euclid")]
799impl Into<euclid::Point2D<f32, PdfSpace>> for Point {
800 fn into(self) -> euclid::Point2D<f32, PdfSpace> {
801 let Point { x, y } = self;
802
803 euclid::Point2D::new(x, y)
804 }
805}
806#[cfg(feature = "euclid")]
807impl From<euclid::Point2D<f32, PdfSpace>> for Point {
808 fn from(from: euclid::Point2D<f32, PdfSpace>) -> Self {
809 let euclid::Point2D { x, y, .. } = from;
810
811 Point { x, y }
812 }
813}
814#[cfg(feature = "euclid")]
815impl Into<euclid::Vector2D<f32, PdfSpace>> for Point {
816 fn into(self) -> euclid::Vector2D<f32, PdfSpace> {
817 let Point { x, y } = self;
818
819 euclid::Vector2D::new(x, y)
820 }
821}
822#[cfg(feature = "euclid")]
823impl From<euclid::Vector2D<f32, PdfSpace>> for Point {
824 fn from(from: euclid::Vector2D<f32, PdfSpace>) -> Self {
825 let euclid::Vector2D { x, y, .. } = from;
826
827 Point { x, y }
828 }
829}
830
831#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
837#[repr(C, align(8))]
838pub struct ViewRect {
839 pub x: f32,
840 pub y: f32,
841 pub width: f32,
842 pub height: f32,
843}
844
845#[deprecated]
846pub type Rect = ViewRect;
847
848impl Display for ViewRect {
849 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
850 write!(f, "{} {} {} {}", self.x, self.y, self.width, self.height)
851 }
852}
853#[cfg(feature = "euclid")]
854impl Into<euclid::Box2D<f32, PdfSpace>> for ViewRect {
855 fn into(self) -> euclid::Box2D<f32, PdfSpace> {
856 let ViewRect { x, y, width, height } = self;
857
858 assert!(width > 0.0);
859 assert!(height > 0.0);
860
861 euclid::Box2D::new(euclid::Point2D::new(x, y), euclid::Point2D::new(x + width, y + height))
862 }
863}
864#[cfg(feature = "euclid")]
865impl From<euclid::Box2D<f32, PdfSpace>> for ViewRect {
866 fn from(from: euclid::Box2D<f32, PdfSpace>) -> Self {
867 let euclid::Box2D { min: euclid::Point2D { x, y, .. }, max: euclid::Point2D { x: x2, y: y2, .. }, .. } = from;
868
869 assert!(x < x2);
870 assert!(y < y2);
871
872 ViewRect {
873 x, y, width: x2 - x, height: y2 - y
874 }
875 }
876}
877
878#[derive(Debug, Copy, Clone, PartialEq, DataSize, DeepClone)]
879#[repr(C, align(8))]
880pub struct Matrix {
881 pub a: f32,
882 pub b: f32,
883 pub c: f32,
884 pub d: f32,
885 pub e: f32,
886 pub f: f32,
887}
888impl Display for Matrix {
889 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
890 write!(f, "{} {} {} {} {} {}", self.a, self.b, self.c, self.d, self.e, self.f)
891 }
892}
893impl Default for Matrix {
894 fn default() -> Self {
895 Matrix {
896 a: 1.0,
897 b: 0.0,
898 c: 0.0,
899 d: 1.0,
900 e: 0.0,
901 f: 0.0,
902 }
903 }
904}
905impl Object for Matrix {
906 fn from_primitive(p: Primitive, _resolve: &impl Resolve) -> Result<Self> {
907 matrix(&mut p.into_array()?.into_iter())
908 }
909}
910impl ObjectWrite for Matrix {
911 fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
912 let Matrix { a, b, c, d, e, f } = *self;
913 Primitive::array::<f32, _, _, _>([a, b, c, d, e, f].iter(), update)
914 }
915}
916#[cfg(feature = "euclid")]
917impl Into<euclid::Transform2D<f32, PdfSpace, PdfSpace>> for Matrix {
918 fn into(self) -> euclid::Transform2D<f32, PdfSpace, PdfSpace> {
919 let Matrix { a, b, c, d, e, f} = self;
920
921 euclid::Transform2D::new(a, b, c, d, e, f)
922 }
923}
924#[cfg(feature = "euclid")]
925impl From<euclid::Transform2D<f32, PdfSpace, PdfSpace>> for Matrix {
926 fn from(from: euclid::Transform2D<f32, PdfSpace, PdfSpace>) -> Self {
927 let euclid::Transform2D { m11: a, m12: b, m21: c, m22: d, m31: e, m32: f, .. } = from;
928
929 Matrix {
930 a, b, c, d, e, f
931 }
932 }
933}
934
935#[derive(Debug, Clone, DataSize)]
936pub enum Color {
937 Gray(f32),
938 Rgb(Rgb),
939 Cmyk(Cmyk),
940 Other(Vec<Primitive>),
941}
942impl From<Rgb> for Color {
943 fn from(value: Rgb) -> Self {
944 Color::Rgb(value)
945 }
946}
947impl Rgb {
948 pub fn red() -> Self {
949 Rgb { red: 1.0, green: 0.0, blue: 0.0 }
950 }
951 pub fn green() -> Self {
952 Rgb { red: 0.0, green: 1.0, blue: 0.0 }
953 }
954 pub fn blue() -> Self {
955 Rgb { red: 0.0, green: 0.0, blue: 1.0 }
956 }
957}
958
959#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
960pub enum TextMode {
961 Fill,
962 Stroke,
963 FillThenStroke,
964 Invisible,
965 FillAndClip,
966 StrokeAndClip
967}
968
969#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
970pub struct Rgb {
971 pub red: f32,
972 pub green: f32,
973 pub blue: f32,
974}
975impl Display for Rgb {
976 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
977 write!(f, "{} {} {}", self.red, self.green, self.blue)
978 }
979}
980
981impl Object for Color {
982 fn from_primitive(p: Primitive, _resolve: &impl Resolve) -> Result<Self> {
983 let arr = p.as_array()?;
984 match arr {
985 [g] => Ok(Color::Gray(g.as_number()?)),
986 [r, g, b] => Ok(Color::Rgb(Rgb { red: r.as_number()?, green: g.as_number()?, blue: b.as_number()?})),
987 [c, m, y, k] => Ok(Color::Cmyk(Cmyk { cyan: c.as_number()?, magenta: m.as_number()?, yellow: y.as_number()?, key: k.as_number()? })),
988 list => Ok(Color::Other(list.into()))
989 }
990 }
991}
992impl ObjectWrite for Color {
993 fn to_primitive(&self, _update: &mut impl Updater) -> Result<Primitive> {
994 let parts = match *self {
995 Color::Gray(g) => vec![Primitive::Number(g)],
996 Color::Rgb(Rgb { red, green, blue }) => vec![Primitive::Number(red), Primitive::Number(green), Primitive::Number(blue)],
997 Color::Cmyk(Cmyk { cyan, magenta, yellow, key }) => vec![Primitive::Number(cyan), Primitive::Number(magenta), Primitive::Number(yellow), Primitive::Number(key)],
998 Color::Other(ref list) => list.clone(),
999 };
1000 Ok(Primitive::Array(parts))
1001 }
1002}
1003
1004#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
1005pub struct Cmyk {
1006 pub cyan: f32,
1007 pub magenta: f32,
1008 pub yellow: f32,
1009 pub key: f32,
1010}
1011impl Display for Cmyk {
1012 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1013 write!(f, "{} {} {} {}", self.cyan, self.magenta, self.yellow, self.key)
1014 }
1015}
1016
1017#[derive(Debug, Clone, DataSize)]
1018pub enum TextDrawAdjusted {
1019 Text(PdfString),
1020 Spacing(f32),
1021}
1022
1023impl Display for TextDrawAdjusted {
1024 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1025 match self {
1026 Self::Text(text) => write!(f, "{:?}", text),
1027 Self::Spacing(spacing) => spacing.fmt(f),
1028 }
1029 }
1030}
1031
1032#[derive(Debug, Clone, DataSize)]
1036pub enum Op {
1037 BeginMarkedContent { tag: Name, properties: Option<Primitive> },
1043
1044 EndMarkedContent,
1050
1051 MarkedContentPoint { tag: Name, properties: Option<Primitive> },
1055
1056
1057 Close,
1058 MoveTo { p: Point },
1059 LineTo { p: Point },
1060 CurveTo { c1: Point, c2: Point, p: Point },
1061 Rect { rect: ViewRect },
1062 EndPath,
1063
1064 Stroke,
1065
1066 FillAndStroke { winding: Winding },
1071
1072
1073 Fill { winding: Winding },
1074
1075 Shade { name: Name },
1079
1080 Clip { winding: Winding },
1081
1082 Save,
1083 Restore,
1084
1085 Transform { matrix: Matrix },
1086
1087 LineWidth { width: f32 },
1088 Dash { pattern: Vec<f32>, phase: f32 },
1089 LineJoin { join: LineJoin },
1090 LineCap { cap: LineCap },
1091 MiterLimit { limit: f32 },
1092 Flatness { tolerance: f32 },
1093
1094 GraphicsState { name: Name },
1095
1096 StrokeColor { color: Color },
1097 FillColor { color: Color },
1098
1099 FillColorSpace { name: Name },
1100 StrokeColorSpace { name: Name },
1101
1102 RenderingIntent { intent: RenderingIntent },
1103
1104 BeginText,
1105 EndText,
1106
1107 CharSpacing { char_space: f32 },
1108 WordSpacing { word_space: f32 },
1109 TextScaling { horiz_scale: f32 },
1110 Leading { leading: f32 },
1111 TextFont { name: Name, size: f32 },
1112 TextRenderMode { mode: TextMode },
1113
1114 TextRise { rise: f32 },
1116
1117 MoveTextPosition { translation: Point },
1119
1120 SetTextMatrix { matrix: Matrix },
1122
1123 TextNewline,
1125
1126 TextDraw { text: PdfString },
1128
1129 TextDrawAdjusted { array: Vec<TextDrawAdjusted> },
1130
1131 XObject { name: Name },
1132
1133 InlineImage { image: Arc<ImageXObject> },
1134}
1135
1136pub fn deep_clone_op(op: &Op, cloner: &mut impl Cloner, old_resources: &Resources, resources: &mut Resources) -> Result<Op> {
1137 match *op {
1138 Op::GraphicsState { ref name } => {
1139 if !resources.graphics_states.contains_key(name) {
1140 if let Some(gs) = old_resources.graphics_states.get(name) {
1141 resources.graphics_states.insert(name.clone(), gs.deep_clone(cloner)?);
1142 }
1143 }
1144 Ok(Op::GraphicsState { name: name.clone() })
1145 }
1146 Op::MarkedContentPoint { ref tag, ref properties } => {
1147 Ok(Op::MarkedContentPoint { tag: tag.clone(), properties: properties.deep_clone(cloner)? })
1148 }
1149 Op::BeginMarkedContent { ref tag, ref properties } => {
1150 Ok(Op::BeginMarkedContent { tag: tag.clone(), properties: properties.deep_clone(cloner)? })
1151 }
1152 Op::TextFont { ref name, size } => {
1153 if !resources.fonts.contains_key(name) {
1154 if let Some(f) = old_resources.fonts.get(name) {
1155 resources.fonts.insert(name.clone(), f.deep_clone(cloner)?);
1156 }
1157 }
1158 Ok(Op::TextFont { name: name.clone(), size })
1159 }
1160 Op::XObject { ref name } => {
1161 if !resources.xobjects.contains_key(name) {
1162 if let Some(xo) = old_resources.xobjects.get(name) {
1163 resources.xobjects.insert(name.clone(), xo.deep_clone(cloner)?);
1164 }
1165 }
1166 Ok(Op::XObject { name: name.clone() })
1167 }
1168 ref op => Ok(op.clone())
1169 }
1170}
1171
1172
1173#[cfg(test)]
1174mod tests {
1175 use super::*;
1176
1177 #[test]
1178 fn test_inline_image() {
1179 let data = br###"
1180/W 768
1181/H 150
1182/BPC 1
1183/IM true
1184/F [/A85 /Fl]
1185ID
1186Gb"0F_%"1Ö"#B1qiGGG^V6GZ#ZkijB5'RjB4S^5I61&$Ni:Xh=4S_9KYN;c9MUZPn/h,c]oCLUmg*Fo?0Hs0nQHp41KkO\Ls5+g0aoD*btT?l]lq0YAucfaoqHp4
11871KkO\Ls5+g0aoD*btT?l^#mD&ORf[0~>
1188EI
1189"###;
1190 let mut lexer = Lexer::new(data);
1191 assert!(inline_image(&mut lexer, &NoResolve).is_ok());
1192 }
1193}