krilla/graphics/
xobject.rs

1use std::ops::DerefMut;
2use std::sync::Arc;
3
4use pdf_writer::{Chunk, Finish, Name, Ref};
5
6use crate::chunk_container::ChunkContainerFn;
7use crate::configure::ValidationError;
8use crate::geom::Rect;
9use crate::graphics::color::{rgb, DEVICE_RGB};
10use crate::resource;
11use crate::resource::{Resource, Resourceable};
12use crate::serialize::{Cacheable, MaybeDeviceColorSpace, SerializeContext};
13use crate::stream::{FilterStreamBuilder, Stream};
14use crate::util::{Deferred, NameExt, Prehashed};
15
16#[derive(Debug, Hash, Eq, PartialEq)]
17struct Repr {
18    stream: Stream,
19    isolated: bool,
20    transparency_group_color_space: bool,
21    custom_bbox: Option<Rect>,
22}
23
24#[derive(Debug, Hash, Clone, Eq, PartialEq)]
25pub(crate) struct XObject(Arc<Prehashed<Repr>>);
26
27impl XObject {
28    pub(crate) fn new(
29        stream: Stream,
30        isolated: bool,
31        transparency_group_color_space: bool,
32        custom_bbox: Option<Rect>,
33    ) -> Self {
34        Self(Arc::new(Prehashed::new(Repr {
35            stream,
36            isolated,
37            transparency_group_color_space,
38            custom_bbox,
39        })))
40    }
41
42    pub(crate) fn is_empty(&self) -> bool {
43        self.0.stream.is_empty()
44    }
45
46    pub(crate) fn bbox(&self) -> Rect {
47        self.0.custom_bbox.unwrap_or(self.0.stream.bbox)
48    }
49}
50
51impl Cacheable for XObject {
52    fn chunk_container(&self) -> ChunkContainerFn {
53        |cc| &mut cc.x_objects
54    }
55
56    fn serialize(self, sc: &mut SerializeContext, root_ref: Ref) -> Deferred<Chunk> {
57        let mut chunk = Chunk::new();
58
59        for validation_error in &self.0.stream.validation_errors {
60            sc.register_validation_error(validation_error.clone());
61        }
62
63        if self.0.isolated || self.0.transparency_group_color_space {
64            sc.register_validation_error(ValidationError::Transparency(sc.location));
65        }
66
67        let serialize_settings = sc.serialize_settings();
68
69        let transparency_group_cs = if self.0.transparency_group_color_space {
70            Some(sc.register_colorspace(rgb::color_space(serialize_settings.no_device_cs)))
71        } else {
72            None
73        };
74
75        Deferred::new(move || {
76            let x_object_stream = FilterStreamBuilder::new_from_content_stream(
77                &self.0.stream.content,
78                &serialize_settings,
79            )
80            .finish(&serialize_settings);
81            let mut x_object = chunk.form_xobject(root_ref, x_object_stream.encoded_data());
82            x_object_stream.write_filters(x_object.deref_mut().deref_mut());
83
84            self.0
85                .stream
86                .resource_dictionary
87                .to_pdf_resources(&mut x_object, serialize_settings.pdf_version());
88            x_object.bbox(
89                self.0
90                    .custom_bbox
91                    .unwrap_or(self.0.stream.bbox)
92                    .to_pdf_rect(),
93            );
94
95            if self.0.isolated || self.0.transparency_group_color_space {
96                let mut group = x_object.group();
97                let transparency = group.transparency();
98
99                if self.0.isolated {
100                    transparency.isolated(self.0.isolated);
101                }
102
103                if let Some(transparency_group_cs) = transparency_group_cs {
104                    let pdf_cs = transparency.insert(Name(b"CS"));
105
106                    match transparency_group_cs {
107                        MaybeDeviceColorSpace::DeviceRgb => {
108                            pdf_cs.primitive(DEVICE_RGB.to_pdf_name())
109                        }
110                        // Can only be SRGB
111                        MaybeDeviceColorSpace::ColorSpace(cs) => pdf_cs.primitive(cs.get_ref()),
112                        _ => unreachable!(),
113                    }
114                }
115
116                transparency.finish();
117                group.finish();
118            }
119
120            x_object.finish();
121
122            chunk
123        })
124    }
125}
126
127impl Resourceable for XObject {
128    type Resource = resource::XObject;
129}