use std::collections::HashMap;
use datasize::DataSize;
use crate as pdf;
use crate::content::deep_clone_op;
use crate::object::*;
use crate::error::*;
use crate::content::{Content, FormXObject, Matrix, parse_ops, serialize_ops, Op};
use crate::font::Font;
use crate::enc::StreamFilter;
#[derive(Debug, Clone, DataSize)]
pub enum PagesNode {
Tree(PageTree),
Leaf(Page),
}
impl Object for PagesNode {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<PagesNode> {
let mut dict = p.resolve(resolve)?.into_dictionary()?;
match dict.require("PagesNode", "Type")?.as_name()? {
"Page" => Ok(PagesNode::Leaf(t!(Page::from_dict(dict, resolve)))),
"Pages" => Ok(PagesNode::Tree(t!(PageTree::from_dict(dict, resolve)))),
other => Err(PdfError::WrongDictionaryType {expected: "Page or Pages".into(), found: other.into()}),
}
}
}
impl ObjectWrite for PagesNode {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
match *self {
PagesNode::Tree(ref t) => t.to_primitive(update),
PagesNode::Leaf(ref l) => l.to_primitive(update),
}
}
}
#[derive(Debug, Clone, DataSize)]
pub struct PageRc(RcRef<PagesNode>);
impl Deref for PageRc {
type Target = Page;
fn deref(&self) -> &Page {
match *self.0 {
PagesNode::Leaf(ref page) => page,
_ => unreachable!()
}
}
}
impl PageRc {
pub fn create(page: Page, update: &mut impl Updater) -> Result<PageRc> {
Ok(PageRc(update.create(PagesNode::Leaf(page))?))
}
pub fn get_ref(&self) -> Ref<PagesNode> {
self.0.get_ref()
}
}
#[derive(Debug, Clone, DataSize)]
pub struct PagesRc(RcRef<PagesNode>);
impl Deref for PagesRc {
type Target = PageTree;
fn deref(&self) -> &PageTree {
match *self.0 {
PagesNode::Tree(ref tree) => tree,
_ => unreachable!()
}
}
}
impl PagesRc {
pub fn create(tree: PageTree, update: &mut impl Updater) -> Result<PagesRc> {
Ok(PagesRc(update.create(PagesNode::Tree(tree))?))
}
}
impl Object for PagesRc {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<PagesRc> {
let node = t!(RcRef::from_primitive(p, resolve));
match *node {
PagesNode::Leaf(_) => Err(PdfError::WrongDictionaryType {expected: "Pages".into(), found: "Page".into()}),
PagesNode::Tree(_) => Ok(PagesRc(node))
}
}
}
impl ObjectWrite for PagesRc {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
self.0.to_primitive(update)
}
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
#[pdf(Type = "Catalog?")]
pub struct Catalog {
#[pdf(key="Version")]
pub version: Option<Name>,
#[pdf(key="Pages")]
pub pages: PagesRc,
#[pdf(key="Names")]
pub names: Option<MaybeRef<NameDictionary>>,
#[pdf(key="Dests")]
pub dests: Option<MaybeRef<Dictionary>>,
#[pdf(key="Outlines")]
pub outlines: Option<Outlines>,
#[pdf(key="AcroForm")]
pub forms: Option<InteractiveFormDictionary>,
#[pdf(key="Metadata")]
pub metadata: Option<Ref<Stream<()>>>,
#[pdf(key="StructTreeRoot")]
pub struct_tree_root: Option<StructTreeRoot>,
}
#[derive(Object, ObjectWrite, Debug, Default, Clone, DataSize)]
#[pdf(Type = "Pages?")]
pub struct PageTree {
#[pdf(key="Parent")]
pub parent: Option<PagesRc>,
#[pdf(key="Kids")]
pub kids: Vec<Ref<PagesNode>>,
#[pdf(key="Count")]
pub count: u32,
#[pdf(key="Resources")]
pub resources: Option<MaybeRef<Resources>>,
#[pdf(key="MediaBox")]
pub media_box: Option<Rect>,
#[pdf(key="CropBox")]
pub crop_box: Option<Rect>,
}
impl PageTree {
pub fn page(&self, resolve: &impl Resolve, page_nr: u32) -> Result<PageRc> {
self.page_limited(resolve, page_nr, 16)
}
fn page_limited(&self, resolve: &impl Resolve, page_nr: u32, depth: usize) -> Result<PageRc> {
if depth == 0 {
bail!("page tree depth exeeded");
}
let mut pos = 0;
for &kid in &self.kids {
let node = resolve.get(kid)?;
match *node {
PagesNode::Tree(ref tree) => {
if (pos .. pos + tree.count).contains(&page_nr) {
return tree.page_limited(resolve, page_nr - pos, depth - 1);
}
pos += tree.count;
}
PagesNode::Leaf(ref _page) => {
if pos == page_nr {
return Ok(PageRc(node));
}
pos += 1;
}
}
}
Err(PdfError::PageOutOfBounds {page_nr, max: pos})
}
}
impl SubType<PagesNode> for PageTree {}
#[derive(Object, ObjectWrite, Debug, Clone, DataSize)]
#[pdf(Type="Page?")]
pub struct Page {
#[pdf(key="Parent")]
pub parent: PagesRc,
#[pdf(key="Resources", indirect)]
pub resources: Option<MaybeRef<Resources>>,
#[pdf(key="MediaBox")]
pub media_box: Option<Rect>,
#[pdf(key="CropBox")]
pub crop_box: Option<Rect>,
#[pdf(key="TrimBox")]
pub trim_box: Option<Rect>,
#[pdf(key="Contents")]
pub contents: Option<Content>,
#[pdf(key="Rotate", default="0")]
pub rotate: i32,
#[pdf(key="Metadata")]
pub metadata: Option<Primitive>,
#[pdf(key="LGIDict")]
pub lgi: Option<Primitive>,
#[pdf(key="VP")]
pub vp: Option<Primitive>,
#[pdf(other)]
pub other: Dictionary,
}
fn inherit<'a, T: 'a, F>(mut parent: &'a PageTree, f: F) -> Result<Option<T>>
where F: Fn(&'a PageTree) -> Option<T>
{
loop {
match (&parent.parent, f(parent)) {
(_, Some(t)) => return Ok(Some(t)),
(Some(ref p), None) => parent = p,
(None, None) => return Ok(None)
}
}
}
impl Page {
pub fn new(parent: PagesRc) -> Page {
Page {
parent,
media_box: None,
crop_box: None,
trim_box: None,
resources: None,
contents: None,
rotate: 0,
metadata: None,
lgi: None,
vp: None,
other: Dictionary::new(),
}
}
pub fn media_box(&self) -> Result<Rect> {
match self.media_box {
Some(b) => Ok(b),
None => inherit(&self.parent, |pt| pt.media_box)?
.ok_or_else(|| PdfError::MissingEntry { typ: "Page", field: "MediaBox".into() })
}
}
pub fn crop_box(&self) -> Result<Rect> {
match self.crop_box {
Some(b) => Ok(b),
None => match inherit(&self.parent, |pt| pt.crop_box)? {
Some(b) => Ok(b),
None => self.media_box()
}
}
}
pub fn resources(&self) -> Result<&MaybeRef<Resources>> {
match self.resources {
Some(ref r) => Ok(r),
None => inherit(&self.parent, |pt| pt.resources.as_ref())?
.ok_or_else(|| PdfError::MissingEntry { typ: "Page", field: "Resources".into() })
}
}
}
impl SubType<PagesNode> for Page {}
#[derive(Object, DataSize)]
pub struct PageLabel {
#[pdf(key="S")]
pub style: Option<Counter>,
#[pdf(key="P")]
pub prefix: Option<PdfString>,
#[pdf(key="St")]
pub start: Option<usize>
}
#[derive(Object, ObjectWrite, Debug, DataSize, Default, DeepClone, Clone)]
pub struct Resources {
#[pdf(key="ExtGState")]
pub graphics_states: HashMap<Name, GraphicsStateParameters>,
#[pdf(key="ColorSpace")]
pub color_spaces: HashMap<Name, ColorSpace>,
#[pdf(key="Pattern")]
pub pattern: HashMap<Name, Ref<Pattern>>,
#[pdf(key="XObject")]
pub xobjects: HashMap<Name, Ref<XObject>>,
#[pdf(key="Font")]
pub fonts: HashMap<Name, MaybeRef<Font>>,
#[pdf(key="Properties")]
pub properties: HashMap<Name, MaybeRef<Dictionary>>,
}
impl Resources {
pub fn fonts(&self) -> impl Iterator<Item=(&str, &MaybeRef<Font>)> {
self.fonts.iter().map(|(k, v)| (k.as_str(), v))
}
}
#[derive(Debug, Object, ObjectWrite, DataSize, Clone, DeepClone)]
pub struct PatternDict {
#[pdf(key="PaintType")]
pub paint_type: Option<i32>,
#[pdf(key="TilingType")]
pub tiling_type: Option<i32>,
#[pdf(key="BBox")]
pub bbox: Rect,
#[pdf(key="XStep")]
pub x_step: f32,
#[pdf(key="YStep")]
pub y_step: f32,
#[pdf(key="Resources")]
pub resources: Ref<Resources>,
#[pdf(key="Matrix")]
pub matrix: Option<Matrix>,
}
#[derive(Debug, DataSize)]
pub enum Pattern {
Dict(PatternDict),
Stream(PatternDict, Vec<Op>),
}
impl Pattern {
pub fn dict(&self) -> &PatternDict {
match *self {
Pattern::Dict(ref d) => d,
Pattern::Stream(ref d, _) => d,
}
}
}
impl Object for Pattern {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
let p = p.resolve(resolve)?;
match p {
Primitive::Dictionary(dict) => Ok(Pattern::Dict(PatternDict::from_dict(dict, resolve)?)),
Primitive::Stream(s) => {
let stream: Stream<PatternDict> = Stream::from_stream(s, resolve)?;
let data = stream.data(resolve)?;
let ops = t!(parse_ops(&data, resolve));
let dict = stream.info.info;
Ok(Pattern::Stream(dict, ops))
}
p => Err(PdfError::UnexpectedPrimitive { expected: "Dictionary or Stream", found: p.get_debug_name() })
}
}
}
impl ObjectWrite for Pattern {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
match self {
Pattern::Dict(ref d) => d.to_primitive(update),
Pattern::Stream(ref d, ref ops) => {
let data = serialize_ops(ops)?;
let stream = Stream::new_with_filters(d.clone(), data, vec![]);
stream.to_primitive(update)
}
}
}
}
impl DeepClone for Pattern {
fn deep_clone(&self, cloner: &mut impl Cloner) -> Result<Self> {
match *self {
Pattern::Dict(ref d) => Ok(Pattern::Dict(d.deep_clone(cloner)?)),
Pattern::Stream(ref dict, ref ops) => {
let old_resources = cloner.get(dict.resources)?;
let mut resources = Resources::default();
let ops: Vec<Op> = ops.iter().map(|op| deep_clone_op(op, cloner, &old_resources, &mut resources)).collect::<Result<Vec<_>>>()?;
let dict = PatternDict {
resources: cloner.create(resources)?.get_ref(),
.. *dict
};
Ok(Pattern::Stream(dict, ops))
}
}
}
}
#[derive(Object, ObjectWrite, DeepClone, Debug, DataSize, Copy, Clone)]
pub enum LineCap {
Butt = 0,
Round = 1,
Square = 2
}
#[derive(Object, ObjectWrite, DeepClone, Debug, DataSize, Copy, Clone)]
pub enum LineJoin {
Miter = 0,
Round = 1,
Bevel = 2
}
#[derive(Object, ObjectWrite, DeepClone, Debug, DataSize, Clone)]
#[pdf(Type = "ExtGState?")]
pub struct GraphicsStateParameters {
#[pdf(key="LW")]
pub line_width: Option<f32>,
#[pdf(key="LC")]
pub line_cap: Option<LineCap>,
#[pdf(key="LJ")]
pub line_join: Option<LineJoin>,
#[pdf(key="ML")]
pub miter_limit: Option<f32>,
#[pdf(key="D")]
pub dash_pattern: Option<Vec<Primitive>>,
#[pdf(key="RI")]
pub rendering_intent: Option<Name>,
#[pdf(key="OP")]
pub overprint: Option<bool>,
#[pdf(key="op")]
pub overprint_fill: Option<bool>,
#[pdf(key="OPM")]
pub overprint_mode: Option<i32>,
#[pdf(key="Font")]
pub font: Option<(Ref<Font>, f32)>,
#[pdf(key="BM")]
pub blend_mode: Option<Primitive>,
#[pdf(key="SMask")]
pub smask: Option<Primitive>,
#[pdf(key="CA")]
pub stroke_alpha: Option<f32>,
#[pdf(key="ca")]
pub fill_alpha: Option<f32>,
#[pdf(key="AIS")]
pub alpha_is_shape: Option<bool>,
#[pdf(key="TK")]
pub text_knockout: Option<bool>,
#[pdf(other)]
_other: Dictionary
}
#[derive(Object, Debug, DataSize, DeepClone)]
#[pdf(is_stream)]
pub enum XObject {
#[pdf(name="PS")]
Postscript (PostScriptXObject),
Image (ImageXObject),
Form (FormXObject),
}
impl ObjectWrite for XObject {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
let (subtype, mut stream) = match self {
XObject::Postscript(s) => ("PS", s.to_pdf_stream(update)?),
XObject::Form(s) => ("Form", s.stream.to_pdf_stream(update)?),
XObject::Image(s) => ("Image", s.inner.to_pdf_stream(update)?),
};
stream.info.insert("Subtype", Name::from(subtype));
stream.info.insert("Type", Name::from("XObject"));
Ok(stream.into())
}
}
pub type PostScriptXObject = Stream<PostScriptDict>;
#[derive(Debug, DataSize, Clone, DeepClone)]
pub struct ImageXObject {
pub inner: Stream<ImageDict>
}
impl Object for ImageXObject {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
let s = PdfStream::from_primitive(p, resolve)?;
Self::from_stream(s, resolve)
}
}
impl ObjectWrite for ImageXObject {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
self.inner.to_primitive(update)
}
}
impl Deref for ImageXObject {
type Target = ImageDict;
fn deref(&self) -> &ImageDict {
&self.inner.info
}
}
pub enum ImageFormat {
Raw,
Jpeg,
Jp2k,
Jbig2,
CittFax,
Png
}
impl ImageXObject {
pub fn from_stream(s: PdfStream, resolve: &impl Resolve) -> Result<Self> {
let inner = Stream::from_stream(s, resolve)?;
Ok(ImageXObject { inner })
}
pub fn raw_image_data(&self, resolve: &impl Resolve) -> Result<(Arc<[u8]>, Option<&StreamFilter>)> {
match self.inner.inner_data {
StreamData::Generated(_) => Ok((self.inner.data(resolve)?, None)),
StreamData::Original(ref file_range, id) => {
let filters = self.inner.filters.as_slice();
let end = filters.iter().rposition(|f| match f {
StreamFilter::ASCIIHexDecode => false,
StreamFilter::ASCII85Decode => false,
StreamFilter::LZWDecode(_) => false,
StreamFilter::RunLengthDecode => false,
StreamFilter::Crypt => true,
_ => true
}).unwrap_or(filters.len());
let (normal_filters, image_filters) = filters.split_at(end);
let data = resolve.get_data_or_decode(id, file_range.clone(), normal_filters)?;
match image_filters {
[] => Ok((data, None)),
[StreamFilter::DCTDecode(_)] |
[StreamFilter::CCITTFaxDecode(_)] |
[StreamFilter::JPXDecode] |
[StreamFilter::FlateDecode(_)] |
[StreamFilter::JBIG2Decode(_)] => Ok((data, Some(&image_filters[0]))),
_ => bail!("??? filters={:?}", image_filters)
}
}
}
}
pub fn image_data(&self, resolve: &impl Resolve) -> Result<Arc<[u8]>> {
let (data, filter) = self.raw_image_data(resolve)?;
let filter = match filter {
Some(f) => f,
None => return Ok(data)
};
let mut data = match filter {
StreamFilter::CCITTFaxDecode(ref params) => {
if self.inner.info.width != params.columns {
bail!("image width mismatch {} != {}", self.inner.info.width, params.columns);
}
let mut data = fax_decode(&data, params)?;
if params.rows == 0 {
data.truncate(self.inner.info.height as usize * self.inner.info.width as usize);
}
data
}
StreamFilter::DCTDecode(ref p) => dct_decode(&data, p)?,
StreamFilter::JPXDecode => jpx_decode(&data)?,
StreamFilter::JBIG2Decode(ref p) => {
let global_data = p.globals.as_ref().map(|s| s.data(resolve)).transpose()?;
jbig2_decode(&data, global_data.as_deref().unwrap_or_default())?
},
StreamFilter::FlateDecode(ref p) => flate_decode(&data, p)?,
_ => unreachable!()
};
if let Some(ref decode) = self.decode {
if decode == &[1.0, 0.0] && self.bits_per_component == Some(1) {
data.iter_mut().for_each(|b| *b = !*b);
}
}
Ok(data.into())
}
}
#[derive(Object, Debug, DataSize, DeepClone, ObjectWrite)]
#[pdf(Type="XObject", Subtype="PS")]
pub struct PostScriptDict {
#[pdf(other)]
pub other: Dictionary
}
#[derive(Object, Debug, Clone, DataSize, DeepClone, ObjectWrite, Default)]
#[pdf(Type="XObject?", Subtype="Image")]
pub struct ImageDict {
#[pdf(key="Width")]
pub width: u32,
#[pdf(key="Height")]
pub height: u32,
#[pdf(key="ColorSpace")]
pub color_space: Option<ColorSpace>,
#[pdf(key="BitsPerComponent")]
pub bits_per_component: Option<i32>,
#[pdf(key="Intent")]
pub intent: Option<RenderingIntent>,
#[pdf(key="ImageMask", default="false")]
pub image_mask: bool,
#[pdf(key="Mask")]
pub mask: Option<Primitive>,
#[pdf(key="Decode")]
pub decode: Option<Vec<f32>>,
#[pdf(key="Interpolate", default="false")]
pub interpolate: bool,
#[pdf(key="StructParent")]
pub struct_parent: Option<i32>,
#[pdf(key="ID")]
pub id: Option<PdfString>,
#[pdf(key="SMask")]
pub smask: Option<Ref<Stream<ImageDict>>>,
#[pdf(other)]
pub other: Dictionary
}
#[derive(Object, Debug, Copy, Clone, DataSize, DeepClone, ObjectWrite)]
pub enum RenderingIntent {
AbsoluteColorimetric,
RelativeColorimetric,
Saturation,
Perceptual,
}
impl RenderingIntent {
pub fn from_str(s: &str) -> Option<RenderingIntent> {
match s {
"AbsoluteColorimetric" => Some(RenderingIntent::AbsoluteColorimetric),
"RelativeColorimetric" => Some(RenderingIntent::RelativeColorimetric),
"Perceptual" => Some(RenderingIntent::Perceptual),
"Saturation" => Some(RenderingIntent::Saturation),
_ => None
}
}
pub fn to_str(self) -> &'static str {
match self {
RenderingIntent::AbsoluteColorimetric => "AbsoluteColorimetric",
RenderingIntent::RelativeColorimetric => "RelativeColorimetric",
RenderingIntent::Perceptual => "Perceptual",
RenderingIntent::Saturation => "Saturation",
}
}
}
#[derive(Object, Debug, DataSize, DeepClone, ObjectWrite)]
#[pdf(Type="XObject?", Subtype="Form")]
pub struct FormDict {
#[pdf(key="FormType", default="1")]
pub form_type: i32,
#[pdf(key="Name")]
pub name: Option<Name>,
#[pdf(key="LastModified")]
pub last_modified: Option<PdfString>,
#[pdf(key="BBox")]
pub bbox: Rect,
#[pdf(key="Matrix")]
pub matrix: Option<Primitive>,
#[pdf(key="Resources")]
pub resources: Option<MaybeRef<Resources>>,
#[pdf(key="Group")]
pub group: Option<Dictionary>,
#[pdf(key="Ref")]
pub reference: Option<Dictionary>,
#[pdf(key="Metadata")]
pub metadata: Option<Ref<Stream<()>>>,
#[pdf(key="PieceInfo")]
pub piece_info: Option<Dictionary>,
#[pdf(key="StructParent")]
pub struct_parent: Option<i32>,
#[pdf(key="StructParents")]
pub struct_parents: Option<i32>,
#[pdf(key="OPI")]
pub opi: Option<Dictionary>,
#[pdf(other)]
pub other: Dictionary,
}
#[derive(Object, ObjectWrite, Debug, Clone, DataSize)]
pub struct InteractiveFormDictionary {
#[pdf(key="Fields")]
pub fields: Vec<RcRef<FieldDictionary>>,
#[pdf(key="NeedAppearances", default="false")]
pub need_appearences: bool,
#[pdf(key="SigFlags", default="0")]
pub sig_flags: u32,
#[pdf(key="CO")]
pub co: Option<Vec<RcRef<FieldDictionary>>>,
#[pdf(key="DR")]
pub dr: Option<MaybeRef<Resources>>,
#[pdf(key="DA")]
pub da: Option<PdfString>,
#[pdf(key="Q")]
pub q: Option<i32>,
#[pdf(key="XFA")]
pub xfa: Option<Primitive>,
}
#[derive(Object, ObjectWrite, Debug, Copy, Clone, PartialEq, DataSize)]
pub enum FieldType {
#[pdf(name="Btn")]
Button,
#[pdf(name="Tx")]
Text,
#[pdf(name="Ch")]
Choice,
#[pdf(name="Sig")]
Signature,
#[pdf(name="SigRef")]
SignatureReference,
}
#[derive(Object, ObjectWrite, Debug)]
#[pdf(Type="SV")]
pub struct SeedValueDictionary {
#[pdf(key="Ff", default="0")]
pub flags: u32,
#[pdf(key="Filter")]
pub filter: Option<Name>,
#[pdf(key="SubFilter")]
pub sub_filter: Option<Vec<Name>>,
#[pdf(key="V")]
pub value: Option<Primitive>,
#[pdf(key="DigestMethod")]
pub digest_method: Vec<PdfString>,
#[pdf(other)]
pub other: Dictionary
}
#[derive(Object, ObjectWrite, Debug)]
#[pdf(Type="Sig?")]
pub struct SignatureDictionary {
#[pdf(key="Filter")]
pub filter: Name,
#[pdf(key="SubFilter")]
pub sub_filter: Name,
#[pdf(key="ByteRange")]
pub byte_range: Vec<usize>,
#[pdf(key="Contents")]
pub contents: PdfString,
#[pdf(key="Cert")]
pub cert: Vec<PdfString>,
#[pdf(key="Reference")]
pub reference: Option<Primitive>,
#[pdf(key="Name")]
pub name: Option<PdfString>,
#[pdf(key="M")]
pub m: Option<PdfString>,
#[pdf(key="Location")]
pub location: Option<PdfString>,
#[pdf(key="Reason")]
pub reason: Option<PdfString>,
#[pdf(key="ContactInfo")]
pub contact_info: Option<PdfString>,
#[pdf(key="V")]
pub v: i32,
#[pdf(key="R")]
pub r: i32,
#[pdf(key="Prop_Build")]
pub prop_build: Dictionary,
#[pdf(key="Prop_AuthTime")]
pub prop_auth_time: i32,
#[pdf(key="Prop_AuthType")]
pub prop_auth_type: Name,
#[pdf(other)]
pub other: Dictionary
}
#[derive(Object, ObjectWrite, Debug)]
#[pdf(Type="SigRef?")]
pub struct SignatureReferenceDictionary {
#[pdf(key="TransformMethod")]
pub transform_method: Name,
#[pdf(key="TransformParams")]
pub transform_params: Option<Dictionary>,
#[pdf(key="Data")]
pub data: Option<Primitive>,
#[pdf(key="DigestMethod")]
pub digest_method: Option<Name>,
#[pdf(other)]
pub other: Dictionary
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
pub struct FieldDictionary {
#[pdf(key="FT")]
pub typ: Option<FieldType>,
#[pdf(key="Parent")]
pub parent: Option<MaybeRef<FieldDictionary>>,
#[pdf(key="Kids")]
pub kids: Vec<Ref<FieldDictionary>>,
#[pdf(key="T")]
pub name: Option<PdfString>,
#[pdf(key="TU")]
pub alt_name: Option<PdfString>,
#[pdf(key="TM")]
pub mapping_name: Option<PdfString>,
#[pdf(key="Ff", default="0")]
pub flags: u32,
#[pdf(key="SigFlags", default="0")]
pub sig_flags: u32,
#[pdf(key="V")]
pub value: Primitive,
#[pdf(key="DV")]
pub default_value: Primitive,
#[pdf(key="AA")]
pub actions: Option<Dictionary>,
}
#[derive(Debug, DataSize)]
pub enum Counter {
Arabic,
RomanUpper,
RomanLower,
AlphaUpper,
AlphaLower
}
impl Object for Counter {
fn from_primitive(_: Primitive, _: &impl Resolve) -> Result<Self> {
unimplemented!();
}
}
#[derive(Debug, DataSize)]
pub enum NameTreeNode<T> {
Intermediate (Vec<Ref<NameTree<T>>>),
Leaf (Vec<(PdfString, T)>)
}
#[derive(Debug, DataSize)]
pub struct NameTree<T> {
pub limits: Option<(PdfString, PdfString)>,
pub node: NameTreeNode<T>,
}
impl<T: Object+DataSize> NameTree<T> {
pub fn walk(&self, r: &impl Resolve, callback: &mut dyn FnMut(&PdfString, &T)) -> Result<(), PdfError> {
match self.node {
NameTreeNode::Leaf(ref items) => {
for (name, val) in items {
callback(name, val);
}
}
NameTreeNode::Intermediate(ref items) => {
for &tree_ref in items {
let tree = r.get(tree_ref)?;
tree.walk(r, callback)?;
}
}
}
Ok(())
}
}
impl<T: Object> Object for NameTree<T> {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
let mut dict = t!(p.resolve(resolve)?.into_dictionary());
let limits = match dict.remove("Limits") {
Some(limits) => {
let limits = limits.resolve(resolve)?.into_array()?;
if limits.len() != 2 {
bail!("Error reading NameTree: 'Limits' is not of length 2");
}
let min = limits[0].clone().into_string()?;
let max = limits[1].clone().into_string()?;
Some((min, max))
}
None => None
};
let kids = dict.remove("Kids");
let names = dict.remove("Names");
Ok(match (kids, names) {
(Some(kids), _) => {
let kids = t!(kids.resolve(resolve)?.into_array()?.iter().map(|kid|
Ref::<NameTree<T>>::from_primitive(kid.clone(), resolve)
).collect::<Result<Vec<_>>>());
NameTree {
limits,
node: NameTreeNode::Intermediate (kids)
}
}
(None, Some(names)) => {
let names = names.resolve(resolve)?.into_array()?;
let mut new_names = Vec::new();
for pair in names.chunks_exact(2) {
let name = pair[0].clone().resolve(resolve)?.into_string()?;
let value = t!(T::from_primitive(pair[1].clone(), resolve));
new_names.push((name, value));
}
NameTree {
limits,
node: NameTreeNode::Leaf (new_names),
}
}
(None, None) => {
warn!("Neither Kids nor Names present in NameTree node.");
NameTree {
limits,
node: NameTreeNode::Intermediate(vec![])
}
}
})
}
}
impl<T: ObjectWrite> ObjectWrite for NameTree<T> {
fn to_primitive(&self, _update: &mut impl Updater) -> Result<Primitive> {
todo!("impl ObjectWrite for NameTree")
}
}
#[derive(Debug, Clone, DataSize)]
pub enum DestView {
XYZ { left: Option<f32>, top: Option<f32>, zoom: f32 },
Fit,
FitH { top: f32 },
FitV { left: f32 },
FitR(Rect),
FitB,
FitBH { top: f32 }
}
#[derive(Debug, Clone, DataSize)]
pub enum MaybeNamedDest {
Named(PdfString),
Direct(Dest),
}
#[derive(Debug, Clone, DataSize)]
pub struct Dest {
pub page: Option<Ref<Page>>,
pub view: DestView
}
impl Object for Dest {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
let p = match p {
Primitive::Reference(r) => resolve.resolve(r)?,
p => p
};
let p = match p {
Primitive::Dictionary(mut dict) => dict.require("Dest", "D")?,
p => p
};
let array = t!(p.as_array(), p);
Dest::from_array(array, resolve)
}
}
impl Dest {
fn from_array(array: &[Primitive], resolve: &impl Resolve) -> Result<Self> {
let page = Object::from_primitive(try_opt!(array.get(0)).clone(), resolve)?;
let kind = try_opt!(array.get(1));
let view = match kind.as_name()? {
"XYZ" => DestView::XYZ {
left: match *try_opt!(array.get(2)) {
Primitive::Null => None,
Primitive::Integer(n) => Some(n as f32),
Primitive::Number(f) => Some(f),
ref p => return Err(PdfError::UnexpectedPrimitive { expected: "Number | Integer | Null", found: p.get_debug_name() }),
},
top: match *try_opt!(array.get(3)) {
Primitive::Null => None,
Primitive::Integer(n) => Some(n as f32),
Primitive::Number(f) => Some(f),
ref p => return Err(PdfError::UnexpectedPrimitive { expected: "Number | Integer | Null", found: p.get_debug_name() }),
},
zoom: match array.get(4) {
Some(Primitive::Null) => 0.0,
Some(&Primitive::Integer(n)) => n as f32,
Some(&Primitive::Number(f)) => f,
Some(p) => return Err(PdfError::UnexpectedPrimitive { expected: "Number | Integer | Null", found: p.get_debug_name() }),
None => 0.0,
},
},
"Fit" => DestView::Fit,
"FitH" => DestView::FitH {
top: try_opt!(array.get(2)).as_number()?
},
"FitV" => DestView::FitV {
left: try_opt!(array.get(2)).as_number()?
},
"FitR" => DestView::FitR(Rect {
left: try_opt!(array.get(2)).as_number()?,
bottom: try_opt!(array.get(3)).as_number()?,
right: try_opt!(array.get(4)).as_number()?,
top: try_opt!(array.get(5)).as_number()?,
}),
"FitB" => DestView::FitB,
"FitBH" => DestView::FitBH {
top: try_opt!(array.get(2)).as_number()?
},
name => return Err(PdfError::UnknownVariant { id: "Dest", name: name.into() })
};
Ok(Dest {
page,
view
})
}
}
impl Object for MaybeNamedDest {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
let p = match p {
Primitive::Reference(r) => resolve.resolve(r)?,
p => p
};
let p = match p {
Primitive::Dictionary(mut dict) => dict.require("Dest", "D")?,
Primitive::String(s) => return Ok(MaybeNamedDest::Named(s)),
p => p
};
let array = t!(p.as_array(), p);
Dest::from_array(array, resolve).map(MaybeNamedDest::Direct)
}
}
impl ObjectWrite for MaybeNamedDest {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
match self {
MaybeNamedDest::Named(s) => Ok(Primitive::String(s.clone())),
MaybeNamedDest::Direct(d) => d.to_primitive(update)
}
}
}
impl ObjectWrite for Dest {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
let mut arr = vec![self.page.to_primitive(update)?];
match self.view {
DestView::XYZ { left, top, zoom } => {
arr.push(Primitive::Name("XYZ".into()));
arr.push(left.to_primitive(update)?);
arr.push(top.to_primitive(update)?);
arr.push(Primitive::Number(zoom));
}
DestView::Fit => {
arr.push(Primitive::Name("Fit".into()));
}
DestView::FitH { top } => {
arr.push(Primitive::Name("FitH".into()));
arr.push(Primitive::Number(top));
}
DestView::FitV { left } => {
arr.push(Primitive::Name("FitV".into()));
arr.push(Primitive::Number(left));
}
DestView::FitR(rect) => {
arr.push(Primitive::Name("FitR".into()));
arr.push(Primitive::Number(rect.left));
arr.push(Primitive::Number(rect.bottom));
arr.push(Primitive::Number(rect.right));
arr.push(Primitive::Number(rect.top));
}
DestView::FitB => {
arr.push(Primitive::Name("FitB".into()));
}
DestView::FitBH { top } => {
arr.push(Primitive::Name("FitBH".into()));
arr.push(Primitive::Number(top));
}
}
Ok(Primitive::Array(arr))
}
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
pub struct NameDictionary {
#[pdf(key="Pages")]
pub pages: Option<NameTree<Primitive>>,
#[pdf(key="Dests")]
pub dests: Option<NameTree<Option<Dest>>>,
#[pdf(key="AP")]
pub ap: Option<NameTree<Primitive>>,
#[pdf(key="JavaScript")]
pub javascript: Option<NameTree<Primitive>>,
#[pdf(key="Templates")]
pub templates: Option<NameTree<Primitive>>,
#[pdf(key="IDS")]
pub ids: Option<NameTree<Primitive>>,
#[pdf(key="URLS")]
pub urls: Option<NameTree<Primitive>>,
#[pdf(key="EmbeddedFiles")]
pub embedded_files: Option<NameTree<FileSpec>>,
}
#[derive(Object, ObjectWrite, Debug, Clone, DataSize, DeepClone)]
pub struct FileSpec {
#[pdf(key="EF")]
pub ef: Option<Files<Ref<Stream<EmbeddedFile>>>>,
}
#[derive(Object, ObjectWrite, Debug, Clone, DeepClone)]
pub struct Files<T> {
#[pdf(key="F")]
pub f: Option<T>,
#[pdf(key="UF")]
pub uf: Option<T>,
#[pdf(key="DOS")]
pub dos: Option<T>,
#[pdf(key="Mac")]
pub mac: Option<T>,
#[pdf(key="Unix")]
pub unix: Option<T>,
}
impl<T: DataSize> DataSize for Files<T> {
const IS_DYNAMIC: bool = T::IS_DYNAMIC;
const STATIC_HEAP_SIZE: usize = 5 * Option::<T>::STATIC_HEAP_SIZE;
fn estimate_heap_size(&self) -> usize {
self.f.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
self.uf.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
self.dos.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
self.mac.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0) +
self.unix.as_ref().map(|t| t.estimate_heap_size()).unwrap_or(0)
}
}
#[derive(Object, Debug, Clone, DataSize, DeepClone, ObjectWrite)]
pub struct EmbeddedFile {
#[pdf(key="Subtype")]
subtype: Option<Name>,
#[pdf(key="Params")]
pub params: Option<EmbeddedFileParamDict>,
}
#[derive(Object, Debug, Clone, DataSize, DeepClone, ObjectWrite)]
pub struct EmbeddedFileParamDict {
#[pdf(key="Size")]
pub size: Option<i32>,
#[pdf(key="CreationDate")]
creationdate: Option<Date>,
#[pdf(key="ModDate")]
moddate: Option<Date>,
#[pdf(key="Mac")]
mac: Option<Date>,
#[pdf(key="CheckSum")]
checksum: Option<Date>,
}
#[derive(Object, Debug, Clone, DataSize)]
pub struct OutlineItem {
#[pdf(key="Title")]
pub title: Option<PdfString>,
#[pdf(key="Prev")]
pub prev: Option<Ref<OutlineItem>>,
#[pdf(key="Next")]
pub next: Option<Ref<OutlineItem>>,
#[pdf(key="First")]
pub first: Option<Ref<OutlineItem>>,
#[pdf(key="Last")]
pub last: Option<Ref<OutlineItem>>,
#[pdf(key="Count", default="0")]
pub count: i32,
#[pdf(key="Dest")]
pub dest: Option<Primitive>,
#[pdf(key="A")]
pub action: Option<Action>,
#[pdf(key="SE")]
pub se: Option<Dictionary>,
#[pdf(key="C")]
pub color: Option<Vec<f32>>,
#[pdf(key="F")]
pub flags: Option<i32>,
}
#[derive(Clone, Debug, DataSize)]
pub enum Action {
Goto(MaybeNamedDest),
Other(Dictionary)
}
impl Object for Action {
fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
let mut d = t!(p.resolve(resolve)?.into_dictionary());
let s = try_opt!(d.get("S")).as_name()?;
match s {
"GoTo" => {
let dest = t!(MaybeNamedDest::from_primitive(try_opt!(d.remove("D")), resolve));
Ok(Action::Goto(dest))
}
_ => Ok(Action::Other(d))
}
}
}
impl ObjectWrite for Action {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
match self {
Action::Goto(dest) => {
let mut dict = Dictionary::new();
dict.insert("D", dest.to_primitive(update)?);
Ok(Primitive::Dictionary(dict))
}
Action::Other(dict) => Ok(Primitive::Dictionary(dict.clone()))
}
}
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
#[pdf(Type="Outlines?")]
pub struct Outlines {
#[pdf(key="Count", default="0")]
pub count: i32,
#[pdf(key="First")]
pub first: Option<Ref<OutlineItem>>,
#[pdf(key="Last")]
pub last: Option<Ref<OutlineItem>>,
}
#[derive(Debug, Copy, Clone, DataSize)]
pub struct Rect {
pub left: f32,
pub bottom: f32,
pub right: f32,
pub top: f32,
}
impl Object for Rect {
fn from_primitive(p: Primitive, r: &impl Resolve) -> Result<Self> {
let arr = p.resolve(r)?.into_array()?;
if arr.len() != 4 {
bail!("len != 4 {:?}", arr);
}
Ok(Rect {
left: arr[0].as_number()?,
bottom: arr[1].as_number()?,
right: arr[2].as_number()?,
top: arr[3].as_number()?
})
}
}
impl ObjectWrite for Rect {
fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
Primitive::array::<f32, _, _, _>([self.left, self.bottom, self.right, self.top].iter(), update)
}
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
pub struct MarkInformation { #[pdf(key="Marked", default="false")]
pub marked: bool,
#[pdf(key="UserProperties", default="false")]
pub user_properties: bool,
#[pdf(key="Suspects", default="false")]
pub suspects: bool,
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
#[pdf(Type = "StructTreeRoot")]
pub struct StructTreeRoot {
#[pdf(key="K")]
pub children: Vec<StructElem>,
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
pub struct StructElem {
#[pdf(key="S")]
pub struct_type: StructType,
#[pdf(key="P")]
pub parent: Ref<StructElem>,
#[pdf(key="ID")]
pub id: Option<PdfString>,
#[pdf(key="Pg")]
pub page: Option<Ref<Page>>,
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
pub enum StructType {
Document,
Part,
Art,
Sect,
Div,
BlockQuote,
Caption,
TOC,
TOCI,
Index,
NonStruct,
Private,
Book,
P,
H,
H1,
H2,
H3,
H4,
H5,
H6,
L,
Ll,
Lbl,
LBody,
Table,
TR,
TH,
TD,
THead,
TBody,
TFoot,
Span,
Quote,
Note,
Reference,
BibEntry,
Code,
Link,
Annot,
Ruby,
RB,
RT,
RP,
Warichu,
WT,
WP,
Figure,
Formula,
Form,
#[pdf(other)]
Other(String),
}
#[derive(Object, ObjectWrite, Debug, DataSize)]
pub enum Trapped {
True,
False,
Unknown,
}
#[derive(Object, ObjectWrite, Debug, DataSize, Default)]
pub struct InfoDict {
#[pdf(key="Title")]
pub title: Option<PdfString>,
#[pdf(key="Author")]
pub author: Option<PdfString>,
#[pdf(key="Subject")]
pub subject: Option<PdfString>,
#[pdf(key="Keywords")]
pub keywords: Option<PdfString>,
#[pdf(key="Creator")]
pub creator: Option<PdfString>,
#[pdf(key="Author")]
pub producer: Option<PdfString>,
#[pdf(key="CreationDate")]
pub creation_date: Option<Date>,
#[pdf(key="ModDate")]
pub mod_date: Option<Date>,
#[pdf(key="Trapped")]
pub trapped: Option<Trapped>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_struct_type() {
assert!(matches!(
StructType::from_primitive(Primitive::Name("BibEntry".into()), &NoResolve),
Ok(StructType::BibEntry)
));
let result =
StructType::from_primitive(Primitive::Name("CustomStructType".into()), &NoResolve);
if let Ok(StructType::Other(name)) = &result {
assert_eq!(name, "CustomStructType");
} else {
panic!("Incorrect result of {:?}", &result);
}
}
#[test]
fn test_field_type() {
assert_eq!(
FieldType::from_primitive(Primitive::Name("Tx".into()), &NoResolve).unwrap(),
FieldType::Text
);
}
}