WIP
This commit is contained in:
parent
5247d69ebd
commit
5fbfaa8053
8 changed files with 2975 additions and 958 deletions
68
Cargo.lock
generated
68
Cargo.lock
generated
|
|
@ -5,71 +5,3 @@ version = 4
|
|||
[[package]]
|
||||
name = "parse_powerisa_pdf"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
|
|
|||
|
|
@ -5,5 +5,4 @@ edition = "2024"
|
|||
license = "LGPL-3.0-or-later"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
|
||||
|
|
|
|||
|
|
@ -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