///|
/// Recursively rebuild an array.
pub fn recurse_array(
f : (PdfObject) -> PdfObject,
elts : Array[PdfObject],
) -> PdfObject {
Array(elts.map(f))
}
///|
/// Recursively rebuild a dictionary.
fn recurse_dict(
f : (PdfObject) -> PdfObject,
elts : Array[(String, PdfObject)],
preserve_order? : Bool = false,
) -> PdfObject {
let _ = preserve_order
let out = Array::new(capacity=elts.length())
for entry in elts {
out.push((entry.0, f(entry.1)))
}
Dictionary(out)
}
///|
/// Iterate over all objects in a document.
pub fn Pdf::objiter(
self : Pdf,
f : (Int, PdfObject) -> Unit raise?,
) -> Unit raise? {
for key in self.objnumbers() {
f(key, self.lookup_obj(key))
}
}
///|
/// Iterate over all objects in a document, ordered by object number.
pub fn Pdf::objiter_inorder(
self : Pdf,
f : (Int, PdfObject) -> Unit raise?,
) -> Unit raise? {
self.objiter(f)
}
///|
/// Iterate over all objects in a document with generation numbers.
pub fn Pdf::objiter_gen(
self : Pdf,
f : (Int, Int, PdfObject) -> Unit raise?,
) -> Unit raise? {
let entries = self.objects.objects.to_array()
entries.sort_by((a, b) => a.0 - b.0)
for entry in entries {
let key = entry.0
let (_obj_ref, gen) = entry.1
f(key, gen, self.lookup_obj(key))
}
}
///|
/// Map a function over all objects in a document.
pub fn Pdf::objselfmap(
self : Pdf,
f : (PdfObject) -> PdfObject raise?,
) -> Unit raise? {
let entries = self.objects.objects.to_array()
entries.sort_by((a, b) => a.0 - b.0)
for entry in entries {
let key = entry.0
let obj = self.lookup_obj(key)
let updated = f(obj)
let (obj_ref, _gen) = entry.1
match obj_ref.val {
ParsedAlreadyDecrypted(_) => obj_ref.val = ParsedAlreadyDecrypted(updated)
_ => obj_ref.val = Parsed(updated)
}
}
}
///|
/// Select objects matching a predicate and return their numbers.
pub fn Pdf::objselect(
self : Pdf,
f : (PdfObject) -> Bool raise?,
) -> Array[Int] raise? {
// Upper bound: all objects could match.
let out = Array::new(capacity=self.objects.objects.length())
self.objiter((num, obj) => if f(obj) { out.push(num) })
out
}
///|
fn Pdf::nullify_deleted_page_references(self : Pdf) -> Unit raise {
fn nullify(numbers : Map[Int, Bool], obj : PdfObject) -> PdfObject {
match obj {
Indirect(i) => if numbers.contains(i) { Null } else { obj }
Array(elts) => recurse_array(v => nullify(numbers, v), elts)
Dictionary(elts) => recurse_dict(v => nullify(numbers, v), elts)
Stream(r) => {
let (dict, stream) = r.val
r.val = (nullify(numbers, dict), stream)
Stream(r)
}
_ => obj
}
}
// Upper bound: all objects could be /Page dictionaries.
let page_object_numbers = Array::new(capacity=self.objects.objects.length())
self.objiter((objnum, obj) => match obj {
Dictionary(_) =>
match self.lookup_direct("/Type", obj) {
Some(Name(name)) if name.equal_string_bytes("/Page") =>
page_object_numbers.push(objnum)
_ => ()
}
_ => ()
})
let refnums = self.page_reference_numbers()
if refnums.length() != page_object_numbers.length() {
let table = Map::new(capacity=page_object_numbers.length())
for num in page_object_numbers {
table.set(num, true)
}
for num in refnums {
table.remove(num)
}
self.objselfmap(obj => nullify(table, obj))
}
}
///|
/// Renumber indirect references using a change table.
pub fn Pdf::renumber_object_parsed(
self : Pdf,
changes : Map[Int, Int],
obj : PdfObject,
preserve_order? : Bool = false,
) -> PdfObject {
match obj {
Indirect(i) =>
match changes.get(i) {
Some(x) => Indirect(x)
None => Indirect(i)
}
Array(values) =>
Array(
values.map(v => self.renumber_object_parsed(changes, v, preserve_order~)),
)
Dictionary(entries) =>
recurse_dict(
v => self.renumber_object_parsed(changes, v, preserve_order~),
entries,
preserve_order~,
)
Stream(r) => {
let (dict, stream) = r.val
let dict_updated = self.renumber_object_parsed(
changes,
dict,
preserve_order~,
)
r.val = (dict_updated, stream)
Stream(r)
}
_ => obj
}
}