2142 lines
60 KiB
Rust
2142 lines
60 KiB
Rust
use crate::{
|
|
pdf::{
|
|
PdfObjectAndParseCache, PdfObjects,
|
|
parse::{
|
|
GetPdfInputPosition, PdfInputPosition, PdfInputPositionNoCompare, PdfParse,
|
|
PdfParseError,
|
|
},
|
|
stream_filters::PdfStreamFilter,
|
|
},
|
|
pdf_parse,
|
|
util::{ArcOrRef, DagDebugState},
|
|
};
|
|
use std::{
|
|
any::TypeId,
|
|
borrow::{Borrow, Cow},
|
|
collections::BTreeMap,
|
|
fmt::{self, Write},
|
|
iter::FusedIterator,
|
|
num::NonZero,
|
|
sync::{Arc, OnceLock},
|
|
};
|
|
|
|
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct PdfString {
|
|
pos: PdfInputPositionNoCompare,
|
|
bytes: ArcOrRef<'static, [u8]>,
|
|
}
|
|
|
|
impl std::fmt::Debug for PdfString {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
let Self { pos, bytes: _ } = self;
|
|
write!(f, "PdfString(at {pos}, {})", self.bytes_debug())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub struct PdfStringBytesDebug<'a>(pub &'a [u8]);
|
|
|
|
impl<'a> fmt::Display for PdfStringBytesDebug<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "b\"{}\"", self.0.escape_ascii())
|
|
}
|
|
}
|
|
|
|
impl<'a> fmt::Debug for PdfStringBytesDebug<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Display::fmt(self, f)
|
|
}
|
|
}
|
|
|
|
impl PdfString {
|
|
pub fn new(pos: impl Into<PdfInputPositionNoCompare>, bytes: ArcOrRef<'static, [u8]>) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
bytes,
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
pub fn bytes(&self) -> &ArcOrRef<'static, [u8]> {
|
|
&self.bytes
|
|
}
|
|
pub fn bytes_debug(&self) -> PdfStringBytesDebug<'_> {
|
|
PdfStringBytesDebug(&self.bytes)
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfString {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub struct PdfDate {
|
|
text: PdfString,
|
|
}
|
|
|
|
impl fmt::Debug for PdfDate {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self { text } = self;
|
|
let pos = text.pos();
|
|
write!(f, "PdfDate(at {pos}, {})", text.bytes_debug())
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfDate {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for PdfDate {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed("date")
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
Self::try_new(PdfString::parse(object)?)
|
|
}
|
|
}
|
|
|
|
impl PdfDate {
|
|
pub fn try_new(text: PdfString) -> Result<Self, PdfParseError> {
|
|
// TODO: check syntax
|
|
Ok(Self { text })
|
|
}
|
|
pub fn text(&self) -> &PdfString {
|
|
&self.text
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfDate {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.text.pos()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct PdfName {
|
|
pos: PdfInputPositionNoCompare,
|
|
bytes: ArcOrRef<'static, [u8]>,
|
|
}
|
|
|
|
impl Borrow<[u8]> for PdfName {
|
|
fn borrow(&self) -> &[u8] {
|
|
&self.bytes
|
|
}
|
|
}
|
|
|
|
impl PdfName {
|
|
pub fn try_new(
|
|
pos: impl Into<PdfInputPositionNoCompare>,
|
|
bytes: impl Into<ArcOrRef<'static, [u8]>>,
|
|
) -> Option<Self> {
|
|
let bytes = bytes.into();
|
|
if bytes.contains(&0) {
|
|
None
|
|
} else {
|
|
Some(Self {
|
|
pos: pos.into(),
|
|
bytes,
|
|
})
|
|
}
|
|
}
|
|
#[track_caller]
|
|
pub const fn new_static(bytes: &'static [u8]) -> Self {
|
|
let mut i = 0;
|
|
while i < bytes.len() {
|
|
if bytes[i] == 0 {
|
|
panic!("shouldn't contain any nul bytes");
|
|
}
|
|
i += 1;
|
|
}
|
|
Self {
|
|
pos: PdfInputPositionNoCompare::empty(),
|
|
bytes: ArcOrRef::Ref(bytes),
|
|
}
|
|
}
|
|
#[track_caller]
|
|
pub fn new(
|
|
pos: impl Into<PdfInputPositionNoCompare>,
|
|
bytes: impl Into<ArcOrRef<'static, [u8]>>,
|
|
) -> Self {
|
|
Self::try_new(pos, bytes).expect("shouldn't contain any nul bytes")
|
|
}
|
|
pub fn as_bytes(&self) -> &ArcOrRef<'static, [u8]> {
|
|
&self.bytes
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfName {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PdfName {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "PdfName(at {}: {self})", self.pos)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for PdfName {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.write_str("/")?;
|
|
for &b in self.bytes.iter() {
|
|
match b {
|
|
0x21..=0x7E if b != b'#' => f.write_char(b.into())?,
|
|
_ => write!(f, "#{b:02X}")?,
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
|
pub struct PdfBoolean {
|
|
pos: PdfInputPositionNoCompare,
|
|
value: bool,
|
|
}
|
|
|
|
impl fmt::Debug for PdfBoolean {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self { pos, value } = *self;
|
|
write!(f, "PdfBoolean(at {pos}, {value})")
|
|
}
|
|
}
|
|
|
|
impl PdfBoolean {
|
|
pub fn new(pos: impl Into<PdfInputPositionNoCompare>, value: bool) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
value,
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
pub fn value(&self) -> bool {
|
|
self.value
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfBoolean {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
|
pub struct PdfInteger {
|
|
pos: PdfInputPositionNoCompare,
|
|
value: i128,
|
|
}
|
|
|
|
impl fmt::Debug for PdfInteger {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self { pos, value } = *self;
|
|
write!(f, "PdfInteger(at {pos}, {value})")
|
|
}
|
|
}
|
|
|
|
impl PdfInteger {
|
|
pub fn new(pos: impl Into<PdfInputPositionNoCompare>, value: i128) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
value,
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
pub fn value(&self) -> i128 {
|
|
self.value
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfInteger {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Default)]
|
|
pub struct PdfReal {
|
|
pos: PdfInputPositionNoCompare,
|
|
value: f64,
|
|
}
|
|
|
|
impl fmt::Debug for PdfReal {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self { pos, value } = *self;
|
|
write!(f, "PdfReal(at {pos}, {value})")
|
|
}
|
|
}
|
|
|
|
impl PdfReal {
|
|
pub fn new(pos: impl Into<PdfInputPositionNoCompare>, value: f64) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
value,
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
pub fn value(&self) -> f64 {
|
|
self.value
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfReal {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
pub enum PdfNumber {
|
|
Integer(PdfInteger),
|
|
Real(PdfReal),
|
|
}
|
|
|
|
impl fmt::Debug for PdfNumber {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Integer(v) => v.fmt(f),
|
|
Self::Real(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfNumber {
|
|
pub fn pos(self) -> PdfInputPosition {
|
|
match self {
|
|
Self::Integer(v) => v.pos(),
|
|
Self::Real(v) => v.pos(),
|
|
}
|
|
}
|
|
pub fn as_f64(self) -> f64 {
|
|
match self {
|
|
Self::Integer(v) => v.value as f64,
|
|
Self::Real(v) => v.value,
|
|
}
|
|
}
|
|
pub fn as_f32(self) -> f32 {
|
|
match self {
|
|
Self::Integer(v) => v.value as f32,
|
|
Self::Real(v) => v.value as f32,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for PdfNumber {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
match (self, other) {
|
|
(Self::Integer(this), Self::Integer(other)) => Some(this.cmp(other)),
|
|
_ => self.as_f64().partial_cmp(&other.as_f64()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for PdfNumber {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.partial_cmp(other).is_some_and(|v| v.is_eq())
|
|
}
|
|
}
|
|
|
|
impl Default for PdfNumber {
|
|
fn default() -> Self {
|
|
PdfNumber::Integer(PdfInteger::default())
|
|
}
|
|
}
|
|
|
|
impl PdfObjectDirect {
|
|
pub fn number(&self) -> Option<PdfNumber> {
|
|
match *self {
|
|
PdfObjectDirect::Integer(v) => Some(PdfNumber::Integer(v)),
|
|
PdfObjectDirect::Real(v) => Some(PdfNumber::Real(v)),
|
|
PdfObjectDirect::Boolean(_)
|
|
| PdfObjectDirect::String(_)
|
|
| PdfObjectDirect::Name(_)
|
|
| PdfObjectDirect::Array(_)
|
|
| PdfObjectDirect::Dictionary(_)
|
|
| PdfObjectDirect::Stream(_)
|
|
| PdfObjectDirect::Null(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfObjectNonNull {
|
|
pub fn number(&self) -> Option<PdfNumber> {
|
|
match *self {
|
|
PdfObjectNonNull::Integer(v) => Some(PdfNumber::Integer(v)),
|
|
PdfObjectNonNull::Real(v) => Some(PdfNumber::Real(v)),
|
|
PdfObjectNonNull::Boolean(_)
|
|
| PdfObjectNonNull::String(_)
|
|
| PdfObjectNonNull::Name(_)
|
|
| PdfObjectNonNull::Array(_)
|
|
| PdfObjectNonNull::Dictionary(_)
|
|
| PdfObjectNonNull::Stream(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfNumber {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for PdfNumber {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed("number")
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
let object = PdfObjectDirect::from(object);
|
|
object.number().ok_or(PdfParseError::InvalidType {
|
|
pos: object.pos(),
|
|
ty: object.type_name(),
|
|
expected_ty: "number",
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum PdfStringOrNumber {
|
|
String(PdfString),
|
|
Number(PdfNumber),
|
|
}
|
|
|
|
impl fmt::Debug for PdfStringOrNumber {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::String(v) => v.fmt(f),
|
|
Self::Number(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfStringOrNumber {
|
|
pub fn pos(self) -> PdfInputPosition {
|
|
match self {
|
|
Self::String(v) => v.pos(),
|
|
Self::Number(v) => v.pos(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfObjectDirect {
|
|
pub fn string_or_number(&self) -> Option<PdfStringOrNumber> {
|
|
match *self {
|
|
PdfObjectDirect::Integer(v) => Some(PdfStringOrNumber::Number(PdfNumber::Integer(v))),
|
|
PdfObjectDirect::Real(v) => Some(PdfStringOrNumber::Number(PdfNumber::Real(v))),
|
|
PdfObjectDirect::String(ref v) => Some(PdfStringOrNumber::String(v.clone())),
|
|
PdfObjectDirect::Boolean(_)
|
|
| PdfObjectDirect::Name(_)
|
|
| PdfObjectDirect::Array(_)
|
|
| PdfObjectDirect::Dictionary(_)
|
|
| PdfObjectDirect::Stream(_)
|
|
| PdfObjectDirect::Null(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfObjectNonNull {
|
|
pub fn string_or_number(&self) -> Option<PdfStringOrNumber> {
|
|
match *self {
|
|
PdfObjectNonNull::Integer(v) => Some(PdfStringOrNumber::Number(PdfNumber::Integer(v))),
|
|
PdfObjectNonNull::Real(v) => Some(PdfStringOrNumber::Number(PdfNumber::Real(v))),
|
|
PdfObjectNonNull::String(ref v) => Some(PdfStringOrNumber::String(v.clone())),
|
|
PdfObjectNonNull::Boolean(_)
|
|
| PdfObjectNonNull::Name(_)
|
|
| PdfObjectNonNull::Array(_)
|
|
| PdfObjectNonNull::Dictionary(_)
|
|
| PdfObjectNonNull::Stream(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfStringOrNumber {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for PdfStringOrNumber {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed("string or number")
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
let object = PdfObjectDirect::from(object);
|
|
object.string_or_number().ok_or(PdfParseError::InvalidType {
|
|
pos: object.pos(),
|
|
ty: object.type_name(),
|
|
expected_ty: "string or number",
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum PdfNameOrInteger {
|
|
Name(PdfName),
|
|
Integer(PdfInteger),
|
|
}
|
|
|
|
impl fmt::Debug for PdfNameOrInteger {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Name(v) => v.fmt(f),
|
|
Self::Integer(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfNameOrInteger {
|
|
pub fn pos(self) -> PdfInputPosition {
|
|
match self {
|
|
Self::Name(v) => v.pos(),
|
|
Self::Integer(v) => v.pos(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfObjectDirect {
|
|
pub fn name_or_integer(&self) -> Option<PdfNameOrInteger> {
|
|
match *self {
|
|
PdfObjectDirect::Name(ref v) => Some(PdfNameOrInteger::Name(v.clone())),
|
|
PdfObjectDirect::Integer(v) => Some(PdfNameOrInteger::Integer(v)),
|
|
PdfObjectDirect::Boolean(_)
|
|
| PdfObjectDirect::Real(_)
|
|
| PdfObjectDirect::String(_)
|
|
| PdfObjectDirect::Array(_)
|
|
| PdfObjectDirect::Dictionary(_)
|
|
| PdfObjectDirect::Stream(_)
|
|
| PdfObjectDirect::Null(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfObjectNonNull {
|
|
pub fn name_or_integer(&self) -> Option<PdfNameOrInteger> {
|
|
match *self {
|
|
PdfObjectNonNull::Name(ref v) => Some(PdfNameOrInteger::Name(v.clone())),
|
|
PdfObjectNonNull::Integer(v) => Some(PdfNameOrInteger::Integer(v)),
|
|
PdfObjectNonNull::Boolean(_)
|
|
| PdfObjectNonNull::Real(_)
|
|
| PdfObjectNonNull::String(_)
|
|
| PdfObjectNonNull::Array(_)
|
|
| PdfObjectNonNull::Dictionary(_)
|
|
| PdfObjectNonNull::Stream(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfNameOrInteger {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for PdfNameOrInteger {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed("name or integer")
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
let object = PdfObjectDirect::from(object);
|
|
object.name_or_integer().ok_or(PdfParseError::InvalidType {
|
|
pos: object.pos(),
|
|
ty: object.type_name(),
|
|
expected_ty: "name or integer",
|
|
})
|
|
}
|
|
}
|
|
|
|
macro_rules! make_pdf_object {
|
|
(
|
|
$(
|
|
#[parse = $($parse:ident)?, type_name = $type_name:literal]
|
|
$Variant:ident($ty:ty),
|
|
)+
|
|
) => {
|
|
#[derive(Clone)]
|
|
pub enum PdfObjectNonNull {
|
|
$($Variant($ty),)*
|
|
}
|
|
|
|
impl fmt::Debug for PdfObjectNonNull {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
$(Self::$Variant(v) => v.fmt(f),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfObjectNonNull {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum PdfObjectDirect {
|
|
$($Variant($ty),)*
|
|
Null(PdfNull),
|
|
}
|
|
|
|
impl IsPdfNull for PdfObjectDirect {
|
|
fn is_pdf_null(&self) -> bool {
|
|
self.is_null()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PdfObjectDirect {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
$(Self::$Variant(v) => v.fmt(f),)*
|
|
Self::Null(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum PdfObject {
|
|
$($Variant($ty),)*
|
|
Null(PdfNull),
|
|
Indirect(PdfObjectIndirect),
|
|
}
|
|
|
|
impl IsPdfNull for PdfObject {
|
|
fn is_pdf_null(&self) -> bool {
|
|
self.is_null()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PdfObject {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
$(Self::$Variant(v) => v.fmt(f),)*
|
|
Self::Null(v) => v.fmt(f),
|
|
Self::Indirect(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
$(
|
|
impl From<$ty> for PdfObjectNonNull {
|
|
fn from(value: $ty) -> Self {
|
|
Self::$Variant(value)
|
|
}
|
|
}
|
|
|
|
impl From<$ty> for PdfObjectDirect {
|
|
fn from(value: $ty) -> Self {
|
|
Self::$Variant(value)
|
|
}
|
|
}
|
|
|
|
impl From<$ty> for PdfObject {
|
|
fn from(value: $ty) -> Self {
|
|
Self::$Variant(value)
|
|
}
|
|
}
|
|
|
|
impl From<Option<$ty>> for PdfObjectDirect {
|
|
fn from(value: Option<$ty>) -> Self {
|
|
match value {
|
|
Some(value) => Self::$Variant(value),
|
|
None => Self::Null(Default::default()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Option<$ty>> for PdfObject {
|
|
fn from(value: Option<$ty>) -> Self {
|
|
match value {
|
|
Some(value) => Self::$Variant(value),
|
|
None => Self::Null(Default::default()),
|
|
}
|
|
}
|
|
}
|
|
|
|
$(impl IsPdfNull for $ty {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for $ty {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed($type_name)
|
|
}
|
|
fn $parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
match PdfObjectDirect::from(object) {
|
|
PdfObjectDirect::$Variant(v) => Ok(v),
|
|
object => Err(PdfParseError::InvalidType {
|
|
pos: object.get_pdf_input_position(),
|
|
ty: object.type_name(),
|
|
expected_ty: $type_name,
|
|
}),
|
|
}
|
|
}
|
|
})?
|
|
)*
|
|
|
|
impl From<PdfObjectNonNull> for PdfObjectDirect {
|
|
fn from(value: PdfObjectNonNull) -> Self {
|
|
match value {
|
|
$(PdfObjectNonNull::$Variant(v) => Self::$Variant(v),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PdfObjectNonNull> for PdfObject {
|
|
fn from(value: PdfObjectNonNull) -> Self {
|
|
match value {
|
|
$(PdfObjectNonNull::$Variant(v) => Self::$Variant(v),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PdfObjectDirect> for PdfObject {
|
|
fn from(value: PdfObjectDirect) -> Self {
|
|
match value {
|
|
$(PdfObjectDirect::$Variant(v) => Self::$Variant(v),)*
|
|
PdfObjectDirect::Null(v) => Self::Null(v),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PdfObject> for PdfObjectDirect {
|
|
fn from(value: PdfObject) -> Self {
|
|
match value {
|
|
$(PdfObject::$Variant(v) => Self::$Variant(v),)*
|
|
PdfObject::Null(v) => Self::Null(v),
|
|
PdfObject::Indirect(v) => v.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfObjectNonNull {
|
|
pub fn type_name(&self) -> &'static str {
|
|
match self {
|
|
$(PdfObjectNonNull::$Variant(_) => $type_name,)*
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.get_pdf_input_position()
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfObjectNonNull {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
match self {
|
|
$(PdfObjectNonNull::$Variant(v) => <$ty as GetPdfInputPosition>::get_pdf_input_position(v),)*
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PdfObjectDirect> for Option<PdfObjectNonNull> {
|
|
fn from(value: PdfObjectDirect) -> Self {
|
|
match value {
|
|
$(PdfObjectDirect::$Variant(v) => Some(PdfObjectNonNull::$Variant(v)),)*
|
|
PdfObjectDirect::Null(_) => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PdfObject> for Option<PdfObjectNonNull> {
|
|
fn from(value: PdfObject) -> Self {
|
|
PdfObjectDirect::from(value).into()
|
|
}
|
|
}
|
|
|
|
impl PdfObjectDirect {
|
|
pub fn is_null(&self) -> bool {
|
|
matches!(self, PdfObjectDirect::Null(_))
|
|
}
|
|
pub fn type_name(&self) -> &'static str {
|
|
match self {
|
|
$(PdfObjectDirect::$Variant(_) => $type_name,)*
|
|
PdfObjectDirect::Null(_) => "null",
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.get_pdf_input_position()
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfObjectDirect {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
match self {
|
|
$(PdfObjectDirect::$Variant(v) => <$ty as GetPdfInputPosition>::get_pdf_input_position(v),)*
|
|
PdfObjectDirect::Null(v) => <PdfNull as GetPdfInputPosition>::get_pdf_input_position(v),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PdfObject {
|
|
pub fn is_null(&self) -> bool {
|
|
matches!(self, PdfObject::Null(_))
|
|
}
|
|
pub fn type_name(&self) -> &'static str {
|
|
match self {
|
|
$(PdfObject::$Variant(_) => $type_name,)*
|
|
PdfObject::Null(_) => "null",
|
|
PdfObject::Indirect(_) => "indirect object",
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.get_pdf_input_position()
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfObject {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
match self {
|
|
$(PdfObject::$Variant(v) => <$ty as GetPdfInputPosition>::get_pdf_input_position(v),)*
|
|
PdfObject::Null(v) => <PdfNull as GetPdfInputPosition>::get_pdf_input_position(v),
|
|
PdfObject::Indirect(v) => <PdfObjectIndirect as GetPdfInputPosition>::get_pdf_input_position(v),
|
|
}
|
|
}
|
|
}
|
|
|
|
const _: () = {
|
|
fn _assert_parsable<T: PdfParse>() {}
|
|
|
|
$(let _ = _assert_parsable::<$ty>;)*
|
|
let _ = _assert_parsable::<PdfNull>;
|
|
let _ = _assert_parsable::<PdfObjectIndirect>;
|
|
let _ = _assert_parsable::<PdfObjectNonNull>;
|
|
let _ = _assert_parsable::<PdfObjectDirect>;
|
|
let _ = _assert_parsable::<PdfObject>;
|
|
};
|
|
};
|
|
}
|
|
|
|
make_pdf_object! {
|
|
#[parse = parse, type_name = "boolean"]
|
|
Boolean(PdfBoolean),
|
|
#[parse = parse, type_name = "integer"]
|
|
Integer(PdfInteger),
|
|
#[parse = parse, type_name = "real"]
|
|
Real(PdfReal),
|
|
#[parse = parse, type_name = "string"]
|
|
String(PdfString),
|
|
#[parse = parse, type_name = "name"]
|
|
Name(PdfName),
|
|
#[parse = parse, type_name = "array"]
|
|
Array(PdfArray),
|
|
#[parse =, type_name = "dictionary"]
|
|
Dictionary(PdfDictionary),
|
|
#[parse =, type_name = "stream"]
|
|
Stream(PdfStream),
|
|
}
|
|
|
|
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct PdfNull(PdfInputPositionNoCompare);
|
|
|
|
impl fmt::Debug for PdfNull {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "PdfNull(at {})", self.0)
|
|
}
|
|
}
|
|
|
|
impl PdfNull {
|
|
pub fn new(pos: impl Into<PdfInputPositionNoCompare>) -> Self {
|
|
Self(pos.into())
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfNull {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.0.0
|
|
}
|
|
}
|
|
|
|
impl From<PdfNull> for PdfObjectDirect {
|
|
fn from(v: PdfNull) -> Self {
|
|
Self::Null(v)
|
|
}
|
|
}
|
|
|
|
impl Default for PdfObjectDirect {
|
|
fn default() -> Self {
|
|
Self::Null(PdfNull(PdfInputPositionNoCompare::empty()))
|
|
}
|
|
}
|
|
|
|
impl From<PdfNull> for PdfObject {
|
|
fn from(v: PdfNull) -> Self {
|
|
Self::Null(v)
|
|
}
|
|
}
|
|
|
|
impl Default for PdfObject {
|
|
fn default() -> Self {
|
|
Self::Null(PdfNull(PdfInputPositionNoCompare::empty()))
|
|
}
|
|
}
|
|
|
|
impl From<PdfObjectIndirect> for PdfObject {
|
|
fn from(v: PdfObjectIndirect) -> Self {
|
|
Self::Indirect(v)
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
|
pub struct PdfObjectIdentifier {
|
|
pub pos: PdfInputPositionNoCompare,
|
|
pub object_number: NonZero<u32>,
|
|
pub generation_number: u16,
|
|
}
|
|
|
|
impl fmt::Debug for PdfObjectIdentifier {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
pos,
|
|
object_number,
|
|
generation_number,
|
|
} = *self;
|
|
write!(
|
|
f,
|
|
"PdfObjectIdentifier(at {pos}, {object_number}, {generation_number})"
|
|
)
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfObjectIdentifier {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PdfObjectIndirect {
|
|
objects: std::sync::Weak<PdfObjects>,
|
|
id: PdfObjectIdentifier,
|
|
final_id: Arc<OnceLock<PdfObjectIdentifier>>,
|
|
}
|
|
|
|
impl fmt::Debug for PdfObjectIndirect {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self {
|
|
objects: _,
|
|
id:
|
|
PdfObjectIdentifier {
|
|
pos,
|
|
object_number,
|
|
generation_number,
|
|
},
|
|
final_id: _,
|
|
} = *self;
|
|
write!(
|
|
f,
|
|
"PdfObjectIndirect(at {pos}, {object_number}, {generation_number})"
|
|
)
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfObjectIndirect {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.id.get_pdf_input_position()
|
|
}
|
|
}
|
|
|
|
impl PartialEq for PdfObjectIndirect {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
let Self {
|
|
objects,
|
|
id,
|
|
final_id: _,
|
|
} = self;
|
|
objects.ptr_eq(&other.objects) && *id == other.id
|
|
}
|
|
}
|
|
|
|
impl PdfObjectIndirect {
|
|
pub fn new(objects: &Arc<PdfObjects>, id: PdfObjectIdentifier) -> Self {
|
|
Self {
|
|
objects: Arc::downgrade(objects),
|
|
id,
|
|
final_id: Arc::new(OnceLock::new()),
|
|
}
|
|
}
|
|
pub(crate) fn cache_parse<T: 'static + Send + Sync, E>(
|
|
&self,
|
|
parse_inner: impl FnOnce(PdfObjectDirect) -> Result<Arc<T>, E>,
|
|
) -> Result<Arc<T>, E> {
|
|
self.get_object_and_parse_cache(|object, object_and_parse_cache| {
|
|
match object_and_parse_cache {
|
|
Some(object_and_parse_cache) => {
|
|
if let Some(retval) = object_and_parse_cache.parse_cache_get::<T>() {
|
|
println!("cache reused for {object:?}");
|
|
return Ok(retval);
|
|
}
|
|
parse_inner(object)
|
|
.map(|retval| object_and_parse_cache.parse_cache_get_or_insert::<T>(retval))
|
|
}
|
|
None => parse_inner(object),
|
|
}
|
|
})
|
|
}
|
|
fn get_object_and_parse_cache_inner<'a>(
|
|
&self,
|
|
objects: &'a PdfObjects,
|
|
) -> (PdfObjectDirect, Option<&'a PdfObjectAndParseCache>) {
|
|
if let Some(objects) = objects.inner.get() {
|
|
let final_id = self.final_id.get().copied();
|
|
let limit = if final_id.is_some() { 1 } else { 1000usize };
|
|
let mut id = final_id.unwrap_or(self.id);
|
|
for _ in 0..limit {
|
|
if let Some(object_and_parse_cache) = objects.objects.get(&self.id) {
|
|
let object = match &object_and_parse_cache.object {
|
|
PdfObject::Boolean(v) => PdfObjectDirect::Boolean(*v),
|
|
PdfObject::Integer(v) => PdfObjectDirect::Integer(*v),
|
|
PdfObject::Real(v) => PdfObjectDirect::Real(*v),
|
|
PdfObject::String(v) => PdfObjectDirect::String(v.clone()),
|
|
PdfObject::Name(v) => PdfObjectDirect::Name(v.clone()),
|
|
PdfObject::Array(v) => PdfObjectDirect::Array(v.clone()),
|
|
PdfObject::Dictionary(v) => PdfObjectDirect::Dictionary(v.clone()),
|
|
PdfObject::Stream(v) => PdfObjectDirect::Stream(v.clone()),
|
|
PdfObject::Null(v) => PdfObjectDirect::Null(*v),
|
|
PdfObject::Indirect(v) => {
|
|
id = v.id;
|
|
continue;
|
|
}
|
|
};
|
|
// we could be racing with another thread, so set can fail but that's not a problem
|
|
let _ = self.final_id.set(id);
|
|
return (object, Some(object_and_parse_cache));
|
|
} else {
|
|
return (PdfNull::new(id.pos).into(), None);
|
|
}
|
|
}
|
|
}
|
|
(PdfNull::new(self.pos()).into(), None)
|
|
}
|
|
fn get_object_and_parse_cache<R>(
|
|
&self,
|
|
f: impl FnOnce(PdfObjectDirect, Option<&PdfObjectAndParseCache>) -> R,
|
|
) -> R {
|
|
let Some(objects) = self.objects.upgrade() else {
|
|
panic!("PdfObjects is no longer available");
|
|
};
|
|
let (object, object_and_parse_cache) = self.get_object_and_parse_cache_inner(&objects);
|
|
f(object, object_and_parse_cache)
|
|
}
|
|
pub fn get(&self) -> PdfObjectDirect {
|
|
self.get_object_and_parse_cache(|object, _object_and_parse_cache| object)
|
|
}
|
|
pub fn id(&self) -> PdfObjectIdentifier {
|
|
self.id
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.id.pos.0
|
|
}
|
|
}
|
|
|
|
impl From<PdfObjectIndirect> for PdfObjectDirect {
|
|
fn from(value: PdfObjectIndirect) -> Self {
|
|
value.get()
|
|
}
|
|
}
|
|
|
|
pub trait IsPdfNull {
|
|
fn is_pdf_null(&self) -> bool;
|
|
}
|
|
|
|
impl<T: IsPdfNull> IsPdfNull for Option<T> {
|
|
fn is_pdf_null(&self) -> bool {
|
|
self.as_ref().is_none_or(IsPdfNull::is_pdf_null)
|
|
}
|
|
}
|
|
|
|
pub struct PdfDictionary<T = PdfObject> {
|
|
pos: PdfInputPositionNoCompare,
|
|
fields: Arc<BTreeMap<PdfName, T>>,
|
|
}
|
|
|
|
impl<T> Clone for PdfDictionary<T> {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
pos: self.pos,
|
|
fields: self.fields.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> PdfDictionary<T> {
|
|
pub fn new(pos: impl Into<PdfInputPositionNoCompare>) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
fields: Arc::new(BTreeMap::new()),
|
|
}
|
|
}
|
|
pub fn from_fields(
|
|
pos: impl Into<PdfInputPositionNoCompare>,
|
|
mut fields: Arc<BTreeMap<PdfName, T>>,
|
|
) -> Self
|
|
where
|
|
T: IsPdfNull + Clone,
|
|
{
|
|
if fields.values().any(T::is_pdf_null) {
|
|
Arc::make_mut(&mut fields).retain(|_k, v| !v.is_pdf_null());
|
|
}
|
|
Self {
|
|
pos: pos.into(),
|
|
fields,
|
|
}
|
|
}
|
|
pub fn fields(&self) -> &Arc<BTreeMap<PdfName, T>> {
|
|
&self.fields
|
|
}
|
|
pub fn into_fields(self) -> Arc<BTreeMap<PdfName, T>> {
|
|
self.fields
|
|
}
|
|
pub fn iter(&self) -> std::collections::btree_map::Iter<'_, PdfName, T> {
|
|
self.fields.iter()
|
|
}
|
|
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
|
|
where
|
|
PdfName: std::borrow::Borrow<Q>,
|
|
Q: Ord,
|
|
{
|
|
self.fields.contains_key(key)
|
|
}
|
|
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&T>
|
|
where
|
|
PdfName: std::borrow::Borrow<Q>,
|
|
Q: Ord,
|
|
{
|
|
self.fields.get(key)
|
|
}
|
|
pub fn get_or_null<Q: ?Sized>(&self, key: &Q) -> T
|
|
where
|
|
PdfName: std::borrow::Borrow<Q>,
|
|
Q: Ord,
|
|
T: Clone + From<PdfNull>,
|
|
{
|
|
self.get(key)
|
|
.cloned()
|
|
.unwrap_or_else(|| PdfNull(self.pos).into())
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
impl<T> GetPdfInputPosition for PdfDictionary<T> {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
impl<T> Default for PdfDictionary<T> {
|
|
fn default() -> Self {
|
|
Self::new(PdfInputPosition::empty())
|
|
}
|
|
}
|
|
|
|
impl<T: IsPdfNull> FromIterator<(PdfName, T)> for PdfDictionary<T> {
|
|
fn from_iter<I: IntoIterator<Item = (PdfName, T)>>(iter: I) -> Self {
|
|
Self {
|
|
pos: PdfInputPositionNoCompare::empty(),
|
|
fields: Arc::new(BTreeMap::from_iter(
|
|
iter.into_iter()
|
|
.filter(|(_name, value)| !value.is_pdf_null()),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: Clone> IntoIterator for PdfDictionary<T> {
|
|
type Item = (PdfName, T);
|
|
type IntoIter = std::collections::btree_map::IntoIter<PdfName, T>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
Arc::unwrap_or_clone(self.fields).into_iter()
|
|
}
|
|
}
|
|
|
|
impl<'a, T> IntoIterator for &'a PdfDictionary<T> {
|
|
type Item = (&'a PdfName, &'a T);
|
|
type IntoIter = std::collections::btree_map::Iter<'a, PdfName, T>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
self.fields.iter()
|
|
}
|
|
}
|
|
|
|
impl<T: fmt::Debug + 'static> fmt::Debug for PdfDictionary<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
DagDebugState::scope(|state| {
|
|
state
|
|
.debug_or_id_with(
|
|
&self.fields,
|
|
|_, f| f.debug_map().entries(self).finish(),
|
|
|f| f.write_str("{...}"),
|
|
)
|
|
.fmt(f)
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<T> IsPdfNull for PdfDictionary<T> {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl<T: PdfParse> PdfParse for PdfDictionary<T> {
|
|
fn type_name() -> Cow<'static, str> {
|
|
if TypeId::of::<T>() == TypeId::of::<PdfObject>() {
|
|
Cow::Borrowed("dictionary")
|
|
} else {
|
|
Cow::Owned(format!("PdfDictionary<{}>", T::type_name()))
|
|
}
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
let object = PdfObjectDirect::from(object);
|
|
let PdfObjectDirect::Dictionary(object) = object else {
|
|
return Err(PdfParseError::InvalidType {
|
|
pos: object.pos(),
|
|
ty: object.type_name(),
|
|
expected_ty: "dictionary",
|
|
});
|
|
};
|
|
if let Some(retval) = <dyn std::any::Any>::downcast_ref::<Self>(&object) {
|
|
return Ok(retval.clone());
|
|
}
|
|
let pos = object.pos;
|
|
let fields = Result::from_iter(object.fields.iter().filter_map(|(name, value)| {
|
|
match T::parse(value.clone()) {
|
|
Ok(value) => {
|
|
if value.is_pdf_null() {
|
|
None
|
|
} else {
|
|
Some(Ok((name.clone(), value)))
|
|
}
|
|
}
|
|
Err(e) => Some(Err(e)),
|
|
}
|
|
}))?;
|
|
Ok(Self {
|
|
pos,
|
|
fields: Arc::new(fields),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default)]
|
|
pub struct PdfArray {
|
|
pos: PdfInputPositionNoCompare,
|
|
elements: Arc<[PdfObject]>,
|
|
}
|
|
|
|
impl PdfArray {
|
|
pub fn new(pos: impl Into<PdfInputPositionNoCompare>) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
elements: Arc::default(),
|
|
}
|
|
}
|
|
pub fn from_elements(
|
|
pos: impl Into<PdfInputPositionNoCompare>,
|
|
elements: Arc<[PdfObject]>,
|
|
) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
elements,
|
|
}
|
|
}
|
|
pub fn pos(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
pub fn elements(&self) -> &Arc<[PdfObject]> {
|
|
&self.elements
|
|
}
|
|
pub fn into_elements(self) -> Arc<[PdfObject]> {
|
|
self.elements
|
|
}
|
|
pub fn iter(&self) -> std::slice::Iter<'_, PdfObject> {
|
|
self.elements.iter()
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfArray {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
impl FromIterator<PdfObject> for PdfArray {
|
|
fn from_iter<T: IntoIterator<Item = PdfObject>>(iter: T) -> Self {
|
|
Self {
|
|
pos: PdfInputPositionNoCompare::empty(),
|
|
elements: Arc::from_iter(iter),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PdfArrayIntoIter {
|
|
indexes: std::ops::Range<usize>,
|
|
elements: Arc<[PdfObject]>,
|
|
}
|
|
|
|
impl Iterator for PdfArrayIntoIter {
|
|
type Item = PdfObject;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
self.indexes.next().map(|i| self.elements[i].clone())
|
|
}
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
self.indexes.size_hint()
|
|
}
|
|
|
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
|
self.indexes.nth(n).map(|i| self.elements[i].clone())
|
|
}
|
|
|
|
fn last(self) -> Option<Self::Item> {
|
|
self.indexes.last().map(|i| self.elements[i].clone())
|
|
}
|
|
|
|
fn fold<B, F>(self, init: B, mut f: F) -> B
|
|
where
|
|
F: FnMut(B, Self::Item) -> B,
|
|
{
|
|
self.indexes
|
|
.fold(init, |init, i| f(init, self.elements[i].clone()))
|
|
}
|
|
}
|
|
|
|
impl std::iter::FusedIterator for PdfArrayIntoIter {}
|
|
|
|
impl DoubleEndedIterator for PdfArrayIntoIter {
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
self.indexes.next_back().map(|i| self.elements[i].clone())
|
|
}
|
|
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
|
self.indexes.nth_back(n).map(|i| self.elements[i].clone())
|
|
}
|
|
fn rfold<B, F>(self, init: B, mut f: F) -> B
|
|
where
|
|
F: FnMut(B, Self::Item) -> B,
|
|
{
|
|
self.indexes
|
|
.rfold(init, |init, i| f(init, self.elements[i].clone()))
|
|
}
|
|
}
|
|
|
|
impl ExactSizeIterator for PdfArrayIntoIter {}
|
|
|
|
impl IntoIterator for PdfArray {
|
|
type Item = PdfObject;
|
|
type IntoIter = PdfArrayIntoIter;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
PdfArrayIntoIter {
|
|
indexes: 0..self.elements.len(),
|
|
elements: self.elements,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a PdfArray {
|
|
type Item = &'a PdfObject;
|
|
type IntoIter = std::slice::Iter<'a, PdfObject>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
self.elements.iter()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PdfArray {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.elements.fmt(f)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
pub struct MaybeArray<T>(pub Arc<[T]>);
|
|
|
|
impl<T: fmt::Debug> fmt::Debug for MaybeArray<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.0.fmt(f)
|
|
}
|
|
}
|
|
|
|
impl<T> std::ops::Deref for MaybeArray<T> {
|
|
type Target = Arc<[T]>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<T> std::ops::DerefMut for MaybeArray<T> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl<T> Default for MaybeArray<T> {
|
|
fn default() -> Self {
|
|
Self(Arc::default())
|
|
}
|
|
}
|
|
|
|
impl<'a, T> IntoIterator for &'a MaybeArray<T> {
|
|
type Item = &'a T;
|
|
type IntoIter = std::slice::Iter<'a, T>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
self.iter()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum NameOr<T> {
|
|
Name(PdfName),
|
|
Value(T),
|
|
}
|
|
|
|
impl<T> NameOr<T> {
|
|
pub fn into_resolved<E>(self, resolve: impl FnOnce(PdfName) -> Result<T, E>) -> Result<T, E> {
|
|
match self {
|
|
Self::Name(name) => resolve(name),
|
|
Self::Value(v) => Ok(v),
|
|
}
|
|
}
|
|
pub fn replace_with_resolved<E>(
|
|
&mut self,
|
|
resolve: impl FnOnce(&PdfName) -> Result<T, E>,
|
|
) -> Result<&mut T, E> {
|
|
match self {
|
|
Self::Name(name) => {
|
|
*self = Self::Value(resolve(name)?);
|
|
let Self::Value(v) = self else {
|
|
unreachable!();
|
|
};
|
|
Ok(v)
|
|
}
|
|
Self::Value(v) => Ok(v),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: fmt::Debug> fmt::Debug for NameOr<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Name(v) => v.fmt(f),
|
|
Self::Value(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: GetPdfInputPosition> GetPdfInputPosition for NameOr<T> {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
match self {
|
|
Self::Name(v) => v.pos(),
|
|
Self::Value(v) => v.get_pdf_input_position(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: IsPdfNull> IsPdfNull for NameOr<T> {
|
|
fn is_pdf_null(&self) -> bool {
|
|
match self {
|
|
Self::Name(_) => false,
|
|
Self::Value(v) => v.is_pdf_null(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T: PdfParse> PdfParse for NameOr<T> {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Owned(format!("NameOr<{}>", T::type_name()))
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
Ok(match PdfObjectDirect::from(object) {
|
|
PdfObjectDirect::Name(name) => Self::Name(name),
|
|
object => Self::Value(T::parse(object.into())?),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub struct PdfMatrix {
|
|
pub pos: PdfInputPositionNoCompare,
|
|
pub elements: [f32; 6],
|
|
}
|
|
|
|
impl PdfMatrix {
|
|
pub fn identity(pos: impl Into<PdfInputPositionNoCompare>) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
elements: [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
|
|
}
|
|
}
|
|
#[must_use]
|
|
pub fn mul(self, other: PdfMatrix, new_pos: impl Into<PdfInputPositionNoCompare>) -> Self {
|
|
let [la, lb, lc, ld, le, lf] = self.elements;
|
|
let [ra, rb, rc, rd, re, rf] = other.elements;
|
|
Self {
|
|
pos: new_pos.into(),
|
|
elements: [
|
|
lb * rc + la * ra,
|
|
lb * rd + la * rb,
|
|
ld * rc + lc * ra,
|
|
ld * rd + lc * rb,
|
|
re + lf * rc + le * ra,
|
|
rf + lf * rd + le * rb,
|
|
],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for PdfMatrix {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self { pos, elements } = *self;
|
|
write!(f, "PdfMatrix(at {pos}, {elements:?})")
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfMatrix {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for PdfMatrix {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed("matrix")
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
Ok(Self {
|
|
pos: object.pos().into(),
|
|
elements: PdfParse::parse(object)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl PdfMatrix {
|
|
pub fn parse_flat(
|
|
a: PdfObject,
|
|
b: PdfObject,
|
|
c: PdfObject,
|
|
d: PdfObject,
|
|
e: PdfObject,
|
|
f: PdfObject,
|
|
) -> Result<Self, PdfParseError> {
|
|
Ok(Self {
|
|
pos: a.pos().into(),
|
|
elements: [
|
|
PdfParse::parse(a)?,
|
|
PdfParse::parse(b)?,
|
|
PdfParse::parse(c)?,
|
|
PdfParse::parse(d)?,
|
|
PdfParse::parse(e)?,
|
|
PdfParse::parse(f)?,
|
|
],
|
|
})
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfMatrix {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
pub struct PdfVec2D {
|
|
pub pos: PdfInputPositionNoCompare,
|
|
pub x: f32,
|
|
pub y: f32,
|
|
}
|
|
|
|
impl fmt::Debug for PdfVec2D {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let Self { pos, x, y } = *self;
|
|
write!(f, "PdfVec2D(at {pos}, {x}, {y})")
|
|
}
|
|
}
|
|
|
|
impl PdfVec2D {
|
|
pub fn parse(x: PdfObject, y: PdfObject) -> Result<Self, PdfParseError> {
|
|
Ok(Self {
|
|
pos: x.pos().into(),
|
|
x: PdfParse::parse(x)?,
|
|
y: PdfParse::parse(y)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfVec2D {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct PdfRectangle {
|
|
/// the corner with the smaller x and y coordinates
|
|
smaller: PdfVec2D,
|
|
/// the corner with the larger x and y coordinates
|
|
larger: PdfVec2D,
|
|
}
|
|
|
|
impl PdfRectangle {
|
|
pub fn new(mut smaller: PdfVec2D, mut larger: PdfVec2D) -> Self {
|
|
// `pos` follows the `x` coordinate
|
|
if smaller.x.is_nan() {
|
|
smaller.pos = larger.pos;
|
|
} else if larger.x.is_nan() {
|
|
larger.pos = smaller.pos;
|
|
} else if larger.x < smaller.x {
|
|
std::mem::swap(&mut smaller.pos, &mut larger.pos);
|
|
}
|
|
Self {
|
|
smaller: PdfVec2D {
|
|
pos: smaller.pos,
|
|
x: smaller.x.min(larger.x),
|
|
y: smaller.y.min(larger.y),
|
|
},
|
|
larger: PdfVec2D {
|
|
pos: larger.pos,
|
|
x: smaller.x.max(larger.x),
|
|
y: smaller.y.max(larger.y),
|
|
},
|
|
}
|
|
}
|
|
/// return the corner with the smaller x and y coordinates
|
|
pub fn smaller(&self) -> PdfVec2D {
|
|
self.smaller
|
|
}
|
|
/// return the corner with the larger x and y coordinates
|
|
pub fn larger(&self) -> PdfVec2D {
|
|
self.larger
|
|
}
|
|
pub fn parse_flat(
|
|
lower_left_x: PdfObject,
|
|
lower_left_y: PdfObject,
|
|
upper_right_x: PdfObject,
|
|
upper_right_y: PdfObject,
|
|
) -> Result<Self, PdfParseError> {
|
|
Ok(Self::new(
|
|
PdfVec2D::parse(lower_left_x, lower_left_y)?,
|
|
PdfVec2D::parse(upper_right_x, upper_right_y)?,
|
|
))
|
|
}
|
|
}
|
|
|
|
impl GetPdfInputPosition for PdfRectangle {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.smaller.get_pdf_input_position()
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfRectangle {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for PdfRectangle {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed("rectangle")
|
|
}
|
|
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
let object = object.into();
|
|
let PdfObjectDirect::Array(array) = &object else {
|
|
return Err(PdfParseError::InvalidType {
|
|
pos: object.pos(),
|
|
ty: object.type_name(),
|
|
expected_ty: "rectangle",
|
|
});
|
|
};
|
|
let [lower_left_x, lower_left_y, upper_right_x, upper_right_y] = &**array.elements() else {
|
|
return Err(PdfParseError::InvalidType {
|
|
pos: object.pos(),
|
|
ty: object.type_name(),
|
|
expected_ty: "rectangle",
|
|
});
|
|
};
|
|
Self::parse_flat(
|
|
lower_left_x.clone(),
|
|
lower_left_y.clone(),
|
|
upper_right_x.clone(),
|
|
upper_right_y.clone(),
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub enum PdfFileSpecification {
|
|
String(PdfString),
|
|
Dictionary(PdfDictionary),
|
|
}
|
|
|
|
impl fmt::Debug for PdfFileSpecification {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::String(v) => v.fmt(f),
|
|
Self::Dictionary(v) => v.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IsPdfNull for PdfFileSpecification {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl PdfParse for PdfFileSpecification {
|
|
fn type_name() -> Cow<'static, str> {
|
|
Cow::Borrowed("file specification")
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
match PdfObjectDirect::from(object) {
|
|
PdfObjectDirect::String(v) => Ok(Self::String(v)),
|
|
PdfObjectDirect::Dictionary(v) => Ok(Self::Dictionary(v)),
|
|
object => Err(PdfParseError::InvalidType {
|
|
pos: object.pos(),
|
|
ty: object.type_name(),
|
|
expected_ty: "PdfFileSpecification",
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
pdf_parse! {
|
|
#[pdf]
|
|
#[derive(Clone)]
|
|
pub struct PdfStreamDictionary<Rest = PdfDictionary> {
|
|
#[pdf(name = "Length")]
|
|
pub len: usize,
|
|
#[pdf(name = "Filter")]
|
|
pub filters: MaybeArray<PdfStreamFilter>,
|
|
#[pdf(name = "DecodeParms")]
|
|
pub decode_parms: MaybeArray<Option<PdfDictionary>>,
|
|
#[pdf(name = "F")]
|
|
pub file: Option<PdfFileSpecification>,
|
|
#[pdf(name = "FFilter")]
|
|
pub file_filters: MaybeArray<PdfStreamFilter>,
|
|
#[pdf(name = "FDecodeParms")]
|
|
pub file_decode_parms: MaybeArray<Option<PdfDictionary>>,
|
|
#[pdf(name = "DL")]
|
|
pub decoded_len: Option<usize>,
|
|
#[pdf(flatten)]
|
|
pub rest: Rest,
|
|
}
|
|
}
|
|
|
|
impl<Rest: fmt::Debug> fmt::Debug for PdfStreamDictionary<Rest> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
DagDebugState::scope(|_state| {
|
|
let Self {
|
|
len,
|
|
filters,
|
|
decode_parms,
|
|
file,
|
|
file_filters,
|
|
file_decode_parms,
|
|
decoded_len,
|
|
rest,
|
|
} = self;
|
|
f.debug_struct("PdfStreamDictionary")
|
|
.field("len", len)
|
|
.field("filters", filters)
|
|
.field("decode_parms", decode_parms)
|
|
.field("file", file)
|
|
.field("file_filters", file_filters)
|
|
.field("file_decode_parms", file_decode_parms)
|
|
.field("decoded_len", decoded_len)
|
|
.field("rest", rest)
|
|
.finish()
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct PdfStreamDictionaryFiltersAndParms<'a> {
|
|
filters: std::iter::Enumerate<std::slice::Iter<'a, PdfStreamFilter>>,
|
|
decode_parms: &'a [Option<PdfDictionary>],
|
|
}
|
|
|
|
impl<'a> PdfStreamDictionaryFiltersAndParms<'a> {
|
|
fn item_helper(
|
|
filter: (usize, &'a PdfStreamFilter),
|
|
decode_parms: &'a [Option<PdfDictionary>],
|
|
) -> (&'a PdfStreamFilter, &'a PdfDictionary) {
|
|
static EMPTY_DICTIONARY: OnceLock<PdfDictionary> = OnceLock::new();
|
|
let (index, filter) = filter;
|
|
(
|
|
filter,
|
|
match decode_parms.get(index) {
|
|
Some(Some(v)) => v,
|
|
_ => EMPTY_DICTIONARY.get_or_init(PdfDictionary::default),
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for PdfStreamDictionaryFiltersAndParms<'a> {
|
|
type Item = (&'a PdfStreamFilter, &'a PdfDictionary);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
self.filters
|
|
.next()
|
|
.map(|filter| Self::item_helper(filter, self.decode_parms))
|
|
}
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
self.filters.size_hint()
|
|
}
|
|
|
|
fn nth(&mut self, n: usize) -> Option<Self::Item> {
|
|
self.filters
|
|
.nth(n)
|
|
.map(|filter| Self::item_helper(filter, self.decode_parms))
|
|
}
|
|
|
|
fn fold<B, F>(self, init: B, f: F) -> B
|
|
where
|
|
F: FnMut(B, Self::Item) -> B,
|
|
{
|
|
self.filters
|
|
.map(|filter| Self::item_helper(filter, self.decode_parms))
|
|
.fold(init, f)
|
|
}
|
|
}
|
|
|
|
impl<'a> FusedIterator for PdfStreamDictionaryFiltersAndParms<'a> {}
|
|
|
|
impl<'a> ExactSizeIterator for PdfStreamDictionaryFiltersAndParms<'a> {}
|
|
|
|
impl<'a> DoubleEndedIterator for PdfStreamDictionaryFiltersAndParms<'a> {
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
self.filters
|
|
.next_back()
|
|
.map(|filter| Self::item_helper(filter, self.decode_parms))
|
|
}
|
|
|
|
fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
|
|
self.filters
|
|
.nth_back(n)
|
|
.map(|filter| Self::item_helper(filter, self.decode_parms))
|
|
}
|
|
|
|
fn rfold<B, F>(self, init: B, f: F) -> B
|
|
where
|
|
F: FnMut(B, Self::Item) -> B,
|
|
{
|
|
self.filters
|
|
.map(|filter| Self::item_helper(filter, self.decode_parms))
|
|
.rfold(init, f)
|
|
}
|
|
}
|
|
|
|
impl<Rest> PdfStreamDictionary<Rest> {
|
|
pub fn filters_and_parms<'a>(&'a self) -> PdfStreamDictionaryFiltersAndParms<'a> {
|
|
PdfStreamDictionaryFiltersAndParms {
|
|
filters: self.filters.iter().enumerate(),
|
|
decode_parms: &self.decode_parms,
|
|
}
|
|
}
|
|
pub fn file_filters_and_parms<'a>(&'a self) -> PdfStreamDictionaryFiltersAndParms<'a> {
|
|
PdfStreamDictionaryFiltersAndParms {
|
|
filters: self.file_filters.iter().enumerate(),
|
|
decode_parms: &self.file_decode_parms,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait PdfStreamContents: Sized + fmt::Debug + 'static {
|
|
fn parse(
|
|
data: &[u8],
|
|
stream_pos: PdfInputPosition,
|
|
objects: Arc<PdfObjects>,
|
|
) -> Result<Self, PdfParseError>;
|
|
fn parse_arc(
|
|
data: Arc<[u8]>,
|
|
stream_pos: PdfInputPosition,
|
|
objects: Arc<PdfObjects>,
|
|
) -> Result<Self, PdfParseError> {
|
|
Self::parse(&*data, stream_pos, objects)
|
|
}
|
|
}
|
|
|
|
impl PdfStreamContents for Arc<[u8]> {
|
|
fn parse(
|
|
data: &[u8],
|
|
_stream_pos: PdfInputPosition,
|
|
_objects: Arc<PdfObjects>,
|
|
) -> Result<Self, PdfParseError> {
|
|
Ok(Arc::from(data))
|
|
}
|
|
fn parse_arc(
|
|
data: Arc<[u8]>,
|
|
_stream_pos: PdfInputPosition,
|
|
_objects: Arc<PdfObjects>,
|
|
) -> Result<Self, PdfParseError> {
|
|
Ok(data.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct PdfStream<Rest = PdfDictionary, Data: PdfStreamContents = Arc<[u8]>> {
|
|
pos: PdfInputPositionNoCompare,
|
|
objects: std::sync::Weak<PdfObjects>,
|
|
dictionary: PdfStreamDictionary<Rest>,
|
|
encoded_data: Arc<[u8]>,
|
|
decoded_data: Arc<OnceLock<Result<Data, PdfParseError>>>,
|
|
}
|
|
|
|
struct DumpBytes<'a>(&'a [u8]);
|
|
|
|
impl<'a> fmt::Debug for DumpBytes<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
fmt::Display::fmt(self, f)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for DumpBytes<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let mut first = true;
|
|
let mut fmt_chunk = |chunk: &[u8]| -> fmt::Result {
|
|
if first {
|
|
first = false;
|
|
} else {
|
|
f.write_str("\n")?;
|
|
}
|
|
write!(f, "\"{}\"", chunk.escape_ascii())
|
|
};
|
|
if self.0.is_empty() {
|
|
return fmt_chunk(self.0);
|
|
}
|
|
for chunk in self.0.chunks(32) {
|
|
fmt_chunk(chunk)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<Rest: fmt::Debug, Data: PdfStreamContents> fmt::Debug for PdfStream<Rest, Data> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
DagDebugState::scope(|state| {
|
|
state
|
|
.debug_or_id_with(
|
|
&self.decoded_data,
|
|
|_, f| {
|
|
let Self {
|
|
pos,
|
|
objects: _,
|
|
dictionary,
|
|
encoded_data,
|
|
decoded_data,
|
|
} = self;
|
|
let mut debug_struct = f.debug_struct("PdfStream");
|
|
debug_struct.field("pos", pos);
|
|
debug_struct.field("dictionary", dictionary);
|
|
debug_struct.field("encoded_data", &DumpBytes(encoded_data));
|
|
if let Some(decoded_data) = decoded_data.get() {
|
|
match decoded_data {
|
|
Ok(decoded_data) => {
|
|
if let Some(decoded_data) =
|
|
<dyn std::any::Any>::downcast_ref::<Arc<[u8]>>(decoded_data)
|
|
{
|
|
debug_struct
|
|
.field("decoded_data", &DumpBytes(&**decoded_data))
|
|
} else {
|
|
debug_struct.field("decoded_data", decoded_data)
|
|
}
|
|
}
|
|
Err(e) => debug_struct.field("decoded_data", &Err::<(), _>(e)),
|
|
};
|
|
} else {
|
|
debug_struct.field("decoded_data", &format_args!("<not-yet-decoded>"));
|
|
}
|
|
debug_struct.finish()
|
|
},
|
|
|f| f.write_str("PdfStream(...)"),
|
|
)
|
|
.fmt(f)
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<Rest, Data: PdfStreamContents> PdfStream<Rest, Data> {
|
|
pub fn new(
|
|
pos: impl Into<PdfInputPositionNoCompare>,
|
|
objects: &Arc<PdfObjects>,
|
|
dictionary: PdfStreamDictionary<Rest>,
|
|
encoded_data: Arc<[u8]>,
|
|
) -> Self {
|
|
Self {
|
|
pos: pos.into(),
|
|
objects: Arc::downgrade(objects),
|
|
dictionary,
|
|
encoded_data,
|
|
decoded_data: Arc::new(OnceLock::new()),
|
|
}
|
|
}
|
|
pub fn dictionary(&self) -> &PdfStreamDictionary<Rest> {
|
|
&self.dictionary
|
|
}
|
|
pub fn encoded_data(&self) -> &Arc<[u8]> {
|
|
&self.encoded_data
|
|
}
|
|
fn try_decode_data(&self) -> Result<Data, PdfParseError> {
|
|
let Some(objects) = self.objects.upgrade() else {
|
|
panic!("PdfObjects is no longer available");
|
|
};
|
|
let dictionary = self.dictionary();
|
|
let (data, filters) = if let Some(file) = &dictionary.file {
|
|
todo!()
|
|
} else {
|
|
(&self.encoded_data, dictionary.filters_and_parms())
|
|
};
|
|
if filters.len() == 0 {
|
|
return Data::parse_arc(data.clone(), self.pos.0, objects);
|
|
}
|
|
let mut data: &[u8] = data;
|
|
let mut buffer;
|
|
for (filter, filter_parms) in filters {
|
|
buffer = filter.decode_stream_data(filter_parms.clone(), self.pos.0, &data)?;
|
|
data = &buffer;
|
|
}
|
|
Data::parse(data, self.pos.0, objects)
|
|
}
|
|
pub fn decoded_data(&self) -> &Result<Data, PdfParseError> {
|
|
self.decoded_data.get_or_init(|| self.try_decode_data())
|
|
}
|
|
}
|
|
|
|
impl<Rest, Data: PdfStreamContents> GetPdfInputPosition for PdfStream<Rest, Data> {
|
|
fn get_pdf_input_position(&self) -> PdfInputPosition {
|
|
self.pos.0
|
|
}
|
|
}
|
|
|
|
impl<Rest, Data: PdfStreamContents> IsPdfNull for PdfStream<Rest, Data> {
|
|
fn is_pdf_null(&self) -> bool {
|
|
false
|
|
}
|
|
}
|
|
|
|
impl<Rest: PdfParse, Data: PdfStreamContents> PdfParse for PdfStream<Rest, Data> {
|
|
fn type_name() -> Cow<'static, str> {
|
|
if TypeId::of::<Rest>() == TypeId::of::<PdfDictionary>() {
|
|
Cow::Borrowed("stream")
|
|
} else {
|
|
Cow::Owned(format!("PdfStream<{}>", Rest::type_name()))
|
|
}
|
|
}
|
|
fn parse(object: PdfObject) -> Result<Self, PdfParseError> {
|
|
match PdfObjectDirect::from(object) {
|
|
PdfObjectDirect::Stream(stream) => {
|
|
Ok(PdfStream {
|
|
pos: stream.pos,
|
|
dictionary: {
|
|
let PdfStreamDictionary {
|
|
len,
|
|
filters,
|
|
decode_parms,
|
|
file,
|
|
file_filters,
|
|
file_decode_parms,
|
|
decoded_len,
|
|
rest,
|
|
} = stream.dictionary;
|
|
PdfStreamDictionary {
|
|
len,
|
|
filters,
|
|
decode_parms,
|
|
file,
|
|
file_filters,
|
|
file_decode_parms,
|
|
decoded_len,
|
|
rest: Rest::parse(rest.into())?,
|
|
}
|
|
},
|
|
encoded_data: stream.encoded_data,
|
|
decoded_data: if let Some(decoded_data) =
|
|
<dyn std::any::Any>::downcast_ref(&stream.decoded_data)
|
|
{
|
|
Arc::clone(decoded_data)
|
|
} else {
|
|
let Some(objects) = stream.objects.upgrade() else {
|
|
panic!("PdfObjects is no longer available");
|
|
};
|
|
Arc::new(
|
|
stream
|
|
.decoded_data
|
|
.get()
|
|
.cloned()
|
|
.map(|data| {
|
|
OnceLock::from(data.and_then(|data| {
|
|
Data::parse_arc(data, stream.pos.0, objects)
|
|
}))
|
|
})
|
|
.unwrap_or_default(),
|
|
)
|
|
},
|
|
objects: stream.objects,
|
|
})
|
|
}
|
|
object => Err(PdfParseError::InvalidType {
|
|
pos: object.get_pdf_input_position(),
|
|
ty: object.type_name(),
|
|
expected_ty: "stream",
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
|
|
pdf_parse! {
|
|
#[pdf(name)]
|
|
#[derive(Clone, Copy, Debug, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub enum PdfObjectStreamType {
|
|
#[pdf(name = "ObjStm")]
|
|
#[default]
|
|
ObjStm,
|
|
}
|
|
}
|
|
|
|
pdf_parse! {
|
|
#[pdf]
|
|
#[derive(Clone)]
|
|
pub struct PdfObjectStreamDictionary {
|
|
#[pdf(name = Self::TYPE_NAME)]
|
|
pub ty: PdfObjectStreamType,
|
|
#[pdf(name = "N")]
|
|
pub n: usize,
|
|
#[pdf(name = "First")]
|
|
pub first: usize,
|
|
#[pdf(name = "Extends")]
|
|
pub extends: Option<PdfObjectIndirect>,
|
|
#[pdf(flatten)]
|
|
pub rest: PdfDictionary,
|
|
}
|
|
}
|
|
|
|
impl PdfObjectStreamDictionary {
|
|
pub const TYPE_NAME: &str = "Type";
|
|
pub(crate) fn parse_type_from_dictionary(
|
|
dictionary: &PdfDictionary,
|
|
) -> Result<PdfObjectStreamType, PdfParseError> {
|
|
PdfParse::parse(dictionary.get_or_null(Self::TYPE_NAME.as_bytes()))
|
|
}
|
|
}
|