1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
use crate::{
assert_empty,
data_structures::Rectangle,
error::PdfResult,
objects::{Dictionary, Object},
pdf_enum, Resolve,
};
use self::{
axial::AxialShading, coons_patch_mesh::CoonsPatchMeshShading, freeform::FreeformShading,
function_based::FunctionBasedShading, latticeform::LatticeformShading, radial::RadialShading,
tensor_product_patch_mesh::TensorProductPatchMeshShading,
};
mod axial;
mod coons_patch_mesh;
mod freeform;
mod function_based;
mod latticeform;
mod radial;
mod tensor_product_patch_mesh;
#[derive(Debug)]
pub enum ShadingObject {
Dictionary(ShadingDictionary),
Stream(ShadingStream),
}
impl ShadingObject {
pub fn from_obj(obj: Object, resolver: &mut dyn Resolve) -> PdfResult<Self> {
let obj = resolver.resolve(obj)?;
Ok(
if let Ok(mut stream) = resolver.assert_stream(obj.clone()) {
let dict = &mut stream.dict.other;
todo!("shading stream: {:?}", dict);
} else {
let dict = resolver.assert_dict(obj)?;
ShadingObject::Dictionary(ShadingDictionary::from_dict(dict, resolver)?)
},
)
}
}
#[derive(Debug)]
pub struct ShadingStream;
#[derive(Debug)]
pub struct ShadingDictionary {
base: BaseShadingDictionary,
sub_type: SubtypeShadingDictionary,
}
impl ShadingDictionary {
pub fn from_dict(mut dict: Dictionary, resolver: &mut dyn Resolve) -> PdfResult<Self> {
let base = BaseShadingDictionary::from_dict(&mut dict, resolver)?;
let sub_type = SubtypeShadingDictionary::from_dict(&mut dict, base.shading_type, resolver)?;
assert_empty(dict);
Ok(Self { base, sub_type })
}
}
#[derive(Debug)]
pub enum SubtypeShadingDictionary {
FunctionBased(FunctionBasedShading),
Axial(AxialShading),
Radial(RadialShading),
Freeform(FreeformShading),
Latticeform(LatticeformShading),
CoonsPatchMesh(CoonsPatchMeshShading),
TensorProductPatchMesh(TensorProductPatchMeshShading),
}
impl SubtypeShadingDictionary {
pub fn from_dict(
dict: &mut Dictionary,
sub_type: ShadingType,
resolver: &mut dyn Resolve,
) -> PdfResult<Self> {
Ok(match sub_type {
ShadingType::FunctionBased => SubtypeShadingDictionary::FunctionBased(
FunctionBasedShading::from_dict(dict, resolver)?,
),
ShadingType::Axial => {
SubtypeShadingDictionary::Axial(AxialShading::from_dict(dict, resolver)?)
}
ShadingType::Radial => {
SubtypeShadingDictionary::Radial(RadialShading::from_dict(dict, resolver)?)
}
ShadingType::Freeform => {
SubtypeShadingDictionary::Freeform(FreeformShading::from_dict(dict, resolver)?)
}
ShadingType::Latticeform => SubtypeShadingDictionary::Latticeform(
LatticeformShading::from_dict(dict, resolver)?,
),
ShadingType::CoonsPatchMesh => SubtypeShadingDictionary::CoonsPatchMesh(
CoonsPatchMeshShading::from_dict(dict, resolver)?,
),
ShadingType::TensorProductPatchMesh => {
SubtypeShadingDictionary::TensorProductPatchMesh(
TensorProductPatchMeshShading::from_dict(dict, resolver)?,
)
}
})
}
}
#[derive(Debug)]
pub struct BaseShadingDictionary {
shading_type: ShadingType,
/// The colour space in which colour values shall be expressed. This may be any device,
/// CIE-based, or special colour space except a Pattern space
// todo: actually parse the color space
color_space: Object,
/// An array of colour components appropriate to the colour space, specifying a single
/// background colour value. If present, this colour shall be used, before any painting
/// operation involving the shading, to fill those portions of the area to be painted
/// that lie outside the bounds of the shading object
///
/// In the opaque imaging model, the effect is as if the painting operation were performed
/// twice: first with the background colour and then with the shading
///
/// The background colour is applied only when the shading is used as part of a shading
/// pattern, not when it is painted directly with the sh operator
// todo: better typing based on color space
background: Option<Vec<f32>>,
/// An array of four numbers giving the left, bottom, right, and top coordinates,
/// respectively, of the shading's bounding box. The coordinates shall be interpreted
/// in the shading's target coordinate space. If present, this bounding box shall be
/// applied as a temporary clipping boundary when the shading is painted, in addition
/// to the current clipping path and any other clipping boundaries in effect at that
/// time
bbox: Option<Rectangle>,
/// A flag indicating whether to filter the shading function to prevent aliasing artifacts
///
/// The shading operators sample shading functions at a rate determined by the resolution
/// of the output device. Aliasing can occur if the function is not smooth -- that is, if it
/// has a high spatial frequency relative to the sampling rate. Anti-aliasing can be
/// computationally expensive and is usually unnecessary, since most shading functions are
/// smooth enough or are sampled at a high enough frequency to avoid aliasing effects.
///
/// Anti-aliasing may not be implemented on some output devices, in which case this flag is ignored
///
/// Default value: false
anti_alias: bool,
}
impl BaseShadingDictionary {
pub fn from_dict(dict: &mut Dictionary, resolver: &mut dyn Resolve) -> PdfResult<Self> {
let shading_type =
ShadingType::from_integer(dict.expect_integer("ShadingType", resolver)?)?;
let color_space = dict.expect_object("ColorSpace", resolver)?;
let background = dict
.get_arr("Background", resolver)?
.map(|objs| {
objs.into_iter()
.map(|obj| resolver.assert_number(obj))
.collect()
})
.transpose()?;
let bbox = dict.get_rectangle("BBox", resolver)?;
let anti_alias = dict.get_bool("AntiAlias", resolver)?.unwrap_or(false);
Ok(Self {
shading_type,
color_space,
background,
bbox,
anti_alias,
})
}
}
pdf_enum!(
int
#[derive(Debug, Clone, Copy)]
pub enum ShadingType {
FunctionBased = 1,
Axial = 2,
Radial = 3,
/// Free-form Gouraud-shaded triangle mesh
Freeform = 4,
/// Lattice-form Gouraud-shaded triangle mesh
Latticeform = 5,
CoonsPatchMesh = 6,
TensorProductPatchMesh = 7,
}
);