pdf/
content.rs

1/// PDF content streams.
2use 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/// Represents a PDF content stream - a `Vec` of `Operator`s
17#[derive(Debug, Clone, DataSize)]
18pub struct Content {
19    /// The raw content stream parts. usually one, but could be any number.
20    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    // find the end before try parsing.
160    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    // ugh
166    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                    // Operand
247                    buffer.push(obj)
248                }
249                Err(e) => {
250                    if e.is_eof() {
251                        break;
252                    }
253                    // It's not an object/operand - treat it as an operator.
254                    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    /// Convert primitive to Self
496    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    /// Convert primitive to Self
535    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)]  // TODO
551pub 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/// ISO 32000-2:2020(E) Table 58 Pg 186 - ViewRect
832/// Path construction operators - {x y width height re}
833/// Append a rectangle to the current path as a complete
834/// subpath, with lower-left corner (x, y) and dimensions
835/// width and height in user space.
836#[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/// Graphics Operator
994/// 
995/// See PDF32000 A.2
996#[derive(Debug, Clone, DataSize)]
997pub enum Op {
998    /// Begin a marked comtent sequence
999    /// 
1000    /// Pairs with the following EndMarkedContent.
1001    /// 
1002    /// generated by operators `BMC` and `BDC`
1003    BeginMarkedContent { tag: Name, properties: Option<Primitive> },
1004
1005    /// End a marked content sequence.
1006    /// 
1007    /// Pairs with the previous BeginMarkedContent.
1008    /// 
1009    /// generated by operator `EMC`
1010    EndMarkedContent,
1011
1012    /// A marked content point.
1013    /// 
1014    /// generated by operators `MP` and `DP`.
1015    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    /// Fill and Stroke operation
1028    /// 
1029    /// generated by operators `b`, `B`, `b*`, `B*`
1030    /// `close` indicates whether the path should be closed first
1031    FillAndStroke { winding: Winding },
1032
1033
1034    Fill { winding: Winding },
1035
1036    /// Fill using the named shading pattern
1037    /// 
1038    /// operator: `sh`
1039    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    /// `Ts`
1076    TextRise { rise: f32 },
1077
1078    /// `Td`, `TD`
1079    MoveTextPosition { translation: Point },
1080
1081    /// `Tm`
1082    SetTextMatrix { matrix: Matrix },
1083
1084    /// `T*`
1085    TextNewline,
1086
1087    /// `Tj`
1088    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&#XD6"#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}