pdf/object/
color.rs

1use datasize::DataSize;
2use crate as pdf;
3use crate::object::*;
4use crate::error::*;
5
6#[derive(Object, Debug, DataSize, DeepClone, ObjectWrite)]
7pub struct IccInfo {
8    #[pdf(key="N")]
9    pub components: u32,
10
11    #[pdf(key="Alternate")]
12    pub alternate: Option<Box<ColorSpace>>,
13
14    #[pdf(key="Range")]
15    pub range: Option<Vec<f32>>,
16
17    #[pdf(key="Metadata")]
18    pub metadata: Option<Stream<()>>,
19}
20
21#[derive(Debug, Clone, DeepClone)]
22pub enum ColorSpace {
23    DeviceGray,
24    DeviceRGB,
25    DeviceCMYK,
26    DeviceN { names: Vec<Name>, alt: Box<ColorSpace>, tint: Function, attr: Option<Dictionary> },
27    CalGray(Dictionary),
28    CalRGB(Dictionary),
29    CalCMYK(Dictionary),
30    Indexed(Box<ColorSpace>, u8, Arc<[u8]>),
31    Separation(Name, Box<ColorSpace>, Function),
32    Icc(RcRef<Stream<IccInfo>>),
33    Pattern,
34    Named(Name),
35    Other(Vec<Primitive>)
36}
37impl DataSize for ColorSpace {
38    const IS_DYNAMIC: bool = true;
39    const STATIC_HEAP_SIZE: usize = 0;
40
41    #[inline]
42    fn estimate_heap_size(&self) -> usize {
43        match *self {
44            ColorSpace::DeviceGray | ColorSpace::DeviceRGB | ColorSpace::DeviceCMYK => 0,
45            ColorSpace::DeviceN { ref names, ref alt, ref tint, ref attr } => {
46                names.estimate_heap_size() +
47                alt.estimate_heap_size() +
48                tint.estimate_heap_size() +
49                attr.estimate_heap_size()
50            }
51            ColorSpace::CalGray(ref d) | ColorSpace::CalRGB(ref d) | ColorSpace::CalCMYK(ref d) => {
52                d.estimate_heap_size()
53            }
54            ColorSpace::Indexed(ref cs, _, ref data) => {
55                cs.estimate_heap_size() + data.estimate_heap_size()
56            }
57            ColorSpace::Separation(ref name, ref cs, ref f) => {
58                name.estimate_heap_size() + cs.estimate_heap_size() + f.estimate_heap_size()
59            }
60            ColorSpace::Icc(ref s) => s.estimate_heap_size(),
61            ColorSpace::Pattern => 0,
62            ColorSpace::Other(ref v) => v.estimate_heap_size(),
63            ColorSpace::Named(ref n) => n.estimate_heap_size()
64        }
65    }
66}
67
68fn get_index(arr: &[Primitive], idx: usize) -> Result<&Primitive> {
69     arr.get(idx).ok_or(PdfError::Bounds { index: idx, len: arr.len() })
70}
71
72impl Object for ColorSpace {
73    fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<ColorSpace> {
74        ColorSpace::from_primitive_depth(p, resolve, 5)
75    }
76}
77impl ColorSpace {
78    fn from_primitive_depth(p: Primitive, resolve: &impl Resolve, depth: usize) -> Result<ColorSpace> {
79        let p = p.resolve(resolve)?;
80
81        if let Ok(name) = p.as_name() {
82            let cs = match name {
83                "DeviceGray" => ColorSpace::DeviceGray,
84                "DeviceRGB" => ColorSpace::DeviceRGB,
85                "DeviceCMYK" => ColorSpace::DeviceCMYK,
86                "Pattern" => ColorSpace::Pattern,
87                name => ColorSpace::Named(name.into()),
88            };
89            return Ok(cs);
90        }
91        let arr = t!(p.into_array());
92        let typ_p = t!(get_index(&arr, 0)).clone().resolve(resolve)?;
93        let typ = t!(typ_p.as_name());
94        
95        if depth == 0 {
96            bail!("ColorSpace base recursion");
97        }
98        match typ {
99            "Indexed" => {
100                let base = Box::new(t!(ColorSpace::from_primitive_depth(t!(get_index(&arr, 1)).clone(), resolve, depth-1)));
101                let hival = t!(t!(get_index(&arr, 2)).as_u8());
102                let lookup = match t!(get_index(&arr, 3)) {
103                    &Primitive::Reference(r) => resolve.resolve(r)?,
104                    p => p.clone()
105                };
106                let lookup = match lookup {
107                    Primitive::String(string) => {
108                        let data: Vec<u8> = string.into_bytes().into();
109                        data.into()
110                    }
111                    Primitive::Stream(stream) => {
112                        let s: Stream::<()> = Stream::from_stream(stream, resolve)?;
113                        t!(s.data(resolve))
114                    },
115                    p => return Err(PdfError::UnexpectedPrimitive {
116                        expected: "String or Stream",
117                        found: p.get_debug_name()
118                    })
119                };
120                Ok(ColorSpace::Indexed(base, hival, lookup))
121            }
122            "Separation" => {
123                let name = t!(t!(get_index(&arr, 1)).clone().into_name());
124                let alternate = Box::new(t!(ColorSpace::from_primitive_depth(t!(get_index(&arr, 2)).clone(), resolve, depth-1)));
125                let tint = t!(Function::from_primitive(t!(get_index(&arr, 3)).clone(), resolve));
126                Ok(ColorSpace::Separation(name, alternate, tint))
127            }
128            "ICCBased" => {
129                let s = t!(RcRef::from_primitive(t!(get_index(&arr, 1)).clone(), resolve));
130                Ok(ColorSpace::Icc(s))
131            }
132            "DeviceN" => {
133                let names = t!(Object::from_primitive(t!(get_index(&arr, 1)).clone(), resolve));
134                let alt = t!(Object::from_primitive(t!(get_index(&arr, 2)).clone(), resolve));
135                let tint = t!(Function::from_primitive(t!(get_index(&arr, 3)).clone(), resolve));
136                let attr = arr.get(4).map(|p| Dictionary::from_primitive(p.clone(), resolve)).transpose()?;
137
138                Ok(ColorSpace::DeviceN { names, alt, tint, attr})
139            }
140            "CalGray" => {
141                let dict = Dictionary::from_primitive(t!(get_index(&arr, 1)).clone(), resolve)?;
142                Ok(ColorSpace::CalGray(dict))
143            }
144            "CalRGB" => {
145                let dict = Dictionary::from_primitive(t!(get_index(&arr, 1)).clone(), resolve)?;
146                Ok(ColorSpace::CalRGB(dict))
147            }
148            "CalCMYK" => {
149                let dict = Dictionary::from_primitive(t!(get_index(&arr, 1)).clone(), resolve)?;
150                Ok(ColorSpace::CalCMYK(dict))
151            }
152            "Pattern" => {
153                Ok(ColorSpace::Pattern)
154            }
155            _ => Ok(ColorSpace::Other(arr))
156        }
157    }
158}
159impl ObjectWrite for ColorSpace {
160    fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
161        match *self {
162            ColorSpace::DeviceCMYK => Ok(Primitive::name("DeviceCMYK")),
163            ColorSpace::DeviceRGB => Ok(Primitive::name("DeviceRGB")),
164            ColorSpace::Indexed(ref  base, hival, ref lookup) => {
165                let base = base.to_primitive(update)?;
166                let hival = Primitive::Integer(hival.into());
167                let lookup = if lookup.len() < 100 {
168                    PdfString::new((**lookup).into()).into()
169                } else {
170                    Stream::new((), lookup.clone()).to_primitive(update)?
171                };
172                Ok(Primitive::Array(vec![Primitive::name("Indexed"), base, hival, lookup]))
173            }
174            ref p => {
175                dbg!(p);
176                unimplemented!()
177            }
178        }
179    }
180}