///|
fn clear_object_stream_ids(ids : Map[Int, Int]) -> Unit {
ids.clear()
}
///|
fn split_into_groups(items : Array[Int], group_size : Int) -> Array[Array[Int]] {
let groups = Array::new()
let mut index = 0
while index < items.length() {
let group = Array::new()
let mut i = 0
while i < group_size && index < items.length() {
group.push(items[index])
index = index + 1
i = i + 1
}
groups.push(group)
}
groups
}
///|
fn generate_object_stream_hints(
pdf : @pdf.Pdf,
preserve_existing : Bool,
encrypting : Bool,
) -> Unit {
if !preserve_existing {
clear_object_stream_ids(pdf.objects.object_stream_ids)
}
let indirect_lengths : Map[Int, Bool] = Map::new(
capacity=pdf.objects.objects.length(),
)
let entries = @pdf.pdfobjmap_bindings(pdf.objects.objects)
for pair in entries {
let objnum = pair.0
let obj = pdf.lookup_obj(objnum)
match obj {
PdfObject::Stream(r) => {
let (dict, _) = r.val
match pdf.lookup_direct("/Length", dict) {
Some(PdfObject::Indirect(i)) => indirect_lengths.set(i, true)
_ => ()
}
}
_ => ()
}
}
let candidates = Array::new()
for pair in entries {
let objnum = pair.0
if pdf.objects.object_stream_ids.get(objnum) is Some(_) {
continue
}
if indirect_lengths.get(objnum) is Some(_) {
continue
}
if encrypting && objnum == pdf.root {
continue
}
let obj = pdf.lookup_obj(objnum)
match obj {
PdfObject::Stream(_) => ()
_ => candidates.push(objnum)
}
}
candidates.sort_by((a, b) => a - b)
let groups = split_into_groups(candidates, 250)
let mut stream_id = pdf.objects.max_obj_num + 1
for group in groups {
let this_id = stream_id
stream_id = stream_id + 1
for objnum in group {
pdf.objects.object_stream_ids.set(objnum, this_id)
}
}
}
///|
fn build_object_stream_groups(pdf : @pdf.Pdf) -> Array[(Int, Array[Int])] {
let groups : Map[Int, Array[Int]] = Map::new(
capacity=pdf.objects.object_stream_ids.length(),
)
for pair in pdf.objects.object_stream_ids.to_array() {
let objnum = pair.0
let stream_id = pair.1
if pdf.objects.objects.get(objnum) is None {
pdf.objects.object_stream_ids.remove(objnum)
continue
}
let obj = pdf.lookup_obj(objnum)
match obj {
PdfObject::Stream(_) => {
pdf.objects.object_stream_ids.remove(objnum)
continue
}
_ => ()
}
let list = match groups.get(stream_id) {
Some(existing) => existing
None => {
let created = Array::new()
groups.set(stream_id, created)
created
}
}
list.push(objnum)
}
let groups_array = groups.to_array()
groups_array.sort_by((a, b) => a.0 - b.0)
for pair in groups_array {
pair.1.sort_by((a, b) => a - b)
}
groups_array
}
///|
fn bake_object_streams(
pdf : @pdf.Pdf,
compress : Bool,
groups : Array[(Int, Array[Int])],
) -> Map[Int, (Int, Int)] raise {
let entries : Map[Int, (Int, Int)] = Map::new(
capacity=pdf.objects.object_stream_ids.length(),
)
for group in groups {
let stream_id = group.0
let objs = group.1
if objs.length() == 0 {
continue
}
let object_strings = Array::new()
let offsets = Array::new()
let mut offset = 0
let mut index = 0
for objnum in objs {
let obj = pdf.lookup_obj(objnum)
let s = PdfWrite::new().string_of_pdf(obj) + " "
object_strings.push(s)
offsets.push(offset)
entries.set(objnum, (stream_id, index))
pdf.objects.object_stream_ids.set(objnum, stream_id)
offset = offset + s.length()
index = index + 1
}
for objnum in objs {
pdf.removeobj(objnum)
}
let header = StringBuilder::new()
for i in 0..