pub enum Object {
Null,
Bool(bool),
Int(i64),
Real(f32),
Str(PdfString),
Name(Name),
Array(Array),
Dict(Dict),
Stream(Box<Stream>),
Ref(ObjRef),
}Expand description
A PDF object (ISO 32000-1 §7.3).
The eight basic types plus streams, plus a reference standing in for an
indirect object. Int and Real are separate variants because the
distinction is observable: an integer and a real that happen to be equal
serialize differently and read back differently through the integer
accessors.
use pdfrum_object::{Object, PdfString};
assert_eq!(Object::Int(1245).as_int(), Some(1245));
assert_eq!(Object::Real(9.5).number(), Some(9.5));
assert_eq!(Object::Bool(true).as_bool(), Some(true));
// A name has no numeric value.
assert_eq!(Object::Name("Foo".into()).number(), None);
// ...but every object has a string spelling, empty for most.
assert_eq!(Object::Str(PdfString::literal(b"hi")).to_byte_string(), b"hi");
assert_eq!(Object::Null.to_byte_string(), b"");Variants§
Null
The null object.
Bool(bool)
true or false.
Int(i64)
An integer.
Holds the mathematical value. Every integer a conforming lexer can
produce lies in -2^31 ..= 2^32 - 1
(see INT_RANGE) — larger literals fold to zero
during parsing. Reading it back has two flavours,
as_int and number, which
disagree above i32::MAX; see narrow_to_signed32.
Real(f32)
A real number. f32 rather than f64 to match the precision the
oracle parses, formats and renders with.
Str(PdfString)
A string, in either syntax.
Name(Name)
A name.
Array(Array)
An array.
Dict(Dict)
A dictionary.
Stream(Box<Stream>)
A stream: a dictionary with bytes attached.
Boxed because it is the one wide payload — a Dict plus a ByteSpan
is 56 bytes where every other payload is 24 — and the one that never
sits in the hot structures: ISO 32000-1 §7.3.8.1 forbids a stream as
a direct array element or dictionary value, so the box is only
dereferenced on the indirect-object path. It takes Object from 56
bytes to 32 and a dictionary pair from 80 to 56.
Ref(ObjRef)
A reference to an indirect object.
Implementations§
Source§impl Object
impl Object
Sourcepub fn as_bool(&self) -> Option<bool>
pub fn as_bool(&self) -> Option<bool>
The boolean value, only for an actual boolean.
Int(1) is deliberately not a boolean: the type check happens
before any coercion, so a file that writes 1 for a flag reads as
“absent, use the default”.
Sourcepub fn as_int(&self) -> Option<i64>
pub fn as_int(&self) -> Option<i64>
The integer value of any object that has one, in the C-integer view.
Booleans count as 0 and 1, reals truncate toward zero (saturating, NaN
to 0), and everything else has no integer value. Note this is not a
type test — use Object::as_number for “is this a number”.
Sourcepub fn number(&self) -> Option<f32>
pub fn number(&self) -> Option<f32>
The numeric value of a number, coercing integers to f32.
Only numbers have one — unlike Object::as_int, a boolean does not
count.
Sourcepub fn as_number(&self) -> Option<&Self>
pub fn as_number(&self) -> Option<&Self>
The number itself, for accessors that type-check before coercing.
Sourcepub fn as_dict(&self) -> Option<&Dict>
pub fn as_dict(&self) -> Option<&Dict>
The dictionary — of a dictionary object, or of a stream.
Streams answer with their own dictionary, which is what lets page-tree
and cross-reference code read /Type off either kind of object
without branching.
Sourcepub fn to_byte_string(&self) -> Vec<u8> ⓘ
pub fn to_byte_string(&self) -> Vec<u8> ⓘ
The object’s byte-string spelling.
Booleans spell true/false, numbers spell as the writer would, a
string yields its bytes and a name its decoded bytes. Everything else
— null, arrays, dictionaries, streams, references — has no spelling
and yields empty.
Sourcepub fn to_text(&self) -> String
pub fn to_text(&self) -> String
The object read as text: strings and names decode, everything else yields empty.
A stream’s text needs its filters applied first, which this crate
cannot do — the reader composes decoding with
decode_text instead.
Sourcepub fn resolve<'a>(&'a self, r: &impl Resolve) -> Result<Resolved<'a>, Error>
pub fn resolve<'a>(&'a self, r: &impl Resolve) -> Result<Resolved<'a>, Error>
Resolve one level: a reference becomes the object the store holds, anything else is already itself.
The result may still be a reference — an indirect object whose body is
8 0 R resolves to that reference and is not chased further, which
is why typed accessors go through Resolved::as_direct.
§Errors
Whatever the store reports for an unresolvable reference.
let direct = Object::Real(1.5);
assert_eq!(direct.resolve(&NoResolve).unwrap().number(), Some(1.5));
// Without a store every reference is dangling.
assert!(Object::Ref(ObjRef::new(4, 0)).resolve(&NoResolve).is_err());Sourcepub fn clone_direct(&self, r: &impl Resolve) -> Self
pub fn clone_direct(&self, r: &impl Resolve) -> Self
Deep-copy the object with every reference replaced by what it points at, dropping the edges that would close a cycle. Only a reference back to an ancestor is a cycle; siblings may share substructure and both copies survive. A cut edge disappears — the key or element is omitted rather than becoming null — and an unresolvable reference disappears the same way, indistinguishably.
A reference to a stream flattens into the stream itself, stored
directly in the dictionary or array that held it, with its raw,
still-encoded bytes and its /Filter intact: ISO 32000-1 §7.3.8.1
constrains a file, not these in-memory types.
let store = Store(HashMap::from([(7, Arc::new(Object::Int(42)))]));
let array = Object::Array(Array::from_iter([Object::Ref(ObjRef::new(7, 0))]));
assert_eq!(
array.clone_direct(&store),
Object::Array(Array::from_iter([Object::Int(42)])),
);