parsing more of the pdf structure

This commit is contained in:
Jacob Lifshay 2025-12-26 01:13:52 -08:00
parent 83631cc4c6
commit e0993fdb4a
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
9 changed files with 1549 additions and 100 deletions

View file

@ -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)
}

View file

@ -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 {

View file

@ -0,0 +1,6 @@
use crate::pdf::object::PdfStream;
pub struct PdfContentStream {
stream: PdfStream,
// TODO
}

View 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
View 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,
}
}

View file

@ -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)]

View file

@ -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)?)

View file

@ -7,6 +7,7 @@ use crate::pdf::{
pub mod flate;
pdf_parse! {
#[pdf(name)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum PdfStreamFilter {

View file

@ -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")]