This commit is contained in:
Jacob Lifshay 2025-12-24 07:12:48 -08:00
parent 5247d69ebd
commit 5fbfaa8053
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
8 changed files with 2975 additions and 958 deletions

68
Cargo.lock generated
View file

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

View file

@ -5,5 +5,4 @@ edition = "2024"
license = "LGPL-3.0-or-later"
[dependencies]
serde = { version = "1.0.228", features = ["derive"] }

View file

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

File diff suppressed because it is too large Load diff

1111
src/pdf/object.rs Normal file

File diff suppressed because it is too large Load diff

953
src/pdf/parse.rs Normal file
View 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;

View file

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