krilla/graphics/
xobject.rs1use 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 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}