use std::cell::OnceCell;
use std::num::NonZeroU32;
use std::ops::DerefMut;
use pdf_writer::types::TabOrder;
use pdf_writer::writers::NumberTree;
use pdf_writer::{Chunk, Finish, Ref, TextStr};
use crate::configure::PdfVersion;
use crate::content::ContentBuilder;
use crate::error::KrillaResult;
use crate::geom::{Rect, Size, Transform};
use crate::interactive::annotation::Annotation;
use crate::interchange::tagging::{Identifier, PageTagIdentifier};
use crate::resource::ResourceDictionary;
use crate::serialize::{PageInfo, SerializeContext};
use crate::stream::{FilterStreamBuilder, Stream};
use crate::surface::Surface;
use crate::tagging::AnnotationIdentifier;
use crate::util::Deferred;
#[derive(Clone, Debug)]
pub struct PageSettings {
media_box: Option<Rect>,
page_label: PageLabel,
surface_size: Size,
crop_box: Option<Rect>,
bleed_box: Option<Rect>,
trim_box: Option<Rect>,
art_box: Option<Rect>,
}
impl PageSettings {
pub fn new(size: Size) -> Self {
Self {
media_box: Some(Rect::from_xywh(0.0, 0.0, size.width(), size.height()).unwrap()),
surface_size: size,
..Default::default()
}
}
pub fn from_wh(width: f32, height: f32) -> Option<Self> {
Some(Self::new(Size::from_wh(width, height)?))
}
pub fn with_media_box(mut self, media_box: Option<Rect>) -> PageSettings {
self.media_box = media_box;
self
}
pub fn with_page_label(mut self, page_label: PageLabel) -> PageSettings {
self.page_label = page_label;
self
}
pub(crate) fn media_box(&self) -> Option<Rect> {
self.media_box
}
pub(crate) fn surface_size(&self) -> Size {
self.surface_size
}
pub(crate) fn page_label(&self) -> &PageLabel {
&self.page_label
}
pub fn with_crop_box(mut self, crop_box: Option<Rect>) -> PageSettings {
self.crop_box = crop_box;
self
}
pub(crate) fn crop_box(&self) -> Option<Rect> {
self.crop_box
}
pub fn with_bleed_box(mut self, bleed_box: Option<Rect>) -> PageSettings {
self.bleed_box = bleed_box;
self
}
pub(crate) fn bleed_box(&self) -> Option<Rect> {
self.bleed_box
}
pub fn with_trim_box(mut self, trim_box: Option<Rect>) -> PageSettings {
self.trim_box = trim_box;
self
}
pub(crate) fn trim_box(&self) -> Option<Rect> {
self.trim_box
}
pub fn with_art_box(mut self, art_box: Option<Rect>) -> PageSettings {
self.art_box = art_box;
self
}
pub(crate) fn art_box(&self) -> Option<Rect> {
self.art_box
}
}
impl Default for PageSettings {
fn default() -> Self {
let width = 595.0;
let height = 842.0;
Self {
media_box: Some(Rect::from_xywh(0.0, 0.0, width, height).unwrap()),
surface_size: Size::from_wh(width, height).unwrap(),
page_label: PageLabel::default(),
crop_box: None,
bleed_box: None,
trim_box: None,
art_box: None,
}
}
}
pub struct Page<'a> {
sc: &'a mut SerializeContext,
page_settings: PageSettings,
page_index: usize,
page_stream: Stream,
num_mcids: i32,
annotations: Vec<Annotation>,
}
impl<'a> Page<'a> {
pub(crate) fn new(
sc: &'a mut SerializeContext,
page_index: usize,
page_settings: PageSettings,
) -> Self {
Self {
sc,
page_settings,
page_index,
num_mcids: 0,
page_stream: Stream::empty(),
annotations: vec![],
}
}
pub(crate) fn root_transform(&self) -> Transform {
page_root_transform(self.page_settings.surface_size().height())
}
pub fn add_annotation(&mut self, annotation: Annotation) {
self.annotations.push(annotation);
}
pub fn add_tagged_annotation(&mut self, mut annotation: Annotation) -> Identifier {
let annot_index = self.annotations.len();
let ai = AnnotationIdentifier::new(self.page_index, annot_index);
let struct_parent = self.sc.register_annotation_parent(ai);
annotation.struct_parent = struct_parent;
self.add_annotation(annotation);
match struct_parent {
None => Identifier::dummy(),
Some(_) => Identifier::new_annotation(self.page_index, annot_index),
}
}
pub fn surface(&mut self) -> Surface<'_> {
let root_builder = ContentBuilder::new(
self.root_transform(),
self.page_settings.media_box.is_none(),
);
let finish_fn = Box::new(|stream, num_mcids| {
self.page_stream = stream;
self.num_mcids = num_mcids;
});
let page_identifier = if self.sc.serialize_settings().enable_tagging {
Some(PageTagIdentifier::new(self.page_index, 0))
} else {
None
};
Surface::new(self.sc, root_builder, page_identifier, finish_fn)
}
pub fn finish(self) {}
}
pub(crate) fn page_root_transform(height: f32) -> Transform {
Transform::from_row(1.0, 0.0, 0.0, -1.0, 0.0, height)
}
impl Drop for Page<'_> {
fn drop(&mut self) {
let annotations = std::mem::take(&mut self.annotations);
let page_settings = std::mem::take(&mut self.page_settings);
let struct_parent = self
.sc
.register_page_struct_parent(self.page_index, self.num_mcids);
let stream = std::mem::replace(&mut self.page_stream, Stream::empty());
let page = InternalPage::new(
stream,
self.sc,
annotations,
struct_parent,
page_settings,
self.page_index,
);
self.sc.register_page(page);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum NumberingStyle {
Arabic,
LowerRoman,
UpperRoman,
LowerAlpha,
UpperAlpha,
}
impl NumberingStyle {
fn to_pdf(self) -> pdf_writer::types::NumberingStyle {
match self {
NumberingStyle::Arabic => pdf_writer::types::NumberingStyle::Arabic,
NumberingStyle::LowerRoman => pdf_writer::types::NumberingStyle::LowerRoman,
NumberingStyle::UpperRoman => pdf_writer::types::NumberingStyle::UpperRoman,
NumberingStyle::LowerAlpha => pdf_writer::types::NumberingStyle::LowerAlpha,
NumberingStyle::UpperAlpha => pdf_writer::types::NumberingStyle::UpperAlpha,
}
}
}
pub(crate) struct InternalPage {
pub stream_ref: Ref,
pub stream_resources: ResourceDictionary,
pub stream_chunk: Deferred<Chunk>,
pub page_settings: PageSettings,
pub page_index: usize,
pub struct_parent: Option<i32>,
pub bbox: Rect,
pub annotations: Vec<Annotation>,
}
impl InternalPage {
pub(crate) fn new(
mut stream: Stream,
sc: &mut SerializeContext,
annotations: Vec<Annotation>,
struct_parent: Option<i32>,
page_settings: PageSettings,
page_index: usize,
) -> Self {
for validation_error in stream.validation_errors {
sc.register_validation_error(validation_error)
}
let stream_ref = sc.new_ref();
let serialize_settings = sc.serialize_settings().clone();
let stream_resources = std::mem::take(&mut stream.resource_dictionary);
let stream_chunk = Deferred::new(move || {
let mut chunk = Chunk::new();
let page_stream =
FilterStreamBuilder::new_from_content_stream(&stream.content, &serialize_settings)
.finish(&serialize_settings.clone());
let mut stream = chunk.stream(stream_ref, page_stream.encoded_data());
page_stream.write_filters(stream.deref_mut());
stream.finish();
chunk
});
Self {
stream_resources,
stream_ref,
stream_chunk,
struct_parent,
bbox: stream.bbox,
annotations,
page_settings,
page_index,
}
}
pub(crate) fn serialize(
self,
sc: &mut SerializeContext,
root_ref: Ref,
) -> KrillaResult<Deferred<Chunk>> {
let mut chunk = Chunk::new();
let mut annotation_refs = vec![];
if !self.annotations.is_empty() {
for annotation in &self.annotations {
let annot_ref = sc.new_ref();
let a = annotation.serialize(
sc,
annot_ref,
self.page_settings.surface_size().height(),
)?;
chunk.extend(&a);
annotation_refs.push((annot_ref, OnceCell::new()));
}
}
let mut page = chunk.page(root_ref);
self.stream_resources
.to_pdf_resources(&mut page, sc.serialize_settings().pdf_version());
let transform_rect = |rect: Rect| {
rect.transform(page_root_transform(
self.page_settings.surface_size().height(),
))
.unwrap()
};
let media_box = transform_rect(self.page_settings.media_box().unwrap_or(self.bbox));
page.media_box(media_box.to_pdf_rect());
if let Some(crop_box) = self.page_settings.crop_box() {
let crop_box = transform_rect(crop_box);
page.crop_box(crop_box.to_pdf_rect());
}
if let Some(bleed_box) = self.page_settings.bleed_box() {
let bleed_box = transform_rect(bleed_box);
page.bleed_box(bleed_box.to_pdf_rect());
}
if let Some(trim_box) = self.page_settings.trim_box() {
let trim_box = transform_rect(trim_box);
page.trim_box(trim_box.to_pdf_rect());
}
if let Some(art_box) = self.page_settings.art_box() {
let art_box = transform_rect(art_box);
page.art_box(art_box.to_pdf_rect());
}
if let Some(struct_parent) = self.struct_parent {
page.struct_parents(struct_parent);
}
if !self.annotations.is_empty()
&& sc.serialize_settings().enable_tagging
&& sc.serialize_settings().pdf_version() >= PdfVersion::Pdf15
{
page.tab_order(TabOrder::StructureOrder);
}
page.parent(sc.page_tree_ref());
page.contents(self.stream_ref);
if !annotation_refs.is_empty() {
page.annotations(annotation_refs.iter().map(|(r, _)| *r));
}
let PageInfo::Krilla { annotations, .. } = &mut sc.page_infos_mut()[self.page_index] else {
unreachable!()
};
*annotations = annotation_refs;
page.finish();
Ok(Deferred::new(move || {
chunk.extend(self.stream_chunk.wait());
chunk
}))
}
}
#[derive(Debug, Hash, Eq, PartialEq, Default, Clone)]
pub struct PageLabel {
pub(crate) style: Option<NumberingStyle>,
pub(crate) prefix: Option<String>,
pub(crate) offset: Option<NonZeroU32>,
}
impl PageLabel {
pub fn new(
style: Option<NumberingStyle>,
prefix: Option<String>,
offset: Option<NonZeroU32>,
) -> Self {
Self {
style,
prefix,
offset,
}
}
pub(crate) fn is_empty(&self) -> bool {
self.style.is_none() && self.prefix.is_none() && self.offset.is_none()
}
pub(crate) fn serialize(&self, root_ref: Ref) -> Chunk {
let mut chunk = Chunk::new();
let mut label = chunk
.indirect(root_ref)
.start::<pdf_writer::writers::PageLabel>();
if let Some(style) = self.style {
label.style(style.to_pdf());
}
if let Some(prefix) = &self.prefix {
label.prefix(TextStr(prefix));
}
if let Some(offset) = self.offset.and_then(|o| i32::try_from(o.get()).ok()) {
label.offset(offset);
}
label.finish();
chunk
}
}
#[derive(Hash)]
pub(crate) struct PageLabelContainer<'a> {
labels: &'a [PageLabel],
}
impl<'a> PageLabelContainer<'a> {
pub(crate) fn new(labels: &'a [PageLabel]) -> Option<Self> {
if labels.iter().all(|f| f.is_empty()) {
None
} else {
Some(PageLabelContainer { labels })
}
}
pub(crate) fn serialize(&self, sc: &mut SerializeContext, root_ref: Ref) -> Chunk {
let mut filtered_entries = vec![];
let mut prev: Option<PageLabel> = None;
for (i, label) in self.labels.iter().enumerate() {
if let Some(n_prev) = &prev {
if n_prev.style != label.style
|| n_prev.prefix != label.prefix
|| n_prev.offset.map(|n| n.get()) != label.offset.map(|n| n.get() + 1)
{
filtered_entries.push((i, label.clone()));
prev = Some(label.clone());
}
} else {
filtered_entries.push((i, label.clone()));
prev = Some(label.clone());
}
}
let mut chunk = Chunk::new();
let mut num_tree = chunk.indirect(root_ref).start::<NumberTree<Ref>>();
let mut nums = num_tree.nums();
for (page_num, label) in filtered_entries {
let label_ref = sc.register_page_label(label);
nums.insert(page_num as i32, label_ref);
}
nums.finish();
num_tree.finish();
chunk
}
}