///|
/// Construct an empty document object table.
///
/// The next allocated object number will be `1`; the mutation log starts empty.
pub fn pdf_objects_empty() -> PdfObjects {
{
max_object_number: 0,
object_map: pdf_object_map_empty(),
object_stream_ids: HashMap([]),
log: [],
}
}
///|
/// Build a document object table from explicit numbered entries.
///
/// Entries are installed without recording alteration events. The maximum
/// object number is derived from the supplied numbers.
pub fn pdf_objects_of_entries(
entries : ArrayView[(Int, PdfObjectEntry)],
) -> PdfObjects {
let objects = pdf_objects_empty()
for entry in entries {
objects.set_entry(entry.0, entry.1)
}
objects
}
///|
/// Return the number of object entries currently stored.
pub fn PdfObjects::length(self : PdfObjects) -> Int {
self.object_map.length()
}
///|
/// Return stored object numbers in ascending order.
pub fn PdfObjects::object_numbers(self : PdfObjects) -> Array[Int] {
self.object_map.object_numbers()
}
///|
/// Transform every parsed object in place.
///
/// Parsed and already-decrypted entries keep their state tags after the object
/// value is transformed. Deferred entries are left unchanged.
pub fn PdfObjects::map_objects_in_place(
self : PdfObjects,
transform : (@syntax.PdfObject) -> @syntax.PdfObject,
) -> Unit {
for number in self.object_numbers() {
let entry = self.get_entry(number).unwrap()
let data = match entry.data {
ObjectParsed(object) => ObjectParsed(transform(object))
ObjectParsedAlreadyDecrypted(object) =>
ObjectParsedAlreadyDecrypted(transform(object))
_ => entry.data
}
self.set_entry(number, { data, generation: entry.generation, })
}
}
///|
/// Return parsed object entries as `(number, generation, object)` triples.
///
/// Results are sorted by object number and exclude deferred entries.
pub fn PdfObjects::parsed_entries(
self : PdfObjects,
) -> Array[(Int, Int, @syntax.PdfObject)] {
let numbers = self.object_numbers()
let entries : Array[(Int, Int, @syntax.PdfObject)] = Array(
capacity=numbers.length(),
)
for number in numbers {
let entry = self.get_entry(number).unwrap()
match entry.data {
ObjectParsed(object) => entries.push((number, entry.generation, object))
ObjectParsedAlreadyDecrypted(object) =>
entries.push((number, entry.generation, object))
_ => ()
}
}
entries
}
///|
/// Return parsed stream objects as `(number, generation, stream)` triples.
///
/// Results are sorted by object number and exclude non-stream or deferred
/// entries.
pub fn PdfObjects::stream_entries(
self : PdfObjects,
) -> Array[(Int, Int, @syntax.PdfStream)] {
let numbers = self.object_numbers()
let entries : Array[(Int, Int, @syntax.PdfStream)] = Array(
capacity=numbers.length(),
)
for number in numbers {
let entry = self.get_entry(number).unwrap()
match entry.data {
ObjectParsed(PdfStreamObject(stream))
| ObjectParsedAlreadyDecrypted(PdfStreamObject(stream)) =>
entries.push((number, entry.generation, stream))
_ => ()
}
}
entries
}
///|
fn PdfObjects::copy(self : PdfObjects) -> PdfObjects {
let object_stream_ids : @hashmap.HashMap[Int, Int] = HashMap([])
for number in self.object_stream_ids.keys() {
object_stream_ids[number] = self.object_stream_ids.get(number).unwrap()
}
{
max_object_number: self.max_object_number,
object_map: self.object_map.copy(),
object_stream_ids,
log: self.log.copy(),
}
}
///|
/// Return the highest object number allocated or loaded so far.
pub fn PdfObjects::max_object_number(self : PdfObjects) -> Int {
self.max_object_number
}
///|
/// Return a copy of the object mutation log.
pub fn PdfObjects::log(self : PdfObjects) -> Array[(Int, PdfObjectEvent)] {
self.log.copy()
}
///|
/// Return the object-table entry for `number`, if present.
pub fn PdfObjects::get_entry(
self : PdfObjects,
number : Int,
) -> PdfObjectEntry? {
self.object_map.get_entry(number)
}
///|
/// Set or replace an object-table entry without logging an alteration.
///
/// The tracked maximum object number is raised when `number` exceeds the
/// previous maximum.
pub fn PdfObjects::set_entry(
self : PdfObjects,
number : Int,
entry : PdfObjectEntry,
) -> Unit {
if number > self.max_object_number {
self.max_object_number = number
}
self.object_map.set_entry(number, entry)
}
///|
/// Add or replace a parsed object at an explicit object number.
///
/// This updates the maximum object number, stores an `ObjectParsed` entry, and
/// appends `ObjectAltered` to the mutation log.
pub fn PdfObjects::add_object_given_number(
self : PdfObjects,
number : Int,
object : @syntax.PdfObject,
generation? : Int = 0,
) -> Unit {
self.set_entry(number, pdf_object_entry(ObjectParsed(object), generation~))
self.log.push((number, ObjectAltered))
}
///|
fn PdfObjects::load_object_given_number(
self : PdfObjects,
number : Int,
object : @syntax.PdfObject,
generation : Int,
) -> Unit {
self.set_entry(number, pdf_object_entry(ObjectParsed(object), generation~))
}
///|
fn PdfObjects::load_object_given_number_already_decrypted(
self : PdfObjects,
number : Int,
object : @syntax.PdfObject,
) -> Unit {
self.set_entry(number, pdf_object_entry(ObjectParsedAlreadyDecrypted(object)))
}
///|
/// Add a parsed object using the next available object number.
///
/// Returns the allocated object number and records an `ObjectAltered` event.
pub fn PdfObjects::add_object(
self : PdfObjects,
object : @syntax.PdfObject,
generation? : Int = 0,
) -> Int {
let number = self.max_object_number + 1
self.add_object_given_number(number, object, generation~)
number
}
///|
/// Remove an object entry and record a deletion event.
///
/// The deletion is logged even if the object number was not present.
pub fn PdfObjects::remove_object(self : PdfObjects, number : Int) -> Unit {
self.object_map.remove(number)
self.log.push((number, ObjectDeleted))
}
///|
/// Return a parsed object by number, or `PdfNull` if it is absent or deferred.
pub fn PdfObjects::lookup_object_or_null(
self : PdfObjects,
number : Int,
) -> @syntax.PdfObject {
self.object_map.lookup_parsed_or_null(number)
}