use std::collections::HashMap;
use crate as pdf;
use crate::object::*;
use crate::error::*;
use crate::content::Content;
use crate::font::Font;
#[derive(Debug, Clone)]
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.into_dictionary(resolve)?;
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)]
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))?))
}
}
#[derive(Debug, Clone)]
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).to_primitive(update)
}
}
#[derive(Object, ObjectWrite, Debug)]
pub struct Catalog {
#[pdf(key="Pages")]
pub pages: PagesRc,
#[pdf(key="Names")]
pub names: Option<RcRef<NameDictionary>>,
#[pdf(key="Dests")]
pub dests: Option<Ref<Dictionary>>,
#[pdf(key="Outlines")]
pub outlines: Option<Outlines>,
#[pdf(key="Metadata")]
pub metadata: Option<Ref<Stream>>,
#[pdf(key="StructTreeRoot")]
pub struct_tree_root: Option<StructTreeRoot>,
}
#[derive(Object, ObjectWrite, Debug, Default, Clone)]
#[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> {
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(resolve, page_nr - pos);
}
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)]
pub struct Page {
#[pdf(key="Parent")]
pub parent: PagesRc,
#[pdf(key="Resources")]
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>
}
fn inherit<'a, T: 'a, F>(mut parent: &'a PageTree, f: F) -> Result<Option<T>>
where F: Fn(&'a PageTree) -> Option<T>
{
loop {
debug!("parent: {:?}", parent);
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
}
}
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)]
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)]
pub struct Resources {
#[pdf(key="ExtGState")]
pub graphics_states: HashMap<String, GraphicsStateParameters>,
#[pdf(key="ColorSpace")]
pub color_spaces: HashMap<String, ColorSpace>,
#[pdf(key="XObject")]
pub xobjects: HashMap<String, Ref<XObject>>,
#[pdf(key="Font")]
pub fonts: HashMap<String, Ref<Font>>,
#[pdf(key="Properties")]
pub properties: HashMap<String, RcRef<Dictionary>>,
}
impl Resources {
pub fn fonts(&self) -> impl Iterator<Item=(&str, &Ref<Font>)> {
self.fonts.iter().map(|(k, v)| (k.as_str(), v))
}
}
#[derive(Object, ObjectWrite, Debug)]
pub enum LineCap {
Butt = 0,
Round = 1,
Square = 2
}
#[derive(Object, ObjectWrite, Debug)]
pub enum LineJoin {
Miter = 0,
Round = 1,
Bevel = 2
}
#[derive(Object, ObjectWrite, Debug)]
#[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="RI")]
pub rendering_intent: Option<String>,
#[pdf(key="Font")]
pub font: Option<(Ref<Font>, f32)>,
#[pdf(other)]
_other: Dictionary
}
#[derive(Object, Debug)]
#[pdf(is_stream)]
pub enum XObject {
#[pdf(name="PS")]
Postscript (PostScriptXObject),
Image (ImageXObject),
Form (FormXObject),
}
pub type PostScriptXObject = Stream<PostScriptDict>;
pub type ImageXObject = Stream<ImageDict>;
pub type FormXObject = Stream<FormDict>;
#[derive(Object, Debug)]
#[pdf(Type="XObject", Subtype="PS")]
pub struct PostScriptDict {
}
#[derive(Object, Debug)]
#[pdf(Type="XObject?", Subtype="Image")]
pub struct ImageDict {
#[pdf(key="Width")]
pub width: i32,
#[pdf(key="Height")]
pub height: i32,
#[pdf(key="ColorSpace")]
pub color_space: Option<Primitive>,
#[pdf(key="BitsPerComponent")]
pub bits_per_component: i32,
#[pdf(key="Intent")]
pub intent: Option<RenderingIntent>,
#[pdf(key="ImageMask", default="false")]
pub image_mask: bool,
#[pdf(key="Mask")]
pub mask: 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)]
other: Dictionary
}
#[derive(Object, Debug, Clone)]
pub enum RenderingIntent {
AbsoluteColorimetric,
RelativeColorimetric,
Saturation,
Perceptual,
}
#[derive(Object, Debug)]
#[pdf(Type="XObject?", Subtype="Form")]
pub struct FormDict {
}
pub enum Counter {
Arabic,
RomanUpper,
RomanLower,
AlphaUpper,
AlphaLower
}
impl Object for Counter {
fn from_primitive(_: Primitive, _: &impl Resolve) -> Result<Self> {
unimplemented!();
}
}
#[derive(Debug)]
pub enum NameTreeNode<T> {
Intermediate (Vec<Ref<NameTree<T>>>),
Leaf (Vec<(PdfString, T)>)
}
#[derive(Debug)]
pub struct NameTree<T> {
pub limits: Option<(PdfString, PdfString)>,
pub node: NameTreeNode<T>,
}
impl<T: Object> 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.into_dictionary(resolve));
let limits = match dict.remove("Limits") {
Some(limits) => {
let limits = limits.into_array(resolve)?;
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.into_array(resolve)?.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.into_array(resolve)?;
let mut new_names = Vec::new();
for pair in names.chunks(2) {
let name = pair[0].clone().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) => bail!("Neither Kids nor Names present in NameTree node.")
})
}
}
#[derive(Debug, Clone)]
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)]
pub struct Dest {
pub page: 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 = p.as_array()?;
let page = Ref::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 try_opt!(array.get(4)) {
&Primitive::Null => 0.0,
&Primitive::Integer(n) => n as f32,
&Primitive::Number(f) => f,
ref p => return Err(PdfError::UnexpectedPrimitive { expected: "Number | Integer | Null", found: p.get_debug_name() }),
},
},
"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
})
}
}
#[derive(Object, Debug)]
pub struct NameDictionary {
#[pdf(key="Pages")]
pub pages: Option<NameTree<Primitive>>,
#[pdf(key="Dests")]
pub dests: Option<NameTree<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, Debug, Clone)]
pub struct FileSpec {
#[pdf(key="EF")]
ef: Option<Files<Ref<Stream<EmbeddedFile>>>>,
}
#[derive(Object, Debug, Clone)]
pub struct Files<T: Object> {
#[pdf(key="F")]
f: Option<T>,
#[pdf(key="UF")]
uf: Option<T>,
#[pdf(key="DOS")]
dos: Option<T>,
#[pdf(key="Mac")]
mac: Option<T>,
#[pdf(key="Unix")]
unix: Option<T>,
}
#[derive(Object, Debug, Clone)]
pub struct EmbeddedFile {
#[pdf(key="Params")]
params: Option<EmbeddedFileParamDict>,
}
#[derive(Object, Debug, Clone)]
pub struct EmbeddedFileParamDict {
#[pdf(key="Size")]
size: Option<i32>,
}
#[derive(Object, Debug, Clone)]
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<PdfString>,
#[pdf(key="A")]
pub action: Option<Dictionary>,
#[pdf(key="SE")]
pub se: Option<Dictionary>,
#[pdf(key="C")]
pub color: Option<Vec<f32>>,
#[pdf(key="F")]
pub flags: Option<i32>,
}
#[derive(Object, ObjectWrite, Debug)]
#[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)]
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.into_array(r)?;
if arr.len() != 4 {
bail!("len != 4");
}
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.top, self.right, self.bottom].iter(), update)
}
}
#[derive(Object, ObjectWrite, Debug)]
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)]
#[pdf(Type = "StructTreeRoot")]
pub struct StructTreeRoot {
#[pdf(key="K")]
pub children: Vec<StructElem>,
}
#[derive(Object, ObjectWrite, Debug)]
pub struct StructElem {
#[pdf(key="S")]
struct_type: StructType,
#[pdf(key="P")]
parent: Ref<StructElem>,
#[pdf(key="ID")]
id: Option<PdfString>,
#[pdf(key="Pg")]
page: Option<Ref<Page>>,
}
#[derive(Object, ObjectWrite, Debug)]
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),
}
#[cfg(test)]
mod tests {
use crate::{
object::{NoResolve, Object, StructType},
primitive::Primitive,
};
#[test]
fn parse_struct_type() {
assert!(matches!(
StructType::from_primitive(Primitive::Name("BibEntry".to_string()), &NoResolve),
Ok(StructType::BibEntry)
));
let result =
StructType::from_primitive(Primitive::Name("CustomStructType".to_string()), &NoResolve);
if let Ok(StructType::Other(name)) = &result {
assert_eq!(name, "CustomStructType");
} else {
panic!("Incorrect result of {:?}", &result);
}
}
}