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, DataSize)]
18pub struct Content {
19 pub parts: Vec<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<ContentStream> = vec![];
499
500 match p {
501 Primitive::Array(arr) => {
502 for p in arr {
503 let part = t!(ContentStream::from_primitive(p, resolve));
504 parts.push(part);
505 }
506 }
507 Primitive::Reference(r) => return Self::from_primitive(t!(resolve.resolve(r)), resolve),
508 p => {
509 let part = t!(ContentStream::from_primitive(p, resolve));
510 parts.push(part);
511 }
512 }
513
514 Ok(Content { parts })
515 }
516}
517
518#[derive(Debug, DataSize, DeepClone, Clone)]
519pub struct FormXObject {
520 pub stream: Stream<FormDict>,
521}
522impl FormXObject {
523 pub fn dict(&self) -> &FormDict {
524 &self.stream.info.info
525 }
526 pub fn operations(&self, resolve: &impl Resolve) -> Result<Vec<Op>> {
527 let mut ops = OpBuilder::new();
528 let data = self.stream.data(resolve)?;
529 t!(ops.parse(&data, resolve));
530 Ok(ops.ops)
531 }
532}
533impl Object for FormXObject {
534 fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
536 let stream = t!(Stream::<FormDict>::from_primitive(p, resolve));
537 Ok(FormXObject {
538 stream,
539 })
540 }
541}
542impl ObjectWrite for FormXObject {
543 fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
544 let mut stream = self.stream.to_pdf_stream(update)?;
545 stream.info.insert("Subtype", Name::from("Form"));
546 Ok(stream.into())
547 }
548}
549
550#[allow(clippy::float_cmp)] pub fn serialize_ops(mut ops: &[Op]) -> Result<Vec<u8>> {
552 use std::io::Write;
553
554 let mut data = Vec::new();
555 let mut current_point = None;
556 let f = &mut data;
557
558 while ops.len() > 0 {
559 let mut advance = 1;
560 match ops[0] {
561 Op::BeginMarkedContent { ref tag, properties: Some(ref name) } => {
562 serialize_name(tag, f)?;
563 write!(f, " ")?;
564 name.serialize(f)?;
565 writeln!(f, " BDC")?;
566 }
567 Op::BeginMarkedContent { ref tag, properties: None } => {
568 serialize_name(tag, f)?;
569 writeln!(f, " BMC")?;
570 }
571 Op::MarkedContentPoint { ref tag, properties: Some(ref name) } => {
572 serialize_name(tag, f)?;
573 write!(f, " ")?;
574 name.serialize(f)?;
575 writeln!(f, " DP")?;
576 }
577 Op::MarkedContentPoint { ref tag, properties: None } => {
578 serialize_name(tag, f)?;
579 writeln!(f, " MP")?;
580 }
581 Op::EndMarkedContent => writeln!(f, "EMC")?,
582 Op::Close => match ops.get(1) {
583 Some(Op::Stroke) => {
584 writeln!(f, "s")?;
585 advance += 1;
586 }
587 Some(Op::FillAndStroke { winding: Winding::NonZero }) => {
588 writeln!(f, "b")?;
589 advance += 1;
590 }
591 Some(Op::FillAndStroke { winding: Winding::EvenOdd }) => {
592 writeln!(f, "b*")?;
593 advance += 1;
594 }
595 _ => writeln!(f, "h")?,
596 }
597 Op::MoveTo { p } => {
598 writeln!(f, "{} m", p)?;
599 current_point = Some(p);
600 }
601 Op::LineTo { p } => {
602 writeln!(f, "{} l", p)?;
603 current_point = Some(p);
604 },
605 Op::CurveTo { c1, c2, p } => {
606 if Some(c1) == current_point {
607 writeln!(f, "{} {} v", c2, p)?;
608 } else if c2 == p {
609 writeln!(f, "{} {} y", c1, p)?;
610 } else {
611 writeln!(f, "{} {} {} c", c1, c2, p)?;
612 }
613 current_point = Some(p);
614 },
615 Op::Rect { rect } => writeln!(f, "{} re", rect)?,
616 Op::EndPath => writeln!(f, "n")?,
617 Op::Stroke => writeln!(f, "S")?,
618 Op::FillAndStroke { winding: Winding::NonZero } => writeln!(f, "B")?,
619 Op::FillAndStroke { winding: Winding::EvenOdd } => writeln!(f, "B*")?,
620 Op::Fill { winding: Winding::NonZero } => writeln!(f, "f")?,
621 Op::Fill { winding: Winding::EvenOdd } => writeln!(f, "f*")?,
622 Op::Shade { ref name } => {
623 serialize_name(name, f)?;
624 writeln!(f, " sh")?;
625 },
626 Op::Clip { winding: Winding::NonZero } => writeln!(f, "W")?,
627 Op::Clip { winding: Winding::EvenOdd } => writeln!(f, "W*")?,
628 Op::Save => writeln!(f, "q")?,
629 Op::Restore => writeln!(f, "Q")?,
630 Op::Transform { matrix } => writeln!(f, "{} cm", matrix)?,
631 Op::LineWidth { width } => writeln!(f, "{} w", width)?,
632 Op::Dash { ref pattern, phase } => writeln!(f, "[{}] {} d", pattern.iter().format(" "), phase)?,
633 Op::LineJoin { join } => writeln!(f, "{} j", join as u8)?,
634 Op::LineCap { cap } => writeln!(f, "{} J", cap as u8)?,
635 Op::MiterLimit { limit } => writeln!(f, "{} M", limit)?,
636 Op::Flatness { tolerance } => writeln!(f, "{} i", tolerance)?,
637 Op::GraphicsState { ref name } => {
638 serialize_name(name, f)?;
639 writeln!(f, " gs")?;
640 },
641 Op::StrokeColor { color: Color::Gray(g) } => writeln!(f, "{} G", g)?,
642 Op::StrokeColor { color: Color::Rgb(rgb) } => writeln!(f, "{} RG", rgb)?,
643 Op::StrokeColor { color: Color::Cmyk(cmyk) } => writeln!(f, "{} K", cmyk)?,
644 Op::StrokeColor { color: Color::Other(ref args) } => {
645 for p in args {
646 p.serialize(f)?;
647 write!(f, " ")?;
648 }
649 writeln!(f, "SCN")?;
650 }
651 Op::FillColor { color: Color::Gray(g) } => writeln!(f, "{} g", g)?,
652 Op::FillColor { color: Color::Rgb(rgb) } => writeln!(f, "{} rg", rgb)?,
653 Op::FillColor { color: Color::Cmyk(cmyk) } => writeln!(f, "{} k", cmyk)?,
654 Op::FillColor { color: Color::Other(ref args) } => {
655 for p in args {
656 p.serialize(f)?;
657 write!(f, " ")?;
658 }
659 writeln!(f, "scn")?;
660 }
661 Op::FillColorSpace { ref name } => {
662 serialize_name(name, f)?;
663 writeln!(f, " cs")?;
664 },
665 Op::StrokeColorSpace { ref name } => {
666 serialize_name(name, f)?;
667 writeln!(f, " CS")?;
668 },
669
670 Op::RenderingIntent { intent } => writeln!(f, "{} ri", intent.to_str())?,
671 Op::BeginText => writeln!(f, "BT")?,
672 Op::EndText => writeln!(f, "ET")?,
673 Op::CharSpacing { char_space } => writeln!(f, "{} Tc", char_space)?,
674 Op::WordSpacing { word_space } => {
675 if let [
676 Op::CharSpacing { char_space },
677 Op::TextNewline,
678 Op::TextDraw { ref text },
679 ..
680 ] = ops[1..] {
681 write!(f, "{} {} ", word_space, char_space)?;
682 text.serialize(f)?;
683 writeln!(f, " \"")?;
684 advance += 3;
685 } else {
686 writeln!(f, "{} Tw", word_space)?;
687 }
688 }
689 Op::TextScaling { horiz_scale } => writeln!(f, "{} Tz", horiz_scale)?,
690 Op::Leading { leading } => match ops[1..] {
691 [Op::MoveTextPosition { translation }, ..] if leading == -translation.x => {
692 writeln!(f, "{} {} TD", translation.x, translation.y)?;
693 advance += 1;
694 }
695 _ => {
696 writeln!(f, "{} TL", leading)?;
697 }
698 }
699 Op::TextFont { ref name, ref size } => {
700 serialize_name(name, f)?;
701 writeln!(f, " {} Tf", size)?;
702 },
703 Op::TextRenderMode { mode } => writeln!(f, "{} Tr", mode as u8)?,
704 Op::TextRise { rise } => writeln!(f, "{} Ts", rise)?,
705 Op::MoveTextPosition { translation } => writeln!(f, "{} {} Td", translation.x, translation.y)?,
706 Op::SetTextMatrix { matrix } => writeln!(f, "{} Tm", matrix)?,
707 Op::TextNewline => {
708 if let [Op::TextDraw { ref text }, ..] = ops[1..] {
709 text.serialize(f)?;
710 writeln!(f, " '")?;
711 advance += 1;
712 } else {
713 writeln!(f, "T*")?;
714 }
715 },
716 Op::TextDraw { ref text } => {
717 text.serialize(f)?;
718 writeln!(f, " Tj")?;
719 },
720 Op::TextDrawAdjusted { ref array } => {
721 write!(f, "[")?;
722 for (i, val) in array.iter().enumerate() {
723 if i > 0 {
724 write!(f, " ")?;
725 }
726 match val {
727 TextDrawAdjusted::Spacing(s) => write!(f, "{s}")?,
728 TextDrawAdjusted::Text(data) => data.serialize(f)?,
729 }
730 }
731 writeln!(f, "] TJ")?;
732 },
733 Op::InlineImage { image: _ } => unimplemented!(),
734 Op::XObject { ref name } => {
735 serialize_name(name, f)?;
736 writeln!(f, " Do")?;
737 },
738 }
739 ops = &ops[advance..];
740 }
741 Ok(data)
742}
743
744impl Content {
745 pub fn from_ops(operations: Vec<Op>) -> Self {
746 let data = serialize_ops(&operations).unwrap();
747 Content {
748 parts: vec![Stream::new((), data)]
749 }
750 }
751}
752
753impl ObjectWrite for Content {
754 fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
755 if self.parts.len() == 1 {
756 let obj = self.parts[0].to_primitive(update)?;
757 update.create(obj)?.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}
942
943#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
944pub enum TextMode {
945 Fill,
946 Stroke,
947 FillThenStroke,
948 Invisible,
949 FillAndClip,
950 StrokeAndClip
951}
952
953#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
954pub struct Rgb {
955 pub red: f32,
956 pub green: f32,
957 pub blue: f32,
958}
959impl Display for Rgb {
960 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
961 write!(f, "{} {} {}", self.red, self.green, self.blue)
962 }
963}
964
965#[derive(Debug, Copy, Clone, PartialEq, DataSize)]
966pub struct Cmyk {
967 pub cyan: f32,
968 pub magenta: f32,
969 pub yellow: f32,
970 pub key: f32,
971}
972impl Display for Cmyk {
973 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
974 write!(f, "{} {} {} {}", self.cyan, self.magenta, self.yellow, self.key)
975 }
976}
977
978#[derive(Debug, Clone, DataSize)]
979pub enum TextDrawAdjusted {
980 Text(PdfString),
981 Spacing(f32),
982}
983
984impl Display for TextDrawAdjusted {
985 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
986 match self {
987 Self::Text(text) => write!(f, "{:?}", text),
988 Self::Spacing(spacing) => spacing.fmt(f),
989 }
990 }
991}
992
993#[derive(Debug, Clone, DataSize)]
997pub enum Op {
998 BeginMarkedContent { tag: Name, properties: Option<Primitive> },
1004
1005 EndMarkedContent,
1011
1012 MarkedContentPoint { tag: Name, properties: Option<Primitive> },
1016
1017
1018 Close,
1019 MoveTo { p: Point },
1020 LineTo { p: Point },
1021 CurveTo { c1: Point, c2: Point, p: Point },
1022 Rect { rect: ViewRect },
1023 EndPath,
1024
1025 Stroke,
1026
1027 FillAndStroke { winding: Winding },
1032
1033
1034 Fill { winding: Winding },
1035
1036 Shade { name: Name },
1040
1041 Clip { winding: Winding },
1042
1043 Save,
1044 Restore,
1045
1046 Transform { matrix: Matrix },
1047
1048 LineWidth { width: f32 },
1049 Dash { pattern: Vec<f32>, phase: f32 },
1050 LineJoin { join: LineJoin },
1051 LineCap { cap: LineCap },
1052 MiterLimit { limit: f32 },
1053 Flatness { tolerance: f32 },
1054
1055 GraphicsState { name: Name },
1056
1057 StrokeColor { color: Color },
1058 FillColor { color: Color },
1059
1060 FillColorSpace { name: Name },
1061 StrokeColorSpace { name: Name },
1062
1063 RenderingIntent { intent: RenderingIntent },
1064
1065 BeginText,
1066 EndText,
1067
1068 CharSpacing { char_space: f32 },
1069 WordSpacing { word_space: f32 },
1070 TextScaling { horiz_scale: f32 },
1071 Leading { leading: f32 },
1072 TextFont { name: Name, size: f32 },
1073 TextRenderMode { mode: TextMode },
1074
1075 TextRise { rise: f32 },
1077
1078 MoveTextPosition { translation: Point },
1080
1081 SetTextMatrix { matrix: Matrix },
1083
1084 TextNewline,
1086
1087 TextDraw { text: PdfString },
1089
1090 TextDrawAdjusted { array: Vec<TextDrawAdjusted> },
1091
1092 XObject { name: Name },
1093
1094 InlineImage { image: Arc<ImageXObject> },
1095}
1096
1097pub fn deep_clone_op(op: &Op, cloner: &mut impl Cloner, old_resources: &Resources, resources: &mut Resources) -> Result<Op> {
1098 match *op {
1099 Op::GraphicsState { ref name } => {
1100 if !resources.graphics_states.contains_key(name) {
1101 if let Some(gs) = old_resources.graphics_states.get(name) {
1102 resources.graphics_states.insert(name.clone(), gs.deep_clone(cloner)?);
1103 }
1104 }
1105 Ok(Op::GraphicsState { name: name.clone() })
1106 }
1107 Op::MarkedContentPoint { ref tag, ref properties } => {
1108 Ok(Op::MarkedContentPoint { tag: tag.clone(), properties: properties.deep_clone(cloner)? })
1109 }
1110 Op::BeginMarkedContent { ref tag, ref properties } => {
1111 Ok(Op::BeginMarkedContent { tag: tag.clone(), properties: properties.deep_clone(cloner)? })
1112 }
1113 Op::TextFont { ref name, size } => {
1114 if !resources.fonts.contains_key(name) {
1115 if let Some(f) = old_resources.fonts.get(name) {
1116 resources.fonts.insert(name.clone(), f.deep_clone(cloner)?);
1117 }
1118 }
1119 Ok(Op::TextFont { name: name.clone(), size })
1120 }
1121 Op::XObject { ref name } => {
1122 if !resources.xobjects.contains_key(name) {
1123 if let Some(xo) = old_resources.xobjects.get(name) {
1124 resources.xobjects.insert(name.clone(), xo.deep_clone(cloner)?);
1125 }
1126 }
1127 Ok(Op::XObject { name: name.clone() })
1128 }
1129 ref op => Ok(op.clone())
1130 }
1131}
1132
1133
1134#[cfg(test)]
1135mod tests {
1136 use super::*;
1137
1138 #[test]
1139 fn test_inline_image() {
1140 let data = br###"
1141/W 768
1142/H 150
1143/BPC 1
1144/IM true
1145/F [/A85 /Fl]
1146ID
1147Gb"0F_%"1Ö"#B1qiGGG^V6GZ#ZkijB5'RjB4S^5I61&$Ni:Xh=4S_9KYN;c9MUZPn/h,c]oCLUmg*Fo?0Hs0nQHp41KkO\Ls5+g0aoD*btT?l]lq0YAucfaoqHp4
11481KkO\Ls5+g0aoD*btT?l^#mD&ORf[0~>
1149EI
1150"###;
1151 let mut lexer = Lexer::new(data);
1152 assert!(inline_image(&mut lexer, &NoResolve).is_ok());
1153 }
1154}