pdf_interpret/
soft_mask.rs1use crate::cache::Cache;
2use crate::color::{Color, ColorComponents, ColorSpace};
3use crate::context::Context;
4use crate::device::Device;
5use crate::function::Function;
6use crate::interpret::state::State;
7use crate::util::hash128;
8use crate::x_object::{FormXObject, draw_form_xobject};
9use crate::{CacheKey, InterpreterSettings};
10use kurbo::Affine;
11use pdf_syntax::object::Name;
12use pdf_syntax::object::ObjectIdentifier;
13use pdf_syntax::object::Stream;
14use pdf_syntax::object::dict::keys::*;
15use pdf_syntax::object::{Dict, Object};
16use pdf_syntax::page::Resources;
17use pdf_syntax::xref::XRef;
18use smallvec::smallvec;
19use std::fmt::Debug;
20use std::hash::{Hash, Hasher};
21use std::ops::Deref;
22use std::sync::Arc;
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
26pub enum MaskType {
27 Luminosity,
29 Alpha,
31}
32
33pub struct TransferFunction(Function);
35
36impl TransferFunction {
37 #[inline]
42 pub fn apply(&self, val: f32) -> f32 {
43 self.0
44 .eval(smallvec![val])
45 .and_then(|v| v.first().copied())
46 .unwrap_or(0.0)
47 .clamp(0.0, 1.0)
48 }
49}
50
51struct Repr<'a> {
52 obj_id: ObjectIdentifier,
53 group: FormXObject<'a>,
54 mask_type: MaskType,
55 parent_resources: Resources<'a>,
56 root_transform: Affine,
57 bbox: kurbo::Rect,
58 object_cache: Cache,
59 transfer_function: Option<TransferFunction>,
60 settings: InterpreterSettings,
61 background: Color,
62 xref: &'a XRef,
63}
64
65impl Hash for Repr<'_> {
66 fn hash<H: Hasher>(&self, state: &mut H) {
67 self.obj_id.hash(state);
68 self.root_transform.cache_key().hash(state);
69 }
70}
71
72#[derive(Clone, Hash)]
74pub struct SoftMask<'a>(Arc<Repr<'a>>);
75
76impl Debug for SoftMask<'_> {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 write!(f, "SoftMask({:?})", self.0.obj_id)
79 }
80}
81
82impl PartialEq for SoftMask<'_> {
83 fn eq(&self, other: &Self) -> bool {
84 self.0.obj_id == other.0.obj_id
85 }
86}
87
88impl Eq for SoftMask<'_> {}
89
90impl CacheKey for SoftMask<'_> {
91 fn cache_key(&self) -> u128 {
92 hash128(self)
93 }
94}
95
96impl<'a> SoftMask<'a> {
97 pub(crate) fn new(
98 dict: &Dict<'a>,
99 context: &Context<'a>,
100 parent_resources: Resources<'a>,
101 ) -> Option<Self> {
102 let obj_id = dict.get_ref(G)?.into();
105 let group_stream = dict.get::<Stream<'_>>(G)?;
106 let group = FormXObject::new(&group_stream, &context.settings.warning_sink)?;
107 let cs = ColorSpace::new(
108 group.dict.get::<Dict<'_>>(GROUP)?.get::<Object<'_>>(CS)?,
109 &context.object_cache,
110 &context.settings.warning_sink,
111 )?;
112 let transfer_function = dict
113 .get::<Object<'_>>(TR)
114 .and_then(|o| Function::new(&o))
115 .map(TransferFunction);
116 let (mask_type, background) = match dict.get::<Name>(S)?.deref() {
117 LUMINOSITY => {
118 let color = dict
119 .get::<ColorComponents>(BC)
120 .map(|c| Color::new(cs, c, 1.0))
121 .unwrap_or(Color::new(ColorSpace::device_gray(), smallvec![0.0], 1.0));
122
123 (MaskType::Luminosity, color)
124 }
125 ALPHA => (
126 MaskType::Alpha,
127 Color::new(ColorSpace::device_gray(), smallvec![0.0], 1.0),
129 ),
130 _ => return None,
131 };
132
133 Some(Self(Arc::new(Repr {
134 obj_id,
135 group,
136 mask_type,
137 root_transform: context.get().ctm,
138 transfer_function,
139 bbox: context.bbox(),
140 object_cache: context.object_cache.clone(),
141 settings: context.settings.clone(),
142 xref: context.xref,
143 background,
144 parent_resources,
145 })))
146 }
147
148 pub fn interpret(&self, device: &mut impl Device<'a>) {
150 let state = State::new(self.0.root_transform);
151 let mut ctx = Context::new_with(
152 self.0.root_transform,
153 self.0.bbox,
154 self.0.object_cache.clone(),
155 self.0.xref,
156 self.0.settings.clone(),
157 state,
158 );
159 draw_form_xobject(&self.0.parent_resources, &self.0.group, &mut ctx, device);
160 }
161
162 pub fn id(&self) -> ObjectIdentifier {
166 self.0.obj_id
167 }
168
169 pub fn mask_type(&self) -> MaskType {
171 self.0.mask_type
172 }
173
174 pub fn background_color(&self) -> Color {
176 self.0.background.clone()
177 }
178
179 pub fn transfer_function(&self) -> Option<&TransferFunction> {
181 self.0.transfer_function.as_ref()
182 }
183}