parsing more of the pdf structure
This commit is contained in:
parent
83631cc4c6
commit
e0993fdb4a
9 changed files with 1549 additions and 100 deletions
11
src/main.rs
11
src/main.rs
|
|
@ -1,4 +1,4 @@
|
|||
use parse_powerisa_pdf::pdf::{Pdf, PdfTrailer};
|
||||
use parse_powerisa_pdf::pdf::Pdf;
|
||||
use std::{
|
||||
error::Error,
|
||||
io::{IsTerminal, Read},
|
||||
|
|
@ -32,14 +32,7 @@ fn main() -> Result<ExitCode, Box<dyn Error>> {
|
|||
buf
|
||||
};
|
||||
let pdf = Pdf::parse(input)?;
|
||||
if let PdfTrailer::Stream {
|
||||
xref_stream,
|
||||
start_xref,
|
||||
} = pdf.trailer
|
||||
{
|
||||
dbg!(xref_stream.dictionary());
|
||||
}
|
||||
|
||||
println!("{:#?}", pdf.trailer.trailer_dictionary());
|
||||
todo!();
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
|
|
|
|||
98
src/pdf.rs
98
src/pdf.rs
|
|
@ -1,11 +1,14 @@
|
|||
use crate::{
|
||||
pdf::{
|
||||
document_structure::PdfDocumentCatalog,
|
||||
object::{
|
||||
PdfArray, PdfBoolean, PdfDictionary, PdfInteger, PdfName, PdfNull, PdfObject,
|
||||
PdfObjectIdentifier, PdfObjectIndirect, PdfObjectStreamDictionary, PdfReal, PdfStream,
|
||||
PdfStreamDictionary, PdfString, UnparsedPdfStreamDictionary,
|
||||
},
|
||||
parse::{GetPdfInputPosition, PdfInputPosition, PdfParse, PdfParseError},
|
||||
parse::{
|
||||
GetPdfInputPosition, PdfInputPosition, PdfInputPositionKnown, PdfParse, PdfParseError,
|
||||
},
|
||||
},
|
||||
pdf_parse,
|
||||
util::ArcOrRef,
|
||||
|
|
@ -19,12 +22,16 @@ use std::{
|
|||
sync::{Arc, OnceLock},
|
||||
};
|
||||
|
||||
pub mod content_stream;
|
||||
pub mod document_structure;
|
||||
pub mod font;
|
||||
pub mod object;
|
||||
pub mod parse;
|
||||
pub mod stream_filters;
|
||||
|
||||
struct PdfObjectsInner {
|
||||
objects: BTreeMap<PdfObjectIdentifier, PdfObject>,
|
||||
#[allow(dead_code)]
|
||||
object_streams: Vec<PdfStream<PdfObjectStreamDictionary>>,
|
||||
}
|
||||
|
||||
|
|
@ -43,6 +50,7 @@ impl PdfHeader {
|
|||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfTrailerDictionary {
|
||||
#[pdf(name = "Size")]
|
||||
|
|
@ -50,7 +58,7 @@ pdf_parse! {
|
|||
#[pdf(name = "Prev")]
|
||||
pub prev: Option<usize>,
|
||||
#[pdf(name = "Root")]
|
||||
pub root: PdfDictionary,
|
||||
pub root: PdfDocumentCatalog,
|
||||
#[pdf(name = "Encrypt")]
|
||||
pub encrypt: Option<PdfDictionary>,
|
||||
#[pdf(name = "Info")]
|
||||
|
|
@ -63,6 +71,7 @@ pdf_parse! {
|
|||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub enum PdfXRefName {
|
||||
#[pdf(name = "XRef")]
|
||||
|
|
@ -72,6 +81,7 @@ pdf_parse! {
|
|||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfXRefStreamDictionaryRest {
|
||||
#[pdf(name = "Type")]
|
||||
|
|
@ -97,6 +107,17 @@ pub enum PdfTrailer {
|
|||
},
|
||||
}
|
||||
|
||||
impl PdfTrailer {
|
||||
pub fn trailer_dictionary(&self) -> &PdfTrailerDictionary {
|
||||
match self {
|
||||
PdfTrailer::Trailer {
|
||||
trailer_dictionary, ..
|
||||
} => trailer_dictionary,
|
||||
PdfTrailer::Stream { xref_stream, .. } => &xref_stream.dictionary().rest.rest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Pdf {
|
||||
pub header: PdfHeader,
|
||||
pub objects: Arc<PdfObjects>,
|
||||
|
|
@ -192,12 +213,12 @@ struct PdfTokenizerPeek<'a> {
|
|||
#[derive(Clone)]
|
||||
struct PdfTokenizer<'a> {
|
||||
bytes: &'a [u8],
|
||||
pos: usize,
|
||||
pos: PdfInputPositionKnown,
|
||||
peek_cache: Option<PdfTokenizerPeek<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> PdfTokenizer<'a> {
|
||||
fn new(bytes: &'a [u8], pos: usize) -> Self {
|
||||
fn new(bytes: &'a [u8], pos: PdfInputPositionKnown) -> Self {
|
||||
Self {
|
||||
bytes,
|
||||
pos,
|
||||
|
|
@ -205,14 +226,14 @@ impl<'a> PdfTokenizer<'a> {
|
|||
}
|
||||
}
|
||||
fn pos(&self) -> PdfInputPosition {
|
||||
PdfInputPosition::new(self.pos)
|
||||
PdfInputPosition::new(Some(self.pos))
|
||||
}
|
||||
fn peek_byte(&mut self) -> Option<u8> {
|
||||
self.bytes.get(self.pos).copied()
|
||||
self.bytes.get(self.pos.pos).copied()
|
||||
}
|
||||
fn next_byte(&mut self) -> Option<u8> {
|
||||
let b = self.bytes.get(self.pos)?;
|
||||
self.pos += 1;
|
||||
let b = self.bytes.get(self.pos.pos)?;
|
||||
self.pos.pos += 1;
|
||||
self.peek_cache = None;
|
||||
Some(*b)
|
||||
}
|
||||
|
|
@ -229,14 +250,16 @@ impl<'a> PdfTokenizer<'a> {
|
|||
let token = tokenizer.next()?;
|
||||
self.peek_cache = Some(PdfTokenizerPeek {
|
||||
token,
|
||||
pos_after_token: tokenizer.pos,
|
||||
pos_after_token: tokenizer.pos.pos,
|
||||
});
|
||||
Some(token)
|
||||
}
|
||||
fn read_bytes(&mut self, len: usize) -> Option<&'a [u8]> {
|
||||
let retval = self.bytes.get(self.pos..self.pos.saturating_add(len))?;
|
||||
let retval = self
|
||||
.bytes
|
||||
.get(self.pos.pos..self.pos.pos.saturating_add(len))?;
|
||||
self.peek_cache = None;
|
||||
self.pos += len;
|
||||
self.pos.pos += len;
|
||||
Some(retval)
|
||||
}
|
||||
}
|
||||
|
|
@ -250,11 +273,11 @@ impl<'a> Iterator for PdfTokenizer<'a> {
|
|||
pos_after_token,
|
||||
}) = self.peek_cache.take()
|
||||
{
|
||||
self.pos = pos_after_token;
|
||||
self.pos.pos = pos_after_token;
|
||||
return Some(token);
|
||||
}
|
||||
loop {
|
||||
let start_pos = self.pos;
|
||||
let start_pos = self.pos.pos;
|
||||
break match PdfCharCategory::new(self.next_byte()?) {
|
||||
PdfCharCategory::Whitespace => continue,
|
||||
PdfCharCategory::LParen => Some(PdfToken::LParen),
|
||||
|
|
@ -272,22 +295,22 @@ impl<'a> Iterator for PdfTokenizer<'a> {
|
|||
None | Some(b'\n') => break,
|
||||
Some(b'\r') => {
|
||||
if let Some(b'\n') = self.peek_byte() {
|
||||
self.pos += 1;
|
||||
self.pos.pos += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
Some(_) => continue,
|
||||
}
|
||||
}
|
||||
Some(PdfToken::Comment(&self.bytes[start_pos..self.pos]))
|
||||
Some(PdfToken::Comment(&self.bytes[start_pos..self.pos.pos]))
|
||||
}
|
||||
PdfCharCategory::Regular => {
|
||||
while let Some(PdfCharCategory::Regular) =
|
||||
self.peek_byte().map(PdfCharCategory::new)
|
||||
{
|
||||
self.pos += 1;
|
||||
self.pos.pos += 1;
|
||||
}
|
||||
Some(PdfToken::Regular(&self.bytes[start_pos..self.pos]))
|
||||
Some(PdfToken::Regular(&self.bytes[start_pos..self.pos.pos]))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -647,7 +670,10 @@ impl<'a> PdfParser<'a> {
|
|||
}
|
||||
let Some(data) = self.tokenizer.read_bytes(len) else {
|
||||
return Err(PdfParseError::TruncatedFile {
|
||||
pos: PdfInputPosition::new(self.tokenizer.bytes.len()),
|
||||
pos: PdfInputPosition::new(Some(PdfInputPositionKnown {
|
||||
pos: self.tokenizer.bytes.len(),
|
||||
..self.tokenizer.pos
|
||||
})),
|
||||
});
|
||||
};
|
||||
let (stream, unparsed) = PdfStream::new_unparsed(stream_pos, dictionary, Arc::from(data));
|
||||
|
|
@ -810,9 +836,22 @@ impl<'a> PdfParser<'a> {
|
|||
object_stream: &PdfStream<PdfObjectStreamDictionary>,
|
||||
) -> Result<(), PdfParseError> {
|
||||
let data = object_stream.decoded_data().as_ref()?;
|
||||
self.with_tokenizer(PdfTokenizer::new(data, 0), |parser| {
|
||||
parser.parse_object_stream_inner(object_stream)
|
||||
})
|
||||
self.with_tokenizer(
|
||||
PdfTokenizer::new(
|
||||
data,
|
||||
PdfInputPositionKnown {
|
||||
pos: 0,
|
||||
containing_streams_pos: Some(
|
||||
object_stream
|
||||
.get_pdf_input_position()
|
||||
.get()
|
||||
.expect("known to be set")
|
||||
.pos,
|
||||
),
|
||||
},
|
||||
),
|
||||
|parser| parser.parse_object_stream_inner(object_stream),
|
||||
)
|
||||
.map_err(|e| PdfParseError::ObjectStreamParseError {
|
||||
stream_pos: object_stream.get_pdf_input_position(),
|
||||
error: Arc::new(e),
|
||||
|
|
@ -913,7 +952,13 @@ impl<'a> PdfParser<'a> {
|
|||
});
|
||||
}
|
||||
let old_tokenizer = self.tokenizer.clone();
|
||||
self.tokenizer = PdfTokenizer::new(self.tokenizer.bytes, start_xref);
|
||||
self.tokenizer = PdfTokenizer::new(
|
||||
self.tokenizer.bytes,
|
||||
PdfInputPositionKnown {
|
||||
pos: start_xref,
|
||||
containing_streams_pos: None,
|
||||
},
|
||||
);
|
||||
let id = self.parse_object_identifier(false);
|
||||
self.tokenizer = old_tokenizer;
|
||||
let Some(id) = id? else {
|
||||
|
|
@ -950,7 +995,13 @@ impl Pdf {
|
|||
}),
|
||||
objects_map: BTreeMap::new(),
|
||||
unparsed_stream_dictionaries: vec![],
|
||||
tokenizer: PdfTokenizer::new(bytes.as_ref(), 0),
|
||||
tokenizer: PdfTokenizer::new(
|
||||
bytes.as_ref(),
|
||||
PdfInputPositionKnown {
|
||||
pos: 0,
|
||||
containing_streams_pos: None,
|
||||
},
|
||||
),
|
||||
}
|
||||
.parse_file()
|
||||
}
|
||||
|
|
@ -971,6 +1022,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_deserialize_dict() -> Result<(), PdfParseError> {
|
||||
crate::pdf::parse::pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct TestStruct {
|
||||
|
|
|
|||
6
src/pdf/content_stream.rs
Normal file
6
src/pdf/content_stream.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
use crate::pdf::object::PdfStream;
|
||||
|
||||
pub struct PdfContentStream {
|
||||
stream: PdfStream,
|
||||
// TODO
|
||||
}
|
||||
501
src/pdf/document_structure.rs
Normal file
501
src/pdf/document_structure.rs
Normal file
|
|
@ -0,0 +1,501 @@
|
|||
use core::fmt;
|
||||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use crate::pdf::{
|
||||
font::PdfFont,
|
||||
object::{
|
||||
IsPdfNull, MaybeArray, PdfDate, PdfDictionary, PdfInteger, PdfName, PdfObject,
|
||||
PdfObjectDirect, PdfObjectIndirect, PdfRectangle, PdfStream, PdfString,
|
||||
},
|
||||
parse::{PdfParse, PdfParseError},
|
||||
pdf_parse,
|
||||
};
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub enum PdfDocumentCatalogType {
|
||||
#[pdf(name = "Catalog")]
|
||||
#[default]
|
||||
Catalog,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfDocumentCatalog {
|
||||
#[pdf(name = "Type")]
|
||||
pub ty: PdfDocumentCatalogType,
|
||||
#[pdf(name = "Version")]
|
||||
pub version: Option<PdfName>,
|
||||
#[pdf(name = "Extensions")]
|
||||
pub extensions: Option<PdfDictionary>,
|
||||
#[pdf(name = "Pages")]
|
||||
pub pages: PdfPageTree,
|
||||
// TODO
|
||||
#[pdf(flatten)]
|
||||
pub rest: PdfDictionary,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfResourcesDictionary {
|
||||
#[pdf(name = "Font")]
|
||||
pub fonts: PdfDictionary<PdfFont>,
|
||||
#[pdf(flatten)]
|
||||
pub rest: PdfDictionary,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PdfPageTree {
|
||||
page_tree: PdfPageTreeNode,
|
||||
pages: Arc<[PdfPage]>,
|
||||
}
|
||||
|
||||
impl PdfPageTree {
|
||||
fn parse_pages(node: &PdfPageTreeNode, pages: &mut Vec<PdfPage>) -> Result<(), PdfParseError> {
|
||||
for kid in node.kids.iter() {
|
||||
match kid {
|
||||
PdfPageTreeNodeOrLeaf::Node(node) => Self::parse_pages(node, pages)?,
|
||||
PdfPageTreeNodeOrLeaf::Leaf(leaf) => {
|
||||
pages.push(PdfPage::parse_after_propagating_inheritable_data(
|
||||
leaf.clone(),
|
||||
)?);
|
||||
}
|
||||
PdfPageTreeNodeOrLeaf::Other(v) => {
|
||||
return Err(PdfParseError::InvalidType {
|
||||
pos: v.pos(),
|
||||
ty: "dictionary",
|
||||
expected_ty: "PdfPageTreeNodeOrLeaf",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn try_from_page_tree_root(mut page_tree: PdfPageTreeNode) -> Result<Self, PdfParseError> {
|
||||
page_tree.propagate_inheritable_data_to_leaves();
|
||||
let mut pages = Vec::new();
|
||||
Self::parse_pages(&page_tree, &mut pages)?;
|
||||
Ok(Self {
|
||||
page_tree,
|
||||
pages: Arc::from(pages),
|
||||
})
|
||||
}
|
||||
pub fn page_tree(&self) -> &PdfPageTreeNode {
|
||||
&self.page_tree
|
||||
}
|
||||
pub fn pages(&self) -> &Arc<[PdfPage]> {
|
||||
&self.pages
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfPageTree {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PdfPageTree")
|
||||
.field("pages", &self.pages)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfPageTree {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
self.page_tree.is_pdf_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfPageTree {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("PdfPageTree")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Self::try_from_page_tree_root(PdfParse::parse(object)?)
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PdfPageInheritableData {
|
||||
#[pdf(name = "Resources")]
|
||||
pub resources: Option<PdfResourcesDictionary>,
|
||||
#[pdf(name = "MediaBox")]
|
||||
pub media_box: Option<PdfRectangle>,
|
||||
#[pdf(name = "CropBox")]
|
||||
pub crop_box: Option<PdfRectangle>,
|
||||
#[pdf(name = "Rotate")]
|
||||
pub rotate: Option<PdfPageRotation>,
|
||||
#[pdf(flatten)]
|
||||
pub rest: PdfDictionary,
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfPageInheritableData {
|
||||
pub fn propagate_to(&self, target: &mut Self) {
|
||||
let Self {
|
||||
resources,
|
||||
media_box,
|
||||
crop_box,
|
||||
rotate,
|
||||
rest: _,
|
||||
} = self;
|
||||
fn propagate_to<T: Clone>(this: &Option<T>, target: &mut Option<T>) {
|
||||
if let (Some(this), target @ None) = (this, target) {
|
||||
*target = Some(this.clone());
|
||||
}
|
||||
}
|
||||
propagate_to(resources, &mut target.resources);
|
||||
propagate_to(media_box, &mut target.media_box);
|
||||
propagate_to(crop_box, &mut target.crop_box);
|
||||
propagate_to(rotate, &mut target.rotate);
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub enum PdfPageTreeNodeType {
|
||||
#[pdf(name = "Pages")]
|
||||
#[default]
|
||||
Pages,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfPageTreeNode {
|
||||
#[pdf(name = "Type")]
|
||||
pub ty: PdfPageTreeNodeType,
|
||||
#[pdf(name = "Parent")]
|
||||
pub parent: Option<PdfObjectIndirect>,
|
||||
#[pdf(name = "Kids")]
|
||||
pub kids: Arc<[PdfPageTreeNodeOrLeaf]>,
|
||||
#[pdf(name = "Count")]
|
||||
pub count: usize,
|
||||
// TODO
|
||||
#[pdf(flatten)]
|
||||
pub inheritable: PdfPageInheritableData,
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfPageTreeNode {
|
||||
pub fn propagate_inheritable_data_to_leaves(&mut self) {
|
||||
for kid in Arc::make_mut(&mut self.kids) {
|
||||
if let Some(target) = kid.inheritable_data_mut() {
|
||||
self.inheritable.propagate_to(target);
|
||||
}
|
||||
kid.propagate_inheritable_data_to_leaves();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub enum PdfPageType {
|
||||
#[pdf(name = "Page")]
|
||||
#[default]
|
||||
Page,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum PdfPageAnnotationsTabOrder {
|
||||
#[pdf(name = "R")]
|
||||
RowOrder,
|
||||
#[pdf(name = "C")]
|
||||
ColumnOrder,
|
||||
#[pdf(name = "S")]
|
||||
StructureOrder,
|
||||
#[pdf(other)]
|
||||
Other(PdfName),
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfPageTreeLeaf {
|
||||
#[pdf(name = "Type")]
|
||||
pub ty: PdfPageType,
|
||||
#[pdf(name = "Parent")]
|
||||
pub parent: PdfObjectIndirect,
|
||||
#[pdf(name = "LastModified")]
|
||||
pub last_modified: Option<PdfDate>,
|
||||
#[pdf(name = "BleedBox")]
|
||||
pub bleed_box: Option<PdfRectangle>,
|
||||
#[pdf(name = "TrimBox")]
|
||||
pub trim_box: Option<PdfRectangle>,
|
||||
#[pdf(name = "ArtBox")]
|
||||
pub art_box: Option<PdfRectangle>,
|
||||
#[pdf(name = "BoxColorInfo")]
|
||||
pub box_color_info: Option<PdfDictionary>,
|
||||
#[pdf(name = "Contents")]
|
||||
pub contents: MaybeArray<PdfStream>,
|
||||
#[pdf(name = "Group")]
|
||||
pub group: Option<PdfDictionary>,
|
||||
#[pdf(name = "Thumb")]
|
||||
pub thumbnail: Option<PdfStream>,
|
||||
#[pdf(name = "B")]
|
||||
pub beads: Option<Arc<[PdfDictionary]>>,
|
||||
#[pdf(name = "Dur")]
|
||||
pub duration: Option<f32>,
|
||||
#[pdf(name = "Trans")]
|
||||
pub transition: Option<PdfDictionary>,
|
||||
#[pdf(name = "Annots")]
|
||||
pub annotations: Option<Arc<[PdfDictionary]>>,
|
||||
#[pdf(name = "AA")]
|
||||
pub additional_actions: Option<PdfDictionary>,
|
||||
#[pdf(name = "Metadata")]
|
||||
pub metadata: Option<PdfStream>,
|
||||
#[pdf(name = "PieceInfo")]
|
||||
pub piece_info: Option<PdfDictionary>,
|
||||
#[pdf(name = "StructParents")]
|
||||
pub structural_parents: Option<PdfInteger>,
|
||||
#[pdf(name = "ID")]
|
||||
pub parent_web_capture_content_set_id: Option<PdfString>,
|
||||
#[pdf(name = "PZ")]
|
||||
pub preferred_zoom_factor: Option<f32>,
|
||||
#[pdf(name = "SeparationInfo")]
|
||||
pub separation_info: Option<PdfDictionary>,
|
||||
#[pdf(name = "Tabs")]
|
||||
pub annotations_tab_order: Option<PdfPageAnnotationsTabOrder>,
|
||||
#[pdf(name = "TemplateInstantiated")]
|
||||
pub template_instantiated: Option<PdfName>,
|
||||
#[pdf(name = "PresSteps")]
|
||||
pub pres_steps: Option<PdfDictionary>,
|
||||
#[pdf(name = "UserUnit")]
|
||||
pub user_unit: Option<f32>,
|
||||
#[pdf(name = "VP")]
|
||||
pub viewports: Option<Arc<[PdfDictionary]>>,
|
||||
#[pdf(flatten)]
|
||||
pub inheritable: PdfPageInheritableData,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(tag = "Type")]
|
||||
#[derive(Clone)]
|
||||
pub enum PdfPageTreeNodeOrLeaf {
|
||||
#[pdf(tag_value = "Pages")]
|
||||
Node(PdfPageTreeNode),
|
||||
#[pdf(tag_value = "Page")]
|
||||
Leaf(PdfPageTreeLeaf),
|
||||
#[pdf(other)]
|
||||
Other(PdfDictionary),
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfPageTreeNodeOrLeaf {
|
||||
pub fn propagate_inheritable_data_to_leaves(&mut self) {
|
||||
match self {
|
||||
PdfPageTreeNodeOrLeaf::Node(v) => v.propagate_inheritable_data_to_leaves(),
|
||||
PdfPageTreeNodeOrLeaf::Leaf(_) | PdfPageTreeNodeOrLeaf::Other(_) => {}
|
||||
}
|
||||
}
|
||||
pub fn inheritable_data_mut(&mut self) -> Option<&mut PdfPageInheritableData> {
|
||||
match self {
|
||||
PdfPageTreeNodeOrLeaf::Node(v) => Some(&mut v.inheritable),
|
||||
PdfPageTreeNodeOrLeaf::Leaf(v) => Some(&mut v.inheritable),
|
||||
PdfPageTreeNodeOrLeaf::Other(_) => None,
|
||||
}
|
||||
}
|
||||
pub fn inheritable_data(&self) -> Option<&PdfPageInheritableData> {
|
||||
match self {
|
||||
PdfPageTreeNodeOrLeaf::Node(v) => Some(&v.inheritable),
|
||||
PdfPageTreeNodeOrLeaf::Leaf(v) => Some(&v.inheritable),
|
||||
PdfPageTreeNodeOrLeaf::Other(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfPageTreeNodeOrLeaf {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Node(v) => v.fmt(f),
|
||||
Self::Leaf(v) => v.fmt(f),
|
||||
Self::Other(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// the amount by which the page is rotated clockwise when displaying or printing, is always a multiple of 90 degrees.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub enum PdfPageRotation {
|
||||
#[default]
|
||||
NoRotation = 0,
|
||||
ClockwiseBy90Degrees = 90,
|
||||
By180Degrees = 180,
|
||||
ClockwiseBy270Degrees = 270,
|
||||
}
|
||||
|
||||
impl PdfPageRotation {
|
||||
pub fn from_clockwise_angle_in_degrees(angle: i32) -> Option<Self> {
|
||||
match angle.rem_euclid(360) {
|
||||
0 => Some(Self::NoRotation),
|
||||
90 => Some(Self::ClockwiseBy90Degrees),
|
||||
180 => Some(Self::By180Degrees),
|
||||
270 => Some(Self::ClockwiseBy270Degrees),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn from_clockwise_angle_in_degrees_i128(angle: i128) -> Option<Self> {
|
||||
Self::from_clockwise_angle_in_degrees((angle % 360) as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PdfPageRotation> for i32 {
|
||||
fn from(value: PdfPageRotation) -> Self {
|
||||
value as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfPageRotation {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfPageRotation {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("page rotation")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let object = PdfObjectDirect::from(object);
|
||||
let pos = object.pos();
|
||||
let angle = PdfInteger::parse(object.into())?;
|
||||
Self::from_clockwise_angle_in_degrees_i128(angle.value())
|
||||
.ok_or(PdfParseError::IntegerOutOfRange { pos })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfPage {
|
||||
pub ty: PdfPageType,
|
||||
pub parent: PdfObjectIndirect,
|
||||
pub last_modified: Option<PdfDate>,
|
||||
pub resources: PdfResourcesDictionary,
|
||||
pub media_box: PdfRectangle,
|
||||
pub crop_box: PdfRectangle,
|
||||
pub bleed_box: PdfRectangle,
|
||||
pub trim_box: PdfRectangle,
|
||||
pub art_box: PdfRectangle,
|
||||
pub box_color_info: Option<PdfDictionary>,
|
||||
pub contents: Arc<[PdfStream]>,
|
||||
pub rotate: PdfPageRotation,
|
||||
pub group: Option<PdfDictionary>,
|
||||
pub thumbnail: Option<PdfStream>,
|
||||
pub beads: Option<Arc<[PdfDictionary]>>,
|
||||
pub duration: Option<f32>,
|
||||
pub transition: Option<PdfDictionary>,
|
||||
pub annotations: Option<Arc<[PdfDictionary]>>,
|
||||
pub additional_actions: Option<PdfDictionary>,
|
||||
pub metadata: Option<PdfStream>,
|
||||
pub piece_info: Option<PdfDictionary>,
|
||||
pub structural_parents: Option<PdfInteger>,
|
||||
pub parent_web_capture_content_set_id: Option<PdfString>,
|
||||
pub preferred_zoom_factor: Option<f32>,
|
||||
pub separation_info: Option<PdfDictionary>,
|
||||
pub annotations_tab_order: Option<PdfPageAnnotationsTabOrder>,
|
||||
pub template_instantiated: Option<PdfName>,
|
||||
pub pres_steps: Option<PdfDictionary>,
|
||||
pub user_unit: f32,
|
||||
pub viewports: Option<Arc<[PdfDictionary]>>,
|
||||
pub rest: PdfDictionary,
|
||||
}
|
||||
|
||||
impl PdfPage {
|
||||
pub fn parse_after_propagating_inheritable_data(
|
||||
leaf: PdfPageTreeLeaf,
|
||||
) -> Result<Self, PdfParseError> {
|
||||
let PdfPageTreeLeaf {
|
||||
ty,
|
||||
parent,
|
||||
last_modified,
|
||||
bleed_box,
|
||||
trim_box,
|
||||
art_box,
|
||||
box_color_info,
|
||||
contents,
|
||||
group,
|
||||
thumbnail,
|
||||
beads,
|
||||
duration,
|
||||
transition,
|
||||
annotations,
|
||||
additional_actions,
|
||||
metadata,
|
||||
piece_info,
|
||||
structural_parents,
|
||||
parent_web_capture_content_set_id,
|
||||
preferred_zoom_factor,
|
||||
separation_info,
|
||||
annotations_tab_order,
|
||||
template_instantiated,
|
||||
pres_steps,
|
||||
user_unit,
|
||||
viewports,
|
||||
inheritable:
|
||||
PdfPageInheritableData {
|
||||
resources,
|
||||
media_box,
|
||||
crop_box,
|
||||
rotate,
|
||||
rest,
|
||||
},
|
||||
} = leaf;
|
||||
let pos = rest.pos();
|
||||
let resources = resources.ok_or(PdfParseError::InvalidType {
|
||||
pos,
|
||||
ty: "null",
|
||||
expected_ty: "page resources dictionary",
|
||||
})?;
|
||||
let media_box = media_box.ok_or(PdfParseError::InvalidType {
|
||||
pos,
|
||||
ty: "null",
|
||||
expected_ty: "page MediaBox rectangle",
|
||||
})?;
|
||||
let crop_box = crop_box.unwrap_or(media_box);
|
||||
let rotate = rotate.unwrap_or(PdfPageRotation::NoRotation);
|
||||
Ok(Self {
|
||||
ty,
|
||||
parent,
|
||||
last_modified,
|
||||
resources,
|
||||
media_box,
|
||||
crop_box,
|
||||
bleed_box: bleed_box.unwrap_or(crop_box),
|
||||
trim_box: trim_box.unwrap_or(crop_box),
|
||||
art_box: art_box.unwrap_or(crop_box),
|
||||
box_color_info,
|
||||
contents: contents.0,
|
||||
rotate,
|
||||
group,
|
||||
thumbnail,
|
||||
beads,
|
||||
duration,
|
||||
transition,
|
||||
annotations,
|
||||
additional_actions,
|
||||
metadata,
|
||||
piece_info,
|
||||
structural_parents,
|
||||
parent_web_capture_content_set_id,
|
||||
preferred_zoom_factor,
|
||||
separation_info,
|
||||
annotations_tab_order,
|
||||
template_instantiated,
|
||||
pres_steps,
|
||||
user_unit: user_unit.unwrap_or(1.0),
|
||||
viewports,
|
||||
rest,
|
||||
})
|
||||
}
|
||||
}
|
||||
236
src/pdf/font.rs
Normal file
236
src/pdf/font.rs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
use std::{borrow::Cow, sync::Arc};
|
||||
|
||||
use crate::pdf::{
|
||||
object::{IsPdfNull, PdfDictionary, PdfName, PdfObject, PdfObjectDirect, PdfStream},
|
||||
parse::{PdfParse, PdfParseError},
|
||||
pdf_parse,
|
||||
};
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(transparent)]
|
||||
#[derive(Clone, Debug)]
|
||||
// TODO: actually parse the stream
|
||||
pub struct PdfFontToUnicode {
|
||||
#[pdf]
|
||||
stream: PdfStream,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(transparent)]
|
||||
#[derive(Clone, Debug)]
|
||||
// TODO: actually parse the dictionary
|
||||
pub struct PdfFontDescriptor {
|
||||
#[pdf]
|
||||
dictionary: PdfDictionary,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub enum PdfFontType {
|
||||
#[pdf(name = "Font")]
|
||||
#[default]
|
||||
Font,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PdfTodo {}
|
||||
|
||||
impl IsPdfNull for PdfTodo {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
match *self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfTodo {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("PdfTodo")
|
||||
}
|
||||
#[track_caller]
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
todo!("{object:?}")
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(tag = "Subtype")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PdfFont {
|
||||
#[pdf(tag_value = "Type0")]
|
||||
Type0(PdfFontType0),
|
||||
#[pdf(tag_value = "Type1")]
|
||||
Type1(PdfFontType1),
|
||||
#[pdf(other)]
|
||||
Other(PdfTodo),
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub enum PdfFontType0Subtype {
|
||||
#[pdf(name = "Type0")]
|
||||
#[default]
|
||||
Type0,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfFontType0 {
|
||||
#[pdf(name = "Type")]
|
||||
pub ty: PdfFontType,
|
||||
#[pdf(name = "Subtype")]
|
||||
pub subtype: PdfFontType0Subtype,
|
||||
#[pdf(name = "BaseFont")]
|
||||
pub base_font: PdfName,
|
||||
#[pdf(name = "Encoding")]
|
||||
// TODO
|
||||
pub encoding: PdfObjectDirect,
|
||||
#[pdf(name = "DescendentFonts")]
|
||||
// TODO
|
||||
pub descendent_fonts: [PdfDictionary; 1],
|
||||
#[pdf(name = "ToUnicode")]
|
||||
pub to_unicode: Option<PdfFontToUnicode>,
|
||||
#[pdf(flatten)]
|
||||
pub rest: PdfDictionary,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
|
||||
pub enum PdfFontType1Subtype {
|
||||
#[pdf(name = "Type1")]
|
||||
#[default]
|
||||
Type1,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum PdfStandardFontName {
|
||||
#[pdf(name = "Times-Roman")]
|
||||
TimesRoman,
|
||||
#[pdf(name = "Helvetica")]
|
||||
Helvetica,
|
||||
#[pdf(name = "Courier")]
|
||||
Courier,
|
||||
#[pdf(name = "Symbol")]
|
||||
Symbol,
|
||||
#[pdf(name = "Times-Bold")]
|
||||
TimesBold,
|
||||
#[pdf(name = "Helvetica-Bold")]
|
||||
HelveticaBold,
|
||||
#[pdf(name = "Courier-Bold")]
|
||||
CourierBold,
|
||||
#[pdf(name = "ZapfDingbats")]
|
||||
ZapfDingbats,
|
||||
#[pdf(name = "Times-Italic")]
|
||||
TimesItalic,
|
||||
#[pdf(name = "Helvetica-Oblique")]
|
||||
HelveticaOblique,
|
||||
#[pdf(name = "Courier-Oblique")]
|
||||
CourierOblique,
|
||||
#[pdf(name = "Times-BoldItalic")]
|
||||
TimesBoldItalic,
|
||||
#[pdf(name = "Helvetica-BoldOblique")]
|
||||
HelveticaBoldOblique,
|
||||
#[pdf(name = "Courier-BoldOblique")]
|
||||
CourierBoldOblique,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PdfFontType1 {
|
||||
Standard(PdfFontType1Standard),
|
||||
Other(PdfFontType1Other),
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfFontType1 {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfFontType1 {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("PdfFontType1")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let object = object.into();
|
||||
let PdfObjectDirect::Dictionary(object) = object else {
|
||||
return PdfFontType1Other::parse(object.into()).map(Self::Other);
|
||||
};
|
||||
if let Ok(_) = PdfStandardFontName::parse(object.get_or_null(b"BaseFont".as_slice())) {
|
||||
PdfFontType1Standard::parse(object.into()).map(Self::Standard)
|
||||
} else {
|
||||
PdfFontType1Other::parse(object.into()).map(Self::Other)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfFontType1Standard {
|
||||
#[pdf(name = "Type")]
|
||||
pub ty: PdfFontType,
|
||||
#[pdf(name = "Subtype")]
|
||||
pub subtype: PdfFontType1Subtype,
|
||||
#[pdf(name = "Name")]
|
||||
pub name: Option<PdfName>,
|
||||
#[pdf(name = "BaseFont")]
|
||||
pub base_font: PdfStandardFontName,
|
||||
#[pdf(name = "FirstChar")]
|
||||
pub first_char: Option<u32>,
|
||||
#[pdf(name = "LastChar")]
|
||||
pub last_char: Option<u32>,
|
||||
#[pdf(name = "Widths")]
|
||||
pub widths: Option<Arc<[f32]>>,
|
||||
#[pdf(name = "FontDescriptor")]
|
||||
pub font_descriptor: Option<PdfFontDescriptor>,
|
||||
#[pdf(name = "Encoding")]
|
||||
// TODO
|
||||
pub encoding: PdfObjectDirect,
|
||||
#[pdf(name = "ToUnicode")]
|
||||
pub to_unicode: Option<PdfFontToUnicode>,
|
||||
#[pdf(flatten)]
|
||||
pub rest: PdfDictionary,
|
||||
}
|
||||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfFontType1Other {
|
||||
#[pdf(name = "Type")]
|
||||
pub ty: PdfFontType,
|
||||
#[pdf(name = "Subtype")]
|
||||
pub subtype: PdfFontType1Subtype,
|
||||
#[pdf(name = "Name")]
|
||||
pub name: Option<PdfName>,
|
||||
#[pdf(name = "BaseFont")]
|
||||
pub base_font: PdfName,
|
||||
#[pdf(name = "FirstChar")]
|
||||
pub first_char: u32,
|
||||
#[pdf(name = "LastChar")]
|
||||
pub last_char: u32,
|
||||
#[pdf(name = "Widths")]
|
||||
pub widths: Arc<[f32]>,
|
||||
#[pdf(name = "FontDescriptor")]
|
||||
pub font_descriptor: PdfFontDescriptor,
|
||||
#[pdf(name = "Encoding")]
|
||||
// TODO
|
||||
pub encoding: PdfObjectDirect,
|
||||
#[pdf(name = "ToUnicode")]
|
||||
pub to_unicode: Option<PdfFontToUnicode>,
|
||||
#[pdf(flatten)]
|
||||
pub rest: PdfDictionary,
|
||||
}
|
||||
}
|
||||
|
|
@ -28,11 +28,23 @@ pub struct PdfString {
|
|||
|
||||
impl std::fmt::Debug for PdfString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self { pos, bytes } = self;
|
||||
f.debug_struct("PdfString")
|
||||
.field("pos", pos)
|
||||
.field("bytes", &format_args!("b\"{}\"", bytes.escape_ascii()))
|
||||
.finish()
|
||||
let Self { pos, bytes: _ } = self;
|
||||
write!(f, "PdfString(at {pos}, {})", self.bytes_debug())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PdfStringBytesDebug<'a>(&'a [u8]);
|
||||
|
||||
impl<'a> fmt::Display for PdfStringBytesDebug<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "b\"{}\"", self.0.escape_ascii())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for PdfStringBytesDebug<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +61,9 @@ impl PdfString {
|
|||
pub fn bytes(&self) -> &ArcOrRef<'static, [u8]> {
|
||||
&self.bytes
|
||||
}
|
||||
pub fn bytes_debug(&self) -> PdfStringBytesDebug<'_> {
|
||||
PdfStringBytesDebug(&self.bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfString {
|
||||
|
|
@ -57,6 +72,50 @@ impl GetPdfInputPosition for PdfString {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct PdfDate {
|
||||
text: PdfString,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfDate {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self { text } = self;
|
||||
let pos = text.pos();
|
||||
write!(f, "PdfDate(at {pos}, {})", text.bytes_debug())
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfDate {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfDate {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("date")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Self::try_new(PdfString::parse(object)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfDate {
|
||||
pub fn try_new(text: PdfString) -> Result<Self, PdfParseError> {
|
||||
// TODO: check syntax
|
||||
Ok(Self { text })
|
||||
}
|
||||
pub fn text(&self) -> &PdfString {
|
||||
&self.text
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfDate {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
self.text.pos()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct PdfName {
|
||||
pos: PdfInputPositionNoCompare,
|
||||
|
|
@ -138,12 +197,19 @@ impl fmt::Display for PdfName {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct PdfBoolean {
|
||||
pos: PdfInputPositionNoCompare,
|
||||
value: bool,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfBoolean {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self { pos, value } = *self;
|
||||
write!(f, "PdfBoolean(at {pos}, {value})")
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfBoolean {
|
||||
pub fn new(pos: impl Into<PdfInputPositionNoCompare>, value: bool) -> Self {
|
||||
Self {
|
||||
|
|
@ -165,12 +231,19 @@ impl GetPdfInputPosition for PdfBoolean {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct PdfInteger {
|
||||
pos: PdfInputPositionNoCompare,
|
||||
value: i128,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfInteger {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self { pos, value } = *self;
|
||||
write!(f, "PdfInteger(at {pos}, {value})")
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfInteger {
|
||||
pub fn new(pos: impl Into<PdfInputPositionNoCompare>, value: i128) -> Self {
|
||||
Self {
|
||||
|
|
@ -192,12 +265,19 @@ impl GetPdfInputPosition for PdfInteger {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Default)]
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Default)]
|
||||
pub struct PdfReal {
|
||||
pos: PdfInputPositionNoCompare,
|
||||
value: f64,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfReal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self { pos, value } = *self;
|
||||
write!(f, "PdfReal(at {pos}, {value})")
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfReal {
|
||||
pub fn new(pos: impl Into<PdfInputPositionNoCompare>, value: f64) -> Self {
|
||||
Self {
|
||||
|
|
@ -219,6 +299,114 @@ impl GetPdfInputPosition for PdfReal {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PdfNumber {
|
||||
Integer(PdfInteger),
|
||||
Real(PdfReal),
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfNumber {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Integer(v) => v.fmt(f),
|
||||
Self::Real(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfNumber {
|
||||
pub fn pos(self) -> PdfInputPosition {
|
||||
match self {
|
||||
Self::Integer(v) => v.pos(),
|
||||
Self::Real(v) => v.pos(),
|
||||
}
|
||||
}
|
||||
pub fn as_f64(self) -> f64 {
|
||||
match self {
|
||||
Self::Integer(v) => v.value as f64,
|
||||
Self::Real(v) => v.value,
|
||||
}
|
||||
}
|
||||
pub fn as_f32(self) -> f32 {
|
||||
match self {
|
||||
Self::Integer(v) => v.value as f32,
|
||||
Self::Real(v) => v.value as f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PdfNumber {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
match (self, other) {
|
||||
(Self::Integer(this), Self::Integer(other)) => Some(this.cmp(other)),
|
||||
_ => self.as_f64().partial_cmp(&other.as_f64()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PdfNumber {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.partial_cmp(other).is_some_and(|v| v.is_eq())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PdfNumber {
|
||||
fn default() -> Self {
|
||||
PdfNumber::Integer(PdfInteger::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfObjectDirect {
|
||||
pub fn number(&self) -> Option<PdfNumber> {
|
||||
match *self {
|
||||
PdfObjectDirect::Integer(v) => Some(PdfNumber::Integer(v)),
|
||||
PdfObjectDirect::Real(v) => Some(PdfNumber::Real(v)),
|
||||
PdfObjectDirect::Boolean(_)
|
||||
| PdfObjectDirect::String(_)
|
||||
| PdfObjectDirect::Name(_)
|
||||
| PdfObjectDirect::Array(_)
|
||||
| PdfObjectDirect::Dictionary(_)
|
||||
| PdfObjectDirect::Stream(_)
|
||||
| PdfObjectDirect::Null(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfObjectNonNull {
|
||||
pub fn number(&self) -> Option<PdfNumber> {
|
||||
match *self {
|
||||
PdfObjectNonNull::Integer(v) => Some(PdfNumber::Integer(v)),
|
||||
PdfObjectNonNull::Real(v) => Some(PdfNumber::Real(v)),
|
||||
PdfObjectNonNull::Boolean(_)
|
||||
| PdfObjectNonNull::String(_)
|
||||
| PdfObjectNonNull::Name(_)
|
||||
| PdfObjectNonNull::Array(_)
|
||||
| PdfObjectNonNull::Dictionary(_)
|
||||
| PdfObjectNonNull::Stream(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfNumber {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfNumber {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("number")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let object = PdfObjectDirect::from(object);
|
||||
object.number().ok_or(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "number",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! make_pdf_object {
|
||||
(
|
||||
$(
|
||||
|
|
@ -239,12 +427,24 @@ macro_rules! make_pdf_object {
|
|||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfObjectNonNull {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum PdfObjectDirect {
|
||||
$($Variant($ty),)*
|
||||
Null(PdfNull),
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfObjectDirect {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfObjectDirect {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
|
@ -261,6 +461,12 @@ macro_rules! make_pdf_object {
|
|||
Indirect(PdfObjectIndirect),
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfObject {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
self.is_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfObject {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
|
@ -308,14 +514,20 @@ macro_rules! make_pdf_object {
|
|||
}
|
||||
}
|
||||
|
||||
$(impl crate::pdf::parse::PdfParse for $ty {
|
||||
$(impl IsPdfNull for $ty {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for $ty {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed($type_name)
|
||||
}
|
||||
fn $parse(object: PdfObject) -> Result<Self, crate::pdf::parse::PdfParseError> {
|
||||
fn $parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
match PdfObjectDirect::from(object) {
|
||||
PdfObjectDirect::$Variant(v) => Ok(v),
|
||||
object => Err(crate::pdf::parse::PdfParseError::InvalidType {
|
||||
object => Err(PdfParseError::InvalidType {
|
||||
pos: object.get_pdf_input_position(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: $type_name,
|
||||
|
|
@ -445,7 +657,7 @@ macro_rules! make_pdf_object {
|
|||
}
|
||||
|
||||
const _: () = {
|
||||
fn _assert_parsable<T: crate::pdf::parse::PdfParse>() {}
|
||||
fn _assert_parsable<T: PdfParse>() {}
|
||||
|
||||
$(let _ = _assert_parsable::<$ty>;)*
|
||||
let _ = _assert_parsable::<PdfNull>;
|
||||
|
|
@ -470,15 +682,21 @@ make_pdf_object! {
|
|||
Name(PdfName),
|
||||
#[parse = parse, type_name = "array"]
|
||||
Array(PdfArray),
|
||||
#[parse = parse, type_name = "dictionary"]
|
||||
#[parse =, type_name = "dictionary"]
|
||||
Dictionary(PdfDictionary),
|
||||
#[parse =, type_name = "stream"]
|
||||
Stream(PdfStream),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct PdfNull(PdfInputPositionNoCompare);
|
||||
|
||||
impl fmt::Debug for PdfNull {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "PdfNull(at {})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfNull {
|
||||
pub fn new(pos: impl Into<PdfInputPositionNoCompare>) -> Self {
|
||||
Self(pos.into())
|
||||
|
|
@ -521,13 +739,27 @@ impl From<PdfObjectIndirect> for PdfObject {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct PdfObjectIdentifier {
|
||||
pub pos: PdfInputPositionNoCompare,
|
||||
pub object_number: NonZero<u32>,
|
||||
pub generation_number: u16,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfObjectIdentifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
pos,
|
||||
object_number,
|
||||
generation_number,
|
||||
} = *self;
|
||||
write!(
|
||||
f,
|
||||
"PdfObjectIdentifier(at {pos}, {object_number}, {generation_number})"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfObjectIdentifier {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
self.pos.0
|
||||
|
|
@ -545,12 +777,18 @@ impl fmt::Debug for PdfObjectIndirect {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
objects: _,
|
||||
id,
|
||||
id:
|
||||
PdfObjectIdentifier {
|
||||
pos,
|
||||
object_number,
|
||||
generation_number,
|
||||
},
|
||||
final_id: _,
|
||||
} = self;
|
||||
f.debug_struct("PdfObjectIndirect")
|
||||
.field("id", id)
|
||||
.finish_non_exhaustive()
|
||||
} = *self;
|
||||
write!(
|
||||
f,
|
||||
"PdfObjectIndirect(at {pos}, {object_number}, {generation_number})"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -627,13 +865,31 @@ impl From<PdfObjectIndirect> for PdfObjectDirect {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PdfDictionary {
|
||||
pos: PdfInputPositionNoCompare,
|
||||
fields: Arc<BTreeMap<PdfName, PdfObject>>,
|
||||
pub trait IsPdfNull {
|
||||
fn is_pdf_null(&self) -> bool;
|
||||
}
|
||||
|
||||
impl PdfDictionary {
|
||||
impl<T: IsPdfNull> IsPdfNull for Option<T> {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
self.as_ref().is_none_or(IsPdfNull::is_pdf_null)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PdfDictionary<T = PdfObject> {
|
||||
pos: PdfInputPositionNoCompare,
|
||||
fields: Arc<BTreeMap<PdfName, T>>,
|
||||
}
|
||||
|
||||
impl<T> Clone for PdfDictionary<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
pos: self.pos,
|
||||
fields: self.fields.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PdfDictionary<T> {
|
||||
pub fn new(pos: impl Into<PdfInputPositionNoCompare>) -> Self {
|
||||
Self {
|
||||
pos: pos.into(),
|
||||
|
|
@ -642,23 +898,26 @@ impl PdfDictionary {
|
|||
}
|
||||
pub fn from_fields(
|
||||
pos: impl Into<PdfInputPositionNoCompare>,
|
||||
mut fields: Arc<BTreeMap<PdfName, PdfObject>>,
|
||||
) -> Self {
|
||||
if fields.values().any(|v| matches!(v, PdfObject::Null(_))) {
|
||||
Arc::make_mut(&mut fields).retain(|_k, v| !matches!(v, PdfObject::Null(_)));
|
||||
mut fields: Arc<BTreeMap<PdfName, T>>,
|
||||
) -> Self
|
||||
where
|
||||
T: IsPdfNull + Clone,
|
||||
{
|
||||
if fields.values().any(T::is_pdf_null) {
|
||||
Arc::make_mut(&mut fields).retain(|_k, v| !v.is_pdf_null());
|
||||
}
|
||||
Self {
|
||||
pos: pos.into(),
|
||||
fields,
|
||||
}
|
||||
}
|
||||
pub fn fields(&self) -> &Arc<BTreeMap<PdfName, PdfObject>> {
|
||||
pub fn fields(&self) -> &Arc<BTreeMap<PdfName, T>> {
|
||||
&self.fields
|
||||
}
|
||||
pub fn into_fields(self) -> Arc<BTreeMap<PdfName, PdfObject>> {
|
||||
pub fn into_fields(self) -> Arc<BTreeMap<PdfName, T>> {
|
||||
self.fields
|
||||
}
|
||||
pub fn iter(&self) -> std::collections::btree_map::Iter<'_, PdfName, PdfObject> {
|
||||
pub fn iter(&self) -> std::collections::btree_map::Iter<'_, PdfName, T> {
|
||||
self.fields.iter()
|
||||
}
|
||||
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
|
||||
|
|
@ -668,75 +927,122 @@ impl PdfDictionary {
|
|||
{
|
||||
self.fields.contains_key(key)
|
||||
}
|
||||
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&PdfObject>
|
||||
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&T>
|
||||
where
|
||||
PdfName: std::borrow::Borrow<Q>,
|
||||
Q: Ord,
|
||||
{
|
||||
self.fields.get(key)
|
||||
}
|
||||
pub fn get_or_null<Q: ?Sized>(&self, key: &Q) -> PdfObject
|
||||
pub fn get_or_null<Q: ?Sized>(&self, key: &Q) -> T
|
||||
where
|
||||
PdfName: std::borrow::Borrow<Q>,
|
||||
Q: Ord,
|
||||
T: Clone + From<PdfNull>,
|
||||
{
|
||||
self.get(key)
|
||||
.cloned()
|
||||
.unwrap_or(PdfObject::Null(PdfNull(self.pos)))
|
||||
.unwrap_or_else(|| PdfNull(self.pos).into())
|
||||
}
|
||||
pub fn pos(&self) -> PdfInputPosition {
|
||||
self.pos.0
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfDictionary {
|
||||
impl<T> GetPdfInputPosition for PdfDictionary<T> {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
self.pos.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PdfDictionary {
|
||||
impl<T> Default for PdfDictionary<T> {
|
||||
fn default() -> Self {
|
||||
Self::new(PdfInputPosition::empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(PdfName, PdfObject)> for PdfDictionary {
|
||||
fn from_iter<T: IntoIterator<Item = (PdfName, PdfObject)>>(iter: T) -> Self {
|
||||
impl<T: IsPdfNull> FromIterator<(PdfName, T)> for PdfDictionary<T> {
|
||||
fn from_iter<I: IntoIterator<Item = (PdfName, T)>>(iter: I) -> Self {
|
||||
Self {
|
||||
pos: PdfInputPositionNoCompare::empty(),
|
||||
fields: Arc::new(BTreeMap::from_iter(
|
||||
iter.into_iter()
|
||||
.filter(|(_name, value)| !matches!(value, PdfObject::Null(_))),
|
||||
.filter(|(_name, value)| !value.is_pdf_null()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for PdfDictionary {
|
||||
type Item = (PdfName, PdfObject);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<PdfName, PdfObject>;
|
||||
impl<T: Clone> IntoIterator for PdfDictionary<T> {
|
||||
type Item = (PdfName, T);
|
||||
type IntoIter = std::collections::btree_map::IntoIter<PdfName, T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Arc::unwrap_or_clone(self.fields).into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a PdfDictionary {
|
||||
type Item = (&'a PdfName, &'a PdfObject);
|
||||
type IntoIter = std::collections::btree_map::Iter<'a, PdfName, PdfObject>;
|
||||
impl<'a, T> IntoIterator for &'a PdfDictionary<T> {
|
||||
type Item = (&'a PdfName, &'a T);
|
||||
type IntoIter = std::collections::btree_map::Iter<'a, PdfName, T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.fields.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfDictionary {
|
||||
impl<T: fmt::Debug> fmt::Debug for PdfDictionary<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_map().entries(self).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IsPdfNull for PdfDictionary<T> {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse> PdfParse for PdfDictionary<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
if TypeId::of::<T>() == TypeId::of::<PdfObject>() {
|
||||
Cow::Borrowed("dictionary")
|
||||
} else {
|
||||
Cow::Owned(format!("PdfDictionary<{}>", T::type_name()))
|
||||
}
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let object = PdfObjectDirect::from(object);
|
||||
let PdfObjectDirect::Dictionary(object) = object else {
|
||||
return Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "dictionary",
|
||||
});
|
||||
};
|
||||
if let Some(retval) = <dyn std::any::Any>::downcast_ref::<Self>(&object) {
|
||||
return Ok(retval.clone());
|
||||
}
|
||||
let pos = object.pos;
|
||||
let fields = Result::from_iter(object.fields.iter().filter_map(|(name, value)| {
|
||||
match T::parse(value.clone()) {
|
||||
Ok(value) => {
|
||||
if value.is_pdf_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Ok((name.clone(), value)))
|
||||
}
|
||||
}
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
}))?;
|
||||
Ok(Self {
|
||||
pos,
|
||||
fields: Arc::new(fields),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PdfArray {
|
||||
pos: PdfInputPositionNoCompare,
|
||||
|
|
@ -869,9 +1175,15 @@ impl fmt::Debug for PdfArray {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct MaybeArray<T>(pub Arc<[T]>);
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for MaybeArray<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for MaybeArray<T> {
|
||||
type Target = Arc<[T]>;
|
||||
|
||||
|
|
@ -901,12 +1213,138 @@ impl<'a, T> IntoIterator for &'a MaybeArray<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct PdfPoint {
|
||||
pub pos: PdfInputPositionNoCompare,
|
||||
pub x: f32,
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfPoint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self { pos, x, y } = *self;
|
||||
write!(f, "PdfPoint(at {pos}, {x}, {y})")
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfPoint {
|
||||
pub fn parse(x: PdfObject, y: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Ok(Self {
|
||||
pos: x.pos().into(),
|
||||
x: PdfParse::parse(x)?,
|
||||
y: PdfParse::parse(y)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfPoint {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
self.pos.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct PdfRectangle {
|
||||
/// the corner with the smaller x and y coordinates
|
||||
smaller: PdfPoint,
|
||||
/// the corner with the larger x and y coordinates
|
||||
larger: PdfPoint,
|
||||
}
|
||||
|
||||
impl PdfRectangle {
|
||||
pub fn new(mut smaller: PdfPoint, mut larger: PdfPoint) -> Self {
|
||||
// `pos` follows the `x` coordinate
|
||||
if smaller.x.is_nan() {
|
||||
smaller.pos = larger.pos;
|
||||
} else if larger.x.is_nan() {
|
||||
larger.pos = smaller.pos;
|
||||
} else if larger.x < smaller.x {
|
||||
std::mem::swap(&mut smaller.pos, &mut larger.pos);
|
||||
}
|
||||
Self {
|
||||
smaller: PdfPoint {
|
||||
pos: smaller.pos,
|
||||
x: smaller.x.min(larger.x),
|
||||
y: smaller.y.min(larger.y),
|
||||
},
|
||||
larger: PdfPoint {
|
||||
pos: larger.pos,
|
||||
x: smaller.x.max(larger.x),
|
||||
y: smaller.y.max(larger.y),
|
||||
},
|
||||
}
|
||||
}
|
||||
/// return the corner with the smaller x and y coordinates
|
||||
pub fn smaller(&self) -> PdfPoint {
|
||||
self.smaller
|
||||
}
|
||||
/// return the corner with the larger x and y coordinates
|
||||
pub fn larger(&self) -> PdfPoint {
|
||||
self.larger
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfRectangle {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
self.smaller.get_pdf_input_position()
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfRectangle {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfRectangle {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("rectangle")
|
||||
}
|
||||
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let object = object.into();
|
||||
let PdfObjectDirect::Array(array) = &object else {
|
||||
return Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "rectangle",
|
||||
});
|
||||
};
|
||||
let [lower_left_x, lower_left_y, upper_right_x, upper_right_y] = &**array.elements() else {
|
||||
return Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "rectangle",
|
||||
});
|
||||
};
|
||||
Ok(Self::new(
|
||||
PdfPoint::parse(lower_left_x.clone(), lower_left_y.clone())?,
|
||||
PdfPoint::parse(upper_right_x.clone(), upper_right_y.clone())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum PdfFileSpecification {
|
||||
String(PdfString),
|
||||
Dictionary(PdfDictionary),
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfFileSpecification {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::String(v) => v.fmt(f),
|
||||
Self::Dictionary(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfFileSpecification {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfFileSpecification {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("file specification")
|
||||
|
|
@ -925,6 +1363,7 @@ impl PdfParse for PdfFileSpecification {
|
|||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfStreamDictionary<Rest = PdfDictionary> {
|
||||
#[pdf(name = PdfStreamDictionary::LENGTH_NAME)]
|
||||
|
|
@ -1200,6 +1639,12 @@ impl<Rest> GetPdfInputPosition for PdfStream<Rest> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Rest> IsPdfNull for PdfStream<Rest> {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<Rest: PdfParse> PdfParse for PdfStream<Rest> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
if TypeId::of::<Rest>() == TypeId::of::<PdfDictionary>() {
|
||||
|
|
@ -1252,6 +1697,7 @@ impl<Rest: PdfParse> PdfParse for PdfStream<Rest> {
|
|||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Clone, Copy, Debug, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum PdfObjectStreamType {
|
||||
#[pdf(name = "ObjStm")]
|
||||
|
|
@ -1261,6 +1707,7 @@ pdf_parse! {
|
|||
}
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PdfObjectStreamDictionary {
|
||||
#[pdf(name = Self::TYPE_NAME)]
|
||||
|
|
|
|||
250
src/pdf/parse.rs
250
src/pdf/parse.rs
|
|
@ -1,24 +1,48 @@
|
|||
use crate::pdf::object::{
|
||||
MaybeArray, PdfInteger, PdfName, PdfNull, PdfObject, PdfObjectDirect, PdfObjectIdentifier,
|
||||
PdfObjectIndirect, PdfObjectNonNull, PdfReal,
|
||||
IsPdfNull, MaybeArray, PdfInteger, PdfName, PdfNull, PdfNumber, PdfObject, PdfObjectDirect,
|
||||
PdfObjectIdentifier, PdfObjectIndirect, PdfObjectNonNull,
|
||||
};
|
||||
use std::{any::Any, borrow::Cow, fmt, mem, num::NonZero, sync::Arc};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct PdfInputPositionKnown {
|
||||
pub pos: usize,
|
||||
pub containing_streams_pos: Option<usize>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfInputPositionKnown {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PdfInputPositionKnown {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
pos,
|
||||
containing_streams_pos,
|
||||
} = *self;
|
||||
if let Some(containing_streams_pos) = containing_streams_pos {
|
||||
write!(f, "{pos:#x} in stream at {containing_streams_pos:#x}")
|
||||
} else {
|
||||
write!(f, "{pos:#x}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
||||
pub struct PdfInputPosition(Option<usize>);
|
||||
pub struct PdfInputPosition(Option<PdfInputPositionKnown>);
|
||||
|
||||
impl fmt::Debug for PdfInputPosition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PdfInputPosition")
|
||||
.field(&format_args!("{self}"))
|
||||
.finish()
|
||||
write!(f, "at {self}")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PdfInputPosition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(pos) = self.0 {
|
||||
write!(f, "{pos:#x}")
|
||||
pos.fmt(f)
|
||||
} else {
|
||||
f.write_str("<unknown>")
|
||||
}
|
||||
|
|
@ -26,12 +50,15 @@ impl fmt::Display for PdfInputPosition {
|
|||
}
|
||||
|
||||
impl PdfInputPosition {
|
||||
pub const fn new(pos: usize) -> Self {
|
||||
Self(Some(pos))
|
||||
pub const fn new(pos: Option<PdfInputPositionKnown>) -> Self {
|
||||
Self(pos)
|
||||
}
|
||||
pub const fn empty() -> PdfInputPosition {
|
||||
Self(None)
|
||||
}
|
||||
pub const fn get(self) -> Option<PdfInputPositionKnown> {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetPdfInputPosition {
|
||||
|
|
@ -87,7 +114,7 @@ impl PdfInputPositionNoCompare {
|
|||
pub const fn empty() -> Self {
|
||||
Self(PdfInputPosition::empty())
|
||||
}
|
||||
pub const fn new(pos: usize) -> Self {
|
||||
pub const fn new(pos: Option<PdfInputPositionKnown>) -> Self {
|
||||
Self(PdfInputPosition::new(pos))
|
||||
}
|
||||
}
|
||||
|
|
@ -106,9 +133,7 @@ impl From<PdfInputPosition> for PdfInputPositionNoCompare {
|
|||
|
||||
impl fmt::Debug for PdfInputPositionNoCompare {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PdfInputPositionNoCompare")
|
||||
.field(&format_args!("{self}"))
|
||||
.finish()
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -431,7 +456,7 @@ impl fmt::Display for PdfParseError {
|
|||
|
||||
impl std::error::Error for PdfParseError {}
|
||||
|
||||
pub trait PdfParse: Sized + 'static {
|
||||
pub trait PdfParse: Sized + 'static + IsPdfNull {
|
||||
fn type_name() -> Cow<'static, str>;
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError>;
|
||||
fn parse_option(object: PdfObject) -> Result<Option<Self>, PdfParseError> {
|
||||
|
|
@ -469,6 +494,11 @@ impl<T: PdfParse> PdfParse for Option<T> {
|
|||
|
||||
macro_rules! impl_pdf_parse_prim_int {
|
||||
($ty:ident) => {
|
||||
impl IsPdfNull for $ty {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
impl PdfParse for $ty {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed(stringify!($ty))
|
||||
|
|
@ -480,6 +510,11 @@ macro_rules! impl_pdf_parse_prim_int {
|
|||
.map_err(|_| PdfParseError::IntegerOutOfRange { pos: v.pos() })
|
||||
}
|
||||
}
|
||||
impl IsPdfNull for NonZero<$ty> {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
impl PdfParse for NonZero<$ty> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed(concat!("NonZero<", stringify!($ty), ">"))
|
||||
|
|
@ -508,6 +543,12 @@ impl_pdf_parse_prim_int!(u128);
|
|||
impl_pdf_parse_prim_int!(usize);
|
||||
impl_pdf_parse_prim_int!(isize);
|
||||
|
||||
impl IsPdfNull for i128 {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for i128 {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("i128")
|
||||
|
|
@ -518,6 +559,12 @@ impl PdfParse for i128 {
|
|||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for NonZero<i128> {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for NonZero<i128> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("NonZero<i128>")
|
||||
|
|
@ -528,12 +575,24 @@ impl PdfParse for NonZero<i128> {
|
|||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for f64 {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for f64 {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("f64")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Ok(<PdfReal as PdfParse>::parse(object)?.value())
|
||||
Ok(<PdfNumber as PdfParse>::parse(object)?.as_f64())
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for f32 {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +601,13 @@ impl PdfParse for f32 {
|
|||
Cow::Borrowed("f32")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Ok(<f64 as PdfParse>::parse(object)? as f32)
|
||||
Ok(<PdfNumber as PdfParse>::parse(object)?.as_f32())
|
||||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfNull {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -599,6 +664,12 @@ impl PdfParse for PdfObject {
|
|||
}
|
||||
}
|
||||
|
||||
impl IsPdfNull for PdfObjectIndirect {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
self.get().is_pdf_null()
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfObjectIndirect {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("indirect object")
|
||||
|
|
@ -626,6 +697,12 @@ impl PdfParse for PdfObjectIndirect {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> IsPdfNull for [T; N] {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse, const N: usize> PdfParse for [T; N] {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("[{}; {N}]", T::type_name()))
|
||||
|
|
@ -661,6 +738,12 @@ impl<T: PdfParse, const N: usize> PdfParse for [T; N] {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IsPdfNull for Arc<[T]> {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse> PdfParse for Arc<[T]> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("Arc<[{}]>", T::type_name()))
|
||||
|
|
@ -688,6 +771,12 @@ impl<T: PdfParse> PdfParse for Arc<[T]> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IsPdfNull for MaybeArray<T> {
|
||||
fn is_pdf_null(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse> PdfParse for MaybeArray<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("MaybeArray<{}>", T::type_name()))
|
||||
|
|
@ -704,6 +793,7 @@ impl<T: PdfParse> PdfParse for MaybeArray<T> {
|
|||
#[macro_export]
|
||||
macro_rules! pdf_parse {
|
||||
(
|
||||
#[pdf $($struct_pdf_meta:tt)*]
|
||||
$(#[$($struct_meta:tt)*])*
|
||||
$struct_vis:vis struct $Struct:ident$(<$($StructParam:ident $(: $StructBound:tt)? $(= $StructParamDefault:ty)?),* $(,)?>)? {
|
||||
$(#[pdf $($pdf_meta:tt)*]
|
||||
|
|
@ -719,6 +809,7 @@ macro_rules! pdf_parse {
|
|||
|
||||
$crate::pdf::parse::pdf_parse! {
|
||||
@impl
|
||||
#[pdf $($struct_pdf_meta)*]
|
||||
struct $Struct$(<$($StructParam $(: $StructBound)?),*>)? {
|
||||
$(#[pdf $($pdf_meta)*]
|
||||
$(#[$($field_meta)*])*
|
||||
|
|
@ -728,11 +819,66 @@ macro_rules! pdf_parse {
|
|||
};
|
||||
(
|
||||
@impl
|
||||
#[pdf(transparent)]
|
||||
struct $Struct:ident$(<$($StructParam:ident $(: $StructBound:tt)?),* $(,)?>)? {
|
||||
#[pdf]
|
||||
$(#[$($field_meta:tt)*])*
|
||||
$field_name:ident: $field_ty:ty,
|
||||
$(#[pdf]
|
||||
$(#[$($phantom_meta:tt)*])*
|
||||
$phantom_name:ident: PhantomData<$phantom_ty:ty>,)?
|
||||
}
|
||||
) => {
|
||||
impl$(<$($StructParam: $crate::pdf::parse::IsPdfNull $(+ $StructBound)?),*>)? $crate::pdf::object::IsPdfNull for $Struct$(<$($StructParam),*>)? {
|
||||
fn is_pdf_null(&self) -> $crate::__std::primitive::bool {
|
||||
<$field_ty as $crate::pdf::object::IsPdfNull>::is_pdf_null(&self.$field_name)
|
||||
}
|
||||
}
|
||||
impl$(<$($StructParam: $crate::pdf::parse::PdfParse $(+ $StructBound)?),*>)? $crate::pdf::parse::PdfParse for $Struct$(<$($StructParam),*>)? {
|
||||
fn type_name() -> $crate::__std::borrow::Cow<'static, $crate::__std::primitive::str> {
|
||||
let args: &[$crate::__std::borrow::Cow<'static, $crate::__std::primitive::str>] = &[
|
||||
$($(<$StructParam as $crate::pdf::parse::PdfParse>::type_name()),*)?
|
||||
];
|
||||
if args.is_empty() {
|
||||
$crate::__std::borrow::Cow::Borrowed($crate::__std::stringify!($Struct))
|
||||
} else {
|
||||
let mut retval = $crate::__std::string::String::new();
|
||||
retval.push_str($crate::__std::stringify!($Struct));
|
||||
retval.push_str("<");
|
||||
let mut first = true;
|
||||
for arg in args {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
retval.push_str(", ");
|
||||
}
|
||||
retval.push_str(arg);
|
||||
}
|
||||
retval.push_str(">");
|
||||
$crate::__std::borrow::Cow::Owned(retval)
|
||||
}
|
||||
}
|
||||
fn parse(object: $crate::pdf::object::PdfObject) -> $crate::__std::result::Result<Self, $crate::pdf::parse::PdfParseError> {
|
||||
$crate::__std::result::Result::Ok(Self {
|
||||
$field_name: <$field_ty as $crate::pdf::parse::PdfParse>::parse(object)?,
|
||||
$($phantom_name: $crate::__std::marker::PhantomData,)?
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl
|
||||
#[pdf]
|
||||
struct $Struct:ident$(<$($StructParam:ident $(: $StructBound:tt)?),* $(,)?>)? {
|
||||
$($(#[$($field_meta:tt)*])*
|
||||
$field_name:ident: $field_ty:ty,)*
|
||||
}
|
||||
) => {
|
||||
impl$(<$($StructParam $(: $StructBound)?),*>)? $crate::pdf::object::IsPdfNull for $Struct$(<$($StructParam),*>)? {
|
||||
fn is_pdf_null(&self) -> $crate::__std::primitive::bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
impl$(<$($StructParam: $crate::pdf::parse::PdfParse $(+ $StructBound)?),*>)? $crate::pdf::parse::PdfParse for $Struct$(<$($StructParam),*>)? {
|
||||
fn type_name() -> $crate::__std::borrow::Cow<'static, $crate::__std::primitive::str> {
|
||||
let args: &[$crate::__std::borrow::Cow<'static, $crate::__std::primitive::str>] = &[
|
||||
|
|
@ -826,7 +972,7 @@ macro_rules! pdf_parse {
|
|||
[$(#[$($field_meta:tt)*])*]
|
||||
$field_name:ident: $field_ty:ty
|
||||
) => {
|
||||
let $field_name = $crate::__std::convert::AsRef::<[u8]>::as_ref($name);
|
||||
let $field_name = $crate::__std::convert::AsRef::<[$crate::__std::primitive::u8]>::as_ref($name);
|
||||
let $field_name = <$field_ty as $crate::pdf::parse::PdfParse>::parse(
|
||||
$object_mut
|
||||
.remove($field_name)
|
||||
|
|
@ -834,6 +980,7 @@ macro_rules! pdf_parse {
|
|||
)?;
|
||||
};
|
||||
(
|
||||
#[pdf $($enum_pdf_meta:tt)*]
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
$enum_vis:vis enum $Enum:ident {
|
||||
$(#[pdf $($pdf_meta:tt)*]
|
||||
|
|
@ -849,6 +996,7 @@ macro_rules! pdf_parse {
|
|||
|
||||
$crate::pdf::parse::pdf_parse! {
|
||||
@impl
|
||||
#[pdf $($enum_pdf_meta)*]
|
||||
$(#[$($enum_meta)*])*
|
||||
enum $Enum {
|
||||
$(#[pdf $($pdf_meta)*]
|
||||
|
|
@ -859,6 +1007,64 @@ macro_rules! pdf_parse {
|
|||
};
|
||||
(
|
||||
@impl
|
||||
#[pdf(tag = $tag_name:expr)]
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
enum $Enum:ident {
|
||||
$(#[pdf(tag_value = $tag_value:expr)]
|
||||
$(#[$($variant_meta:tt)*])*
|
||||
$VariantName:ident($Body:ty),)*
|
||||
#[pdf(other)]
|
||||
$(#[$($variant_meta_other:tt)*])*
|
||||
$VariantNameOther:ident($Other:ty),
|
||||
}
|
||||
) => {
|
||||
impl $crate::pdf::object::IsPdfNull for $Enum {
|
||||
fn is_pdf_null(&self) -> $crate::__std::primitive::bool {
|
||||
if let Self::$VariantNameOther(other) = self {
|
||||
$crate::pdf::object::IsPdfNull::is_pdf_null(other)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
impl $crate::pdf::parse::PdfParse for $Enum {
|
||||
fn type_name() -> $crate::__std::borrow::Cow<'static, $crate::__std::primitive::str> {
|
||||
$crate::__std::borrow::Cow::Borrowed($crate::__std::stringify!($Enum))
|
||||
}
|
||||
fn parse(object: $crate::pdf::object::PdfObject) -> $crate::__std::result::Result<Self, $crate::pdf::parse::PdfParseError> {
|
||||
let object = $crate::__std::convert::From::from(object);
|
||||
let $crate::pdf::object::PdfObjectDirect::Dictionary(object) = object else {
|
||||
return <$Other as $crate::pdf::parse::PdfParse>::parse(
|
||||
$crate::__std::convert::From::from(object),
|
||||
).map($Enum::$VariantNameOther);
|
||||
};
|
||||
'a: {
|
||||
let tag_name = $crate::__std::convert::AsRef::<[$crate::__std::primitive::u8]>::as_ref($tag_name);
|
||||
let $crate::__std::option::Option::Some(tag_value) = object.get(tag_name).cloned() else {
|
||||
break 'a;
|
||||
};
|
||||
let tag_value = $crate::__std::convert::From::from(tag_value);
|
||||
let $crate::pdf::object::PdfObjectDirect::Name(tag_value) = tag_value else {
|
||||
break 'a;
|
||||
};
|
||||
let _ = tag_value;
|
||||
$(if tag_value == $crate::pdf::object::PdfName::new_static(
|
||||
$crate::__std::convert::AsRef::<[u8]>::as_ref($tag_value),
|
||||
) {
|
||||
return <$Body as $crate::pdf::parse::PdfParse>::parse(
|
||||
$crate::pdf::object::PdfObject::Dictionary(object),
|
||||
).map($Enum::$VariantName);
|
||||
})*
|
||||
}
|
||||
<$Other as $crate::pdf::parse::PdfParse>::parse(
|
||||
$crate::pdf::object::PdfObject::Dictionary(object),
|
||||
).map($Enum::$VariantNameOther)
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl
|
||||
#[pdf(name)]
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
enum $Enum:ident {
|
||||
$(#[pdf(name = $name:expr)]
|
||||
|
|
@ -893,9 +1099,15 @@ macro_rules! pdf_parse {
|
|||
}
|
||||
}
|
||||
|
||||
impl $crate::pdf::object::IsPdfNull for $Enum {
|
||||
fn is_pdf_null(&self) -> $crate::__std::primitive::bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::pdf::parse::PdfParse for $Enum {
|
||||
fn type_name() -> $crate::__std::borrow::Cow<'static, $crate::__std::primitive::str> {
|
||||
$crate::__std::borrow::Cow::Borrowed($crate::__std::stringify!($Struct))
|
||||
$crate::__std::borrow::Cow::Borrowed($crate::__std::stringify!($Enum))
|
||||
}
|
||||
fn parse(object: $crate::pdf::object::PdfObject) -> $crate::__std::result::Result<Self, $crate::pdf::parse::PdfParseError> {
|
||||
let object = $crate::__std::convert::From::from(object);
|
||||
|
|
@ -903,7 +1115,7 @@ macro_rules! pdf_parse {
|
|||
return $crate::__std::result::Result::Err($crate::pdf::parse::PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: $crate::__std::stringify!($Struct),
|
||||
expected_ty: $crate::__std::stringify!($Enum),
|
||||
});
|
||||
};
|
||||
$crate::__std::result::Result::Ok($crate::__std::convert::TryInto::<$Enum>::try_into(name)?)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::pdf::{
|
|||
pub mod flate;
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf(name)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum PdfStreamFilter {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::pdf::{
|
|||
use std::{io::Read, num::NonZero};
|
||||
|
||||
pdf_parse! {
|
||||
#[pdf]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PdfFilterParmsFlateDecode {
|
||||
#[pdf(name = "Predictor")]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue