use pdf_writer::{Chunk, Finish, Name, Ref};
use crate::chunk_container::ChunkContainerFn;
use crate::geom::{Rect, Transform};
use crate::graphics::shading_function::{GradientProperties, ShadingFunction};
use crate::graphics::xobject::XObject;
use crate::resource;
use crate::resource::Resourceable;
use crate::serialize::{Cacheable, SerializeContext};
use crate::stream::Stream;
use crate::stream::StreamBuilder;
use crate::util::Deferred;
#[derive(PartialEq, Eq, Debug, Hash)]
pub struct Mask {
stream: Stream,
mask_type: MaskType,
custom_bbox: Option<Rect>,
}
impl Mask {
pub fn new(stream: Stream, mask_type: MaskType) -> Self {
Self {
stream,
mask_type,
custom_bbox: None,
}
}
pub(crate) fn new_from_shading(
gradient_properties: GradientProperties,
shading_transform: Transform,
bbox: Rect,
serializer_context: &mut SerializeContext,
) -> Option<Self> {
match &gradient_properties {
GradientProperties::RadialAxialGradient(rag) => {
if rag.stops.iter().all(|s| s.opacity.get() == 1.0) {
return None;
}
}
GradientProperties::PostScriptGradient(psg) => {
if psg.stops.iter().all(|s| s.opacity.get() == 1.0) {
return None;
}
}
}
let shading_function = ShadingFunction::new(gradient_properties, true);
let shading_stream = {
let mut builder = StreamBuilder::new(serializer_context);
let mut surface = builder.surface();
surface.push_transform(&shading_transform);
surface.draw_shading(&shading_function);
surface.pop();
surface.finish();
builder.finish()
};
Some(Self {
stream: shading_stream,
mask_type: MaskType::Luminosity,
custom_bbox: Some(bbox),
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum MaskType {
Luminosity,
Alpha,
}
impl MaskType {
pub(crate) fn to_name(self) -> Name<'static> {
match self {
MaskType::Alpha => Name(b"Alpha"),
MaskType::Luminosity => Name(b"Luminosity"),
}
}
}
impl Cacheable for Mask {
fn chunk_container(&self) -> ChunkContainerFn {
|cc| &mut cc.masks
}
fn serialize(self, sc: &mut SerializeContext, root_ref: Ref) -> Deferred<Chunk> {
let mut chunk = Chunk::new();
let x_object =
sc.register_cacheable(XObject::new(self.stream, false, true, self.custom_bbox));
let mut dict = chunk.indirect(root_ref).dict();
dict.pair(Name(b"Type"), Name(b"Mask"));
dict.pair(Name(b"S"), self.mask_type.to_name());
dict.pair(Name(b"G"), x_object);
dict.finish();
Deferred::new(|| chunk)
}
}
impl Resourceable for Mask {
type Resource = resource::XObject;
}