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}