WIP
This commit is contained in:
parent
5247d69ebd
commit
5fbfaa8053
8 changed files with 2975 additions and 958 deletions
|
|
@ -1,2 +1,5 @@
|
|||
mod pdf;
|
||||
mod util;
|
||||
#[doc(hidden)]
|
||||
pub use std as __std;
|
||||
|
||||
pub mod pdf;
|
||||
pub mod util;
|
||||
|
|
|
|||
45
src/main.rs
Normal file
45
src/main.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use parse_powerisa_pdf::pdf::{Pdf, PdfTrailer};
|
||||
use std::{
|
||||
error::Error,
|
||||
io::{IsTerminal, Read},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
fn main() -> Result<ExitCode, Box<dyn Error>> {
|
||||
let args: Vec<_> = std::env::args_os().collect();
|
||||
if args
|
||||
.iter()
|
||||
.skip(1)
|
||||
.any(|v| v.as_encoded_bytes().starts_with(b"-") && v != "-")
|
||||
|| args.len() > 2
|
||||
|| (args.len() == 1 && std::io::stdin().is_terminal())
|
||||
{
|
||||
eprintln!(
|
||||
"Usage: {} [<path/to/file.pdf>]\n\
|
||||
Reads the PDF file passed on the command line,\n\
|
||||
Reads stdin if no arguments are passed or if the file name is just a dash `-`.\n\
|
||||
If stdin is a terminal, you have to pass `-` explicitly to read from it.",
|
||||
args[0].display()
|
||||
);
|
||||
return Ok(ExitCode::FAILURE);
|
||||
}
|
||||
let file_path = args.get(1).filter(|v| *v != "-");
|
||||
let input = if let Some(file_path) = file_path {
|
||||
std::fs::read(file_path)?
|
||||
} else {
|
||||
let mut buf = Vec::new();
|
||||
std::io::stdin().lock().read_to_end(&mut buf)?;
|
||||
buf
|
||||
};
|
||||
let pdf = Pdf::parse(input)?;
|
||||
if let PdfTrailer::Stream {
|
||||
xref_stream,
|
||||
start_xref,
|
||||
} = pdf.trailer
|
||||
{
|
||||
dbg!(xref_stream.dictionary());
|
||||
}
|
||||
|
||||
todo!();
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
1746
src/pdf.rs
1746
src/pdf.rs
File diff suppressed because it is too large
Load diff
1111
src/pdf/object.rs
Normal file
1111
src/pdf/object.rs
Normal file
File diff suppressed because it is too large
Load diff
953
src/pdf/parse.rs
Normal file
953
src/pdf/parse.rs
Normal file
|
|
@ -0,0 +1,953 @@
|
|||
use crate::pdf::object::{
|
||||
MaybeArray, PdfInteger, PdfName, PdfNull, PdfObject, PdfObjectDirect, PdfObjectIdentifier,
|
||||
PdfObjectIndirect, PdfObjectNonNull, PdfReal,
|
||||
};
|
||||
use std::{any::Any, borrow::Cow, fmt, mem, num::NonZero, sync::Arc};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
|
||||
pub struct PdfInputPosition(Option<usize>);
|
||||
|
||||
impl fmt::Debug for PdfInputPosition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PdfInputPosition")
|
||||
.field(&format_args!("{self}"))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PdfInputPosition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(pos) = self.0 {
|
||||
write!(f, "{pos:#x}")
|
||||
} else {
|
||||
f.write_str("<unknown>")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfInputPosition {
|
||||
pub const fn new(pos: usize) -> Self {
|
||||
Self(Some(pos))
|
||||
}
|
||||
pub const fn empty() -> PdfInputPosition {
|
||||
Self(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetPdfInputPosition {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetPdfInputPosition> GetPdfInputPosition for &'_ T {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
T::get_pdf_input_position(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetPdfInputPosition> GetPdfInputPosition for &'_ mut T {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
T::get_pdf_input_position(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetPdfInputPosition> GetPdfInputPosition for Box<T> {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
T::get_pdf_input_position(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfInputPosition {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for bool {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
PdfInputPosition::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for i128 {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
PdfInputPosition::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for f64 {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
PdfInputPosition::empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct PdfInputPositionNoCompare(pub PdfInputPosition);
|
||||
|
||||
impl PdfInputPositionNoCompare {
|
||||
pub const fn empty() -> Self {
|
||||
Self(PdfInputPosition::empty())
|
||||
}
|
||||
pub const fn new(pos: usize) -> Self {
|
||||
Self(PdfInputPosition::new(pos))
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfInputPositionNoCompare {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PdfInputPosition> for PdfInputPositionNoCompare {
|
||||
fn from(value: PdfInputPosition) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PdfInputPositionNoCompare {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("PdfInputPositionNoCompare")
|
||||
.field(&format_args!("{self}"))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PdfInputPositionNoCompare {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for PdfInputPositionNoCompare {
|
||||
fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
|
||||
std::cmp::Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PdfInputPositionNoCompare {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for PdfInputPositionNoCompare {
|
||||
fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {
|
||||
// don't hash anything since Self always compares equal
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PdfInputPositionNoCompare {}
|
||||
|
||||
impl PartialEq for PdfInputPositionNoCompare {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum PdfParseError {
|
||||
Custom(String),
|
||||
InvalidType {
|
||||
pos: PdfInputPosition,
|
||||
ty: &'static str,
|
||||
expected_ty: &'static str,
|
||||
},
|
||||
InvalidName {
|
||||
pos: PdfInputPosition,
|
||||
name: PdfName,
|
||||
expected_ty: &'static str,
|
||||
},
|
||||
NotAPdfFile,
|
||||
TruncatedFile {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
InvalidObjectNumber {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
InvalidGenerationNumber {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
InvalidNumber {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
InvalidStringEscape {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
InvalidHexStringDigit {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
DuplicateIndirectObjectDefinition {
|
||||
pos: PdfInputPosition,
|
||||
id: PdfObjectIdentifier,
|
||||
},
|
||||
MissingObj {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
MissingEndObj {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
InvalidDictionaryClosingDoubleRAngle {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
DuplicateDictionaryKey {
|
||||
pos: PdfInputPosition,
|
||||
name: PdfName,
|
||||
},
|
||||
InvalidNameEscape {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
InvalidOrMissingEolAfterStreamKeyword {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
MissingEndStreamKeyword {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
IntegerOutOfRange {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
MissingTrailer {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
WrongArrayLength {
|
||||
pos: PdfInputPosition,
|
||||
len: usize,
|
||||
expected_len: usize,
|
||||
},
|
||||
MissingStartXRefKeyword {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
MissingStartXRefValue {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
MissingEofComment {
|
||||
pos: PdfInputPosition,
|
||||
},
|
||||
UnexpectedByte {
|
||||
pos: PdfInputPosition,
|
||||
byte: u8,
|
||||
},
|
||||
InvalidStartXRefValue {
|
||||
pos: PdfInputPosition,
|
||||
start_xref: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for PdfParseError {
|
||||
fn from(value: std::convert::Infallible) -> Self {
|
||||
match value {}
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPdfInputPosition for PdfParseError {
|
||||
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
||||
match *self {
|
||||
PdfParseError::Custom(_) | PdfParseError::NotAPdfFile => PdfInputPosition::empty(),
|
||||
PdfParseError::InvalidType { pos, .. }
|
||||
| PdfParseError::InvalidName { pos, .. }
|
||||
| PdfParseError::TruncatedFile { pos }
|
||||
| PdfParseError::InvalidObjectNumber { pos }
|
||||
| PdfParseError::InvalidGenerationNumber { pos }
|
||||
| PdfParseError::InvalidNumber { pos }
|
||||
| PdfParseError::InvalidStringEscape { pos }
|
||||
| PdfParseError::InvalidHexStringDigit { pos }
|
||||
| PdfParseError::DuplicateIndirectObjectDefinition { pos, .. }
|
||||
| PdfParseError::MissingObj { pos }
|
||||
| PdfParseError::MissingEndObj { pos }
|
||||
| PdfParseError::InvalidDictionaryClosingDoubleRAngle { pos }
|
||||
| PdfParseError::DuplicateDictionaryKey { pos, .. }
|
||||
| PdfParseError::InvalidNameEscape { pos }
|
||||
| PdfParseError::InvalidOrMissingEolAfterStreamKeyword { pos }
|
||||
| PdfParseError::MissingEndStreamKeyword { pos }
|
||||
| PdfParseError::IntegerOutOfRange { pos }
|
||||
| PdfParseError::MissingTrailer { pos }
|
||||
| PdfParseError::WrongArrayLength { pos, .. }
|
||||
| PdfParseError::MissingStartXRefKeyword { pos }
|
||||
| PdfParseError::MissingStartXRefValue { pos }
|
||||
| PdfParseError::MissingEofComment { pos }
|
||||
| PdfParseError::UnexpectedByte { pos, .. }
|
||||
| PdfParseError::InvalidStartXRefValue { pos, .. } => pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PdfParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
PdfParseError::Custom(ref v) => f.write_str(v),
|
||||
PdfParseError::InvalidType {
|
||||
pos,
|
||||
ty,
|
||||
expected_ty,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: invalid type: expected {expected_ty}, got {ty}"
|
||||
)
|
||||
}
|
||||
PdfParseError::InvalidName {
|
||||
pos,
|
||||
ref name,
|
||||
expected_ty,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: invalid name: expected a(n) {expected_ty}, got {name}"
|
||||
)
|
||||
}
|
||||
PdfParseError::NotAPdfFile => f.write_str("Not a PDF file"),
|
||||
PdfParseError::TruncatedFile { pos } => {
|
||||
write!(f, "at {pos}: PDF file is truncated too early")
|
||||
}
|
||||
PdfParseError::InvalidObjectNumber { pos } => {
|
||||
write!(f, "at {pos}: PDF object number is invalid")
|
||||
}
|
||||
PdfParseError::InvalidGenerationNumber { pos } => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: PDF object identifier's generation number is invalid"
|
||||
)
|
||||
}
|
||||
PdfParseError::InvalidNumber { pos } => {
|
||||
write!(f, "at {pos}: invalid number")
|
||||
}
|
||||
PdfParseError::InvalidStringEscape { pos } => {
|
||||
write!(f, "at {pos}: invalid string escape")
|
||||
}
|
||||
PdfParseError::InvalidHexStringDigit { pos } => {
|
||||
write!(f, "at {pos}: invalid hex string digit")
|
||||
}
|
||||
PdfParseError::DuplicateIndirectObjectDefinition { pos, id } => {
|
||||
write!(f, "at {pos}: duplicate indirect object definition: {id:?}")
|
||||
}
|
||||
PdfParseError::MissingObj { pos } => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: indirect object definition is missing `obj` keyword"
|
||||
)
|
||||
}
|
||||
PdfParseError::MissingEndObj { pos } => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: indirect object definition is missing `endobj` keyword"
|
||||
)
|
||||
}
|
||||
PdfParseError::InvalidDictionaryClosingDoubleRAngle { pos } => {
|
||||
write!(f, "at {pos}: dictionary has an invalid closing `>>` symbol")
|
||||
}
|
||||
PdfParseError::DuplicateDictionaryKey { pos, ref name } => {
|
||||
write!(f, "at {pos}: duplicate dictionary key: {name}")
|
||||
}
|
||||
PdfParseError::InvalidNameEscape { pos } => {
|
||||
write!(f, "at {pos}: invalid name escape")
|
||||
}
|
||||
PdfParseError::InvalidOrMissingEolAfterStreamKeyword { pos } => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: invalid or missing end-of-line after `stream` keyword"
|
||||
)
|
||||
}
|
||||
PdfParseError::MissingEndStreamKeyword { pos } => {
|
||||
write!(f, "at {pos}: missing `endstream` keyword")
|
||||
}
|
||||
PdfParseError::IntegerOutOfRange { pos } => {
|
||||
write!(f, "at {pos}: integer out of range")
|
||||
}
|
||||
PdfParseError::MissingTrailer { pos } => {
|
||||
write!(f, "at {pos}: missing `trailer` keyword")
|
||||
}
|
||||
PdfParseError::WrongArrayLength {
|
||||
pos,
|
||||
len,
|
||||
expected_len,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: wrong array length: expected {expected_len}, got {len}"
|
||||
)
|
||||
}
|
||||
PdfParseError::MissingStartXRefKeyword { pos } => {
|
||||
write!(f, "at {pos}: missing `startxref` keyword")
|
||||
}
|
||||
PdfParseError::MissingStartXRefValue { pos } => {
|
||||
write!(f, "at {pos}: missing `startxref` value")
|
||||
}
|
||||
PdfParseError::MissingEofComment { pos } => {
|
||||
write!(f, "at {pos}: missing `%%EOF` comment")
|
||||
}
|
||||
PdfParseError::UnexpectedByte { pos, byte } => {
|
||||
write!(f, "at {pos}: unexpected byte {}", byte.escape_ascii())
|
||||
}
|
||||
PdfParseError::InvalidStartXRefValue { pos, start_xref } => {
|
||||
write!(
|
||||
f,
|
||||
"at {pos}: invalid `startxref` value: {start_xref} ({start_xref:#x})"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for PdfParseError {}
|
||||
|
||||
pub trait PdfParse: Sized + 'static {
|
||||
fn type_name() -> Cow<'static, str>;
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError>;
|
||||
fn parse_option(object: PdfObject) -> Result<Option<Self>, PdfParseError> {
|
||||
match object {
|
||||
PdfObject::Null(_) => Ok(None),
|
||||
PdfObject::Indirect(ref v) if v.get().is_null() => Ok(None),
|
||||
PdfObject::Boolean(_)
|
||||
| PdfObject::Integer(_)
|
||||
| PdfObject::Real(_)
|
||||
| PdfObject::String(_)
|
||||
| PdfObject::Name(_)
|
||||
| PdfObject::Array(_)
|
||||
| PdfObject::Dictionary(_)
|
||||
| PdfObject::Stream(_)
|
||||
| PdfObject::Indirect(_) => Self::parse(object).map(Some),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse> PdfParse for Option<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
T::type_name()
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
T::parse_option(object)
|
||||
}
|
||||
fn parse_option(object: PdfObject) -> Result<Option<Self>, PdfParseError> {
|
||||
if matches!(object, PdfObject::Null(_)) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Self::parse(object).map(Some)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_pdf_parse_prim_int {
|
||||
($ty:ident) => {
|
||||
impl PdfParse for $ty {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed(stringify!($ty))
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let v: PdfInteger = PdfParse::parse(object)?;
|
||||
v.value()
|
||||
.try_into()
|
||||
.map_err(|_| PdfParseError::IntegerOutOfRange { pos: v.pos() })
|
||||
}
|
||||
}
|
||||
impl PdfParse for NonZero<$ty> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed(concat!("NonZero<", stringify!($ty), ">"))
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let v: PdfInteger = PdfParse::parse(object)?;
|
||||
v.value()
|
||||
.try_into()
|
||||
.ok()
|
||||
.and_then(NonZero::new)
|
||||
.ok_or(PdfParseError::IntegerOutOfRange { pos: v.pos() })
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_pdf_parse_prim_int!(u8);
|
||||
impl_pdf_parse_prim_int!(i8);
|
||||
impl_pdf_parse_prim_int!(u16);
|
||||
impl_pdf_parse_prim_int!(i16);
|
||||
impl_pdf_parse_prim_int!(u32);
|
||||
impl_pdf_parse_prim_int!(i32);
|
||||
impl_pdf_parse_prim_int!(u64);
|
||||
impl_pdf_parse_prim_int!(i64);
|
||||
impl_pdf_parse_prim_int!(u128);
|
||||
impl_pdf_parse_prim_int!(usize);
|
||||
impl_pdf_parse_prim_int!(isize);
|
||||
|
||||
impl PdfParse for i128 {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("i128")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let v: PdfInteger = PdfParse::parse(object)?;
|
||||
Ok(v.value().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for NonZero<i128> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("NonZero<i128>")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
let v: PdfInteger = PdfParse::parse(object)?;
|
||||
NonZero::new(v.value().into()).ok_or(PdfParseError::IntegerOutOfRange { pos: v.pos() })
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for f32 {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("f32")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Ok(<f64 as PdfParse>::parse(object)? as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfNull {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("null")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
match PdfObjectDirect::from(object) {
|
||||
PdfObjectDirect::Null(v) => Ok(v),
|
||||
object => Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "null",
|
||||
}),
|
||||
}
|
||||
}
|
||||
fn parse_option(object: PdfObject) -> Result<Option<Self>, PdfParseError> {
|
||||
Self::parse(object).map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfObjectNonNull {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("non-null")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Option::<PdfObjectNonNull>::from(object).ok_or(PdfParseError::InvalidType {
|
||||
pos: PdfInputPosition::empty(),
|
||||
ty: "null",
|
||||
expected_ty: "non-null",
|
||||
})
|
||||
}
|
||||
fn parse_option(object: PdfObject) -> Result<Option<Self>, PdfParseError> {
|
||||
Ok(object.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfObjectDirect {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("direct object")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Ok(object.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfObject {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("object")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
Ok(object)
|
||||
}
|
||||
}
|
||||
|
||||
impl PdfParse for PdfObjectIndirect {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Borrowed("indirect object")
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
match object {
|
||||
PdfObject::Indirect(v) => Ok(v),
|
||||
_ => Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "indirect object",
|
||||
}),
|
||||
}
|
||||
}
|
||||
fn parse_option(object: PdfObject) -> Result<Option<Self>, PdfParseError> {
|
||||
match object {
|
||||
PdfObject::Indirect(v) => Ok(Some(v)),
|
||||
PdfObject::Null(_) => Ok(None),
|
||||
_ => Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "indirect object",
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse, const N: usize> PdfParse for [T; N] {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("[{}; {N}]", T::type_name()))
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
match PdfObjectDirect::from(object) {
|
||||
PdfObjectDirect::Array(array) => {
|
||||
let array_pos = array.pos();
|
||||
let elements = array.into_elements();
|
||||
let mut elements: Arc<[PdfObject; N]> =
|
||||
elements.try_into().map_err(|elements: Arc<[PdfObject]>| {
|
||||
PdfParseError::WrongArrayLength {
|
||||
pos: array_pos,
|
||||
len: elements.len(),
|
||||
expected_len: N,
|
||||
}
|
||||
})?;
|
||||
let elements: Box<[T]> = if let Some(elements) = Arc::get_mut(&mut elements) {
|
||||
Result::from_iter(elements.iter_mut().map(|v| T::parse(mem::take(v))))?
|
||||
} else {
|
||||
Result::from_iter(elements.iter().map(|v| T::parse(v.clone())))?
|
||||
};
|
||||
Ok(*Box::<[T; N]>::try_from(elements)
|
||||
.ok()
|
||||
.expect("already checked length"))
|
||||
}
|
||||
object => Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "array",
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse> PdfParse for Arc<[T]> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("Arc<[{}]>", T::type_name()))
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
match PdfObjectDirect::from(object) {
|
||||
PdfObjectDirect::Array(array) => {
|
||||
let mut elements = array.into_elements();
|
||||
if let Some(retval) = <dyn Any>::downcast_ref::<Self>(&elements) {
|
||||
return Ok(retval.clone());
|
||||
}
|
||||
if let Some(elements) = Arc::get_mut(&mut elements) {
|
||||
Result::from_iter(elements.iter_mut().map(|v| T::parse(mem::take(v))))
|
||||
} else {
|
||||
Result::from_iter(elements.iter().map(|v| T::parse(v.clone())))
|
||||
}
|
||||
}
|
||||
PdfObjectDirect::Null(_) => Ok(Self::default()),
|
||||
object => Err(PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: "array",
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PdfParse> PdfParse for MaybeArray<T> {
|
||||
fn type_name() -> Cow<'static, str> {
|
||||
Cow::Owned(format!("MaybeArray<{}>", T::type_name()))
|
||||
}
|
||||
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
||||
match PdfObjectDirect::from(object) {
|
||||
PdfObjectDirect::Null(_) => Ok(Self::default()),
|
||||
PdfObjectDirect::Array(object) => Ok(Self(PdfParse::parse(object.into())?)),
|
||||
object => Ok(Self(Arc::new([PdfParse::parse(object.into())?]))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! pdf_parse {
|
||||
(
|
||||
$(#[$($struct_meta:tt)*])*
|
||||
$struct_vis:vis struct $Struct:ident$(<$($StructParam:ident $(: $StructBound:tt)? $(= $StructParamDefault:ty)?),* $(,)?>)? {
|
||||
$(#[pdf $($pdf_meta:tt)*]
|
||||
$(#[$($field_meta:tt)*])*
|
||||
$field_vis:vis $field_name:ident: $field_ty:ty,)*
|
||||
}
|
||||
) => {
|
||||
$(#[$($struct_meta)*])*
|
||||
$struct_vis struct $Struct$(<$($StructParam $(: $StructBound)? $(= $StructParamDefault)?),*>)? {
|
||||
$($(#[$($field_meta)*])*
|
||||
$field_vis $field_name: $field_ty,)*
|
||||
}
|
||||
|
||||
$crate::pdf::parse::pdf_parse! {
|
||||
@impl
|
||||
struct $Struct$(<$($StructParam $(: $StructBound)?),*>)? {
|
||||
$(#[pdf $($pdf_meta)*]
|
||||
$(#[$($field_meta)*])*
|
||||
$field_name: $field_ty,)*
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl
|
||||
struct $Struct:ident$(<$($StructParam:ident $(: $StructBound:tt)?),* $(,)?>)? {
|
||||
$($(#[$($field_meta:tt)*])*
|
||||
$field_name:ident: $field_ty:ty,)*
|
||||
}
|
||||
) => {
|
||||
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> {
|
||||
let object = $crate::__std::convert::From::from(object);
|
||||
let $crate::pdf::object::PdfObjectDirect::Dictionary(object) = object else {
|
||||
return $crate::__std::result::Result::Err($crate::pdf::parse::PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: $crate::__std::stringify!($Struct),
|
||||
});
|
||||
};
|
||||
let pos = object.pos();
|
||||
let mut object = object.into_fields();
|
||||
let object_mut = $crate::__std::sync::Arc::make_mut(&mut object);
|
||||
let _ = object_mut;
|
||||
$($crate::pdf::parse::pdf_parse! {
|
||||
@impl_struct_field(pos, object, object_mut)
|
||||
[]
|
||||
$(#[$($field_meta)*])*
|
||||
$field_name: $field_ty
|
||||
})*
|
||||
$crate::__std::result::Result::Ok(Self {
|
||||
$($field_name,)*
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl_struct_field($pos:ident, $object:ident, $object_mut:ident)
|
||||
[$(#[$($prev_field_meta:tt)*])*]
|
||||
#[pdf $pdf_meta:tt]
|
||||
$(#[$($field_meta:tt)*])*
|
||||
$field_name:ident: $field_ty:ty
|
||||
) => {
|
||||
$crate::pdf::parse::pdf_parse! {
|
||||
@impl_struct_field($pos, $object, $object_mut, pdf $pdf_meta)
|
||||
[$(#[$($prev_field_meta)*])*]
|
||||
$(#[$($field_meta)*])*
|
||||
$field_name: $field_ty
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl_struct_field($pos:ident, $object:ident, $object_mut:ident $($pdf_meta:tt)*)
|
||||
[$(#[$($prev_field_meta:tt)*])*]
|
||||
#[$($next_field_meta:tt)*]
|
||||
$(#[$($field_meta:tt)*])*
|
||||
$field_name:ident: $field_ty:ty
|
||||
) => {
|
||||
$crate::pdf::parse::pdf_parse! {
|
||||
@impl_struct_field($pos, $object, $object_mut $($pdf_meta)*)
|
||||
[$(#[$($prev_field_meta)*])* #[$($next_field_meta)*]]
|
||||
$(#[$($field_meta)*])*
|
||||
$field_name: $field_ty
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl_struct_field($pos:ident, $object:ident, $object_mut:ident, pdf(flatten))
|
||||
[$(#[$($field_meta:tt)*])*]
|
||||
$field_name:ident: $field_ty:ty
|
||||
) => {
|
||||
let $field_name = <$field_ty as $crate::pdf::parse::PdfParse>::parse(
|
||||
$crate::pdf::object::PdfObject::Dictionary(
|
||||
$crate::pdf::object::PdfDictionary::from_fields($pos, $object),
|
||||
),
|
||||
)?;
|
||||
};
|
||||
(
|
||||
@impl_struct_field($pos:ident, $object:ident, $object_mut:ident, pdf(name = $name:expr))
|
||||
[$(#[$($field_meta:tt)*])*]
|
||||
$field_name:ident: $field_ty:ty
|
||||
) => {
|
||||
let $field_name = $crate::pdf::object::PdfName::new_static(
|
||||
$crate::__std::convert::AsRef::<[u8]>::as_ref($name),
|
||||
);
|
||||
let $field_name = <$field_ty as $crate::pdf::parse::PdfParse>::parse(
|
||||
$object_mut
|
||||
.remove(&$field_name)
|
||||
.unwrap_or($crate::pdf::object::PdfObject::Null($crate::pdf::object::PdfNull::new($pos))),
|
||||
)?;
|
||||
};
|
||||
(
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
$enum_vis:vis enum $Enum:ident {
|
||||
$(#[pdf $($pdf_meta:tt)*]
|
||||
$(#[$($variant_meta:tt)*])*
|
||||
$VariantName:ident $(($($variant_paren_body:tt)*))? $({$($variant_brace_body:tt)*})?,)*
|
||||
}
|
||||
) => {
|
||||
$(#[$($enum_meta)*])*
|
||||
$enum_vis enum $Enum {
|
||||
$($(#[$($variant_meta)*])*
|
||||
$VariantName $(($($variant_paren_body)*))? $({$($variant_brace_body)*})?,)*
|
||||
}
|
||||
|
||||
$crate::pdf::parse::pdf_parse! {
|
||||
@impl
|
||||
$(#[$($enum_meta)*])*
|
||||
enum $Enum {
|
||||
$(#[pdf $($pdf_meta)*]
|
||||
$(#[$($variant_meta)*])*
|
||||
$VariantName $(($($variant_paren_body)*))? $({$($variant_brace_body)*})?,)*
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
enum $Enum:ident {
|
||||
$(#[pdf(name = $name:expr)]
|
||||
$(#[$($variant_meta:tt)*])*
|
||||
$VariantName:ident,)*
|
||||
$(#[pdf(other)]
|
||||
$(#[$($variant_meta_other:tt)*])*
|
||||
$VariantNameOther:ident($($PdfName:tt)*),)?
|
||||
}
|
||||
) => {
|
||||
impl $crate::__std::convert::From<$Enum> for $crate::pdf::object::PdfName {
|
||||
fn from(value: $Enum) -> Self {
|
||||
match value {
|
||||
$($Enum::$VariantName => $crate::pdf::object::PdfName::new_static(
|
||||
$crate::__std::convert::AsRef::<[u8]>::as_ref($name),
|
||||
),)*
|
||||
$($Enum::$VariantNameOther(v) => $crate::__std::convert::Into::into(v),)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$crate::pdf::parse::pdf_parse! {
|
||||
@impl_try_from
|
||||
$(#[$($enum_meta)*])*
|
||||
enum $Enum {
|
||||
$(#[pdf(name = $name)]
|
||||
$(#[$($variant_meta)*])*
|
||||
$VariantName,)*
|
||||
$(#[pdf(other)]
|
||||
$(#[$($variant_meta_other)*])*
|
||||
$VariantNameOther($($PdfName)*),)?
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
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::Name(name) = object else {
|
||||
return $crate::__std::result::Result::Err($crate::pdf::parse::PdfParseError::InvalidType {
|
||||
pos: object.pos(),
|
||||
ty: object.type_name(),
|
||||
expected_ty: $crate::__std::stringify!($Struct),
|
||||
});
|
||||
};
|
||||
$crate::__std::result::Result::Ok($crate::__std::convert::TryInto::<$Enum>::try_into(name)?)
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl_try_from
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
enum $Enum:ident {
|
||||
$(#[pdf(name = $name:expr)]
|
||||
$(#[$($variant_meta:tt)*])*
|
||||
$VariantName:ident,)*
|
||||
#[pdf(other)]
|
||||
$(#[$($variant_meta_other:tt)*])*
|
||||
$VariantNameOther:ident(PdfName),
|
||||
}
|
||||
) => {
|
||||
impl $crate::__std::convert::From<$crate::pdf::object::PdfName> for $Enum {
|
||||
fn from(name: $crate::pdf::object::PdfName) -> Self {
|
||||
$(if name == $crate::pdf::object::PdfName::new_static(
|
||||
$crate::__std::convert::AsRef::<[u8]>::as_ref($name),
|
||||
) {
|
||||
$Enum::$VariantName
|
||||
} else)* {
|
||||
$Enum::$VariantNameOther(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl_try_from
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
enum $Enum:ident {
|
||||
$(#[pdf(name = $name:expr)]
|
||||
$(#[$($variant_meta:tt)*])*
|
||||
$VariantName:ident,)*
|
||||
#[pdf(other)]
|
||||
$(#[$($variant_meta_other:tt)*])*
|
||||
$VariantNameOther:ident($PdfName:ty),
|
||||
}
|
||||
) => {
|
||||
impl $crate::__std::convert::TryFrom<$crate::pdf::object::PdfName> for $Enum {
|
||||
type Error = $crate::pdf::parse::PdfParseError;
|
||||
|
||||
fn try_from(name: $crate::pdf::object::PdfName) -> $crate::__std::result::Result<Self, $crate::pdf::parse::PdfParseError> {
|
||||
$(if name == $crate::pdf::object::PdfName::new_static(
|
||||
$crate::__std::convert::AsRef::<[u8]>::as_ref($name),
|
||||
) {
|
||||
$crate::__std::result::Result::Ok($Enum::$VariantName)
|
||||
} else)* {
|
||||
$crate::__std::result::Result::Ok($Enum::$VariantNameOther($crate::__std::convert::TryInto::<$PdfName>::try_into(name)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@impl_try_from
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
enum $Enum:ident {
|
||||
$(#[pdf(name = $name:expr)]
|
||||
$(#[$($variant_meta:tt)*])*
|
||||
$VariantName:ident,)*
|
||||
}
|
||||
) => {
|
||||
impl $crate::__std::convert::TryFrom<$crate::pdf::object::PdfName> for $Enum {
|
||||
type Error = $crate::pdf::parse::PdfParseError;
|
||||
|
||||
fn try_from(name: $crate::pdf::object::PdfName) -> $crate::__std::result::Result<Self, $crate::pdf::parse::PdfParseError> {
|
||||
$(if name == $crate::pdf::object::PdfName::new_static(
|
||||
$crate::__std::convert::AsRef::<[u8]>::as_ref($name),
|
||||
) {
|
||||
$crate::__std::result::Result::Ok($Enum::$VariantName)
|
||||
} else)* {
|
||||
$crate::__std::result::Result::Err($crate::pdf::parse::PdfParseError::InvalidName {
|
||||
pos: name.pos(),
|
||||
name,
|
||||
expected_ty: $crate::__std::stringify!($Struct),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use pdf_parse;
|
||||
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
pub(crate) enum ArcOrRef<'a, T: ?Sized> {
|
||||
pub enum ArcOrRef<'a, T: ?Sized> {
|
||||
Arc(Arc<T>),
|
||||
Ref(&'a T),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue