use std::collections::HashSet;
use crate::number::{fmt_int, fmt_number, narrow_to_signed32, truncate_to_signed32, widen_to_f32};
use crate::{Array, Dict, Error, Name, PdfString, Resolve, Resolved, Stream};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ObjRef {
pub num: u32,
pub generation: u16,
}
impl ObjRef {
const INVALID_NUM: u32 = 0xFFFF_FFFF;
#[must_use]
pub const fn new(num: u32, generation: u16) -> Self {
Self { num, generation }
}
#[must_use]
pub const fn is_invalid(self) -> bool {
self.num == Self::INVALID_NUM
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Object {
Null,
Bool(bool),
Int(i64),
Real(f32),
Str(PdfString),
Name(Name),
Array(Array),
Dict(Dict),
Stream(Box<Stream>),
Ref(ObjRef),
}
impl Object {
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Bool(b) => Some(*b),
_ => None,
}
}
#[must_use]
pub fn as_int(&self) -> Option<i64> {
match self {
Self::Bool(b) => Some(i64::from(*b)),
Self::Int(v) => Some(narrow_to_signed32(*v)),
Self::Real(v) => Some(truncate_to_signed32(*v)),
Self::Null
| Self::Str(_)
| Self::Name(_)
| Self::Array(_)
| Self::Dict(_)
| Self::Stream(_)
| Self::Ref(_) => None,
}
}
#[must_use]
pub fn number(&self) -> Option<f32> {
match self {
Self::Int(v) => Some(widen_to_f32(*v)),
Self::Real(v) => Some(*v),
Self::Null
| Self::Bool(_)
| Self::Str(_)
| Self::Name(_)
| Self::Array(_)
| Self::Dict(_)
| Self::Stream(_)
| Self::Ref(_) => None,
}
}
#[must_use]
pub fn as_number(&self) -> Option<&Self> {
match self {
Self::Int(_) | Self::Real(_) => Some(self),
_ => None,
}
}
#[must_use]
pub fn as_string(&self) -> Option<&PdfString> {
match self {
Self::Str(s) => Some(s),
_ => None,
}
}
#[must_use]
pub fn as_name(&self) -> Option<&Name> {
match self {
Self::Name(n) => Some(n),
_ => None,
}
}
#[must_use]
pub fn as_array(&self) -> Option<&Array> {
match self {
Self::Array(a) => Some(a),
_ => None,
}
}
#[must_use]
pub fn as_dict(&self) -> Option<&Dict> {
match self {
Self::Dict(d) => Some(d),
Self::Stream(s) => Some(&s.dict),
_ => None,
}
}
#[must_use]
pub fn as_stream(&self) -> Option<&Stream> {
match self {
Self::Stream(s) => Some(s),
_ => None,
}
}
#[must_use]
pub fn as_ref_id(&self) -> Option<ObjRef> {
match self {
Self::Ref(r) => Some(*r),
_ => None,
}
}
#[must_use]
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
#[must_use]
pub fn to_byte_string(&self) -> Vec<u8> {
match self {
Self::Bool(true) => b"true".to_vec(),
Self::Bool(false) => b"false".to_vec(),
Self::Int(v) => fmt_int(*v).into_bytes(),
Self::Real(v) => fmt_number(*v).into_bytes(),
Self::Str(s) => s.bytes.to_vec(),
Self::Name(n) => n.as_bytes().to_vec(),
Self::Null | Self::Array(_) | Self::Dict(_) | Self::Stream(_) | Self::Ref(_) => {
Vec::new()
}
}
}
#[must_use]
pub fn to_text(&self) -> String {
match self {
Self::Str(s) => s.as_text().into_owned(),
Self::Name(n) => n.as_text().into_owned(),
Self::Null
| Self::Bool(_)
| Self::Int(_)
| Self::Real(_)
| Self::Array(_)
| Self::Dict(_)
| Self::Stream(_)
| Self::Ref(_) => String::new(),
}
}
pub fn resolve<'a>(&'a self, r: &impl Resolve) -> Result<Resolved<'a>, Error> {
match self {
Self::Ref(id) => Ok(Resolved::Indirect(r.fetch(*id)?)),
_ => Ok(Resolved::Direct(self)),
}
}
#[must_use]
pub fn clone_direct(&self, r: &impl Resolve) -> Self {
let mut ancestors = HashSet::new();
clone_flattened(self, r, &mut ancestors).unwrap_or(Self::Null)
}
}
fn clone_flattened(
obj: &Object,
r: &impl Resolve,
ancestors: &mut HashSet<ObjRef>,
) -> Option<Object> {
match obj {
Object::Ref(id) => {
if !ancestors.insert(*id) {
return None;
}
let target = r.fetch(*id).ok();
let cloned = target
.as_deref()
.and_then(|t| clone_flattened(t, r, &mut ancestors.clone()));
ancestors.remove(id);
cloned
}
Object::Array(a) => Some(Object::Array(
a.iter()
.filter_map(|e| clone_flattened(e, r, &mut ancestors.clone()))
.collect(),
)),
Object::Dict(d) => Some(Object::Dict(
d.iter()
.filter_map(|(k, v)| {
clone_flattened(v, r, &mut ancestors.clone()).map(|v| (k.clone(), v))
})
.collect(),
)),
Object::Stream(s) => {
let dict = s
.dict
.iter()
.filter_map(|(k, v)| {
clone_flattened(v, r, &mut ancestors.clone()).map(|v| (k.clone(), v))
})
.collect();
Some(Object::Stream(Box::new(Stream::new(dict, s.data.clone()))))
}
other => Some(other.clone()),
}
}
impl From<bool> for Object {
fn from(v: bool) -> Self {
Self::Bool(v)
}
}
impl From<i64> for Object {
fn from(v: i64) -> Self {
Self::Int(v)
}
}
impl From<i32> for Object {
fn from(v: i32) -> Self {
Self::Int(i64::from(v))
}
}
impl From<f32> for Object {
fn from(v: f32) -> Self {
Self::Real(v)
}
}
impl From<Name> for Object {
fn from(v: Name) -> Self {
Self::Name(v)
}
}
impl From<PdfString> for Object {
fn from(v: PdfString) -> Self {
Self::Str(v)
}
}
impl From<Array> for Object {
fn from(v: Array) -> Self {
Self::Array(v)
}
}
impl From<Dict> for Object {
fn from(v: Dict) -> Self {
Self::Dict(v)
}
}
impl From<Stream> for Object {
fn from(v: Stream) -> Self {
Self::Stream(Box::new(v))
}
}
impl From<ObjRef> for Object {
fn from(v: ObjRef) -> Self {
Self::Ref(v)
}
}