///|
priv struct PdfReaderXRefStreamRefSet {
values : @hashmap.HashMap[Int, @number_set.PdfNumberSet]
}
///|
fn pdf_reader_xref_stream_ref_set(
refs : ArrayView[(Int, Int)],
) -> PdfReaderXRefStreamRefSet {
let values : @hashmap.HashMap[Int, @number_set.PdfNumberSet] = HashMap([])
for item in refs {
let (object_number, offset) = item
match values.get(object_number) {
Some(offsets) => offsets.add(offset)
None => {
let offsets = @number_set.pdf_number_set([])
offsets.add(offset)
values[object_number] = offsets
}
}
}
{ values, }
}
///|
fn PdfReaderXRefStreamRefSet::contains(
self : PdfReaderXRefStreamRefSet,
object_number : Int,
offset : Int,
) -> Bool {
match self.values.get(object_number) {
Some(offsets) => offsets.contains(offset)
None => false
}
}
///|
fn pdf_reader_stream_dictionary_without_xref_keys(
dictionary : PdfObject,
) -> PdfObject raise PdfError {
dictionary
.remove_dict_entry(pdf_reader_type_name())
.remove_dict_entry(pdf_reader_w_name())
.remove_dict_entry(pdf_reader_index_name())
.remove_dict_entry(pdf_length_name())
.remove_dict_entry(pdf_reader_filter_name())
.remove_dict_entry(pdf_reader_short_filter_name())
.remove_dict_entry(pdf_reader_decodeparms_name())
.remove_dict_entry(pdf_reader_short_decodeparms_name())
}
///|
fn pdf_reader_xref_stream_type_ok(dictionary : PdfObject) -> Bool {
match dictionary.lookup_immediate(pdf_reader_type_name()) {
Some(PdfNameObject(name)) => name == pdf_reader_xref_name()
Some(_) => false
None => dictionary.lookup_immediate(pdf_reader_w_name()) != None
}
}
///|
fn pdf_reader_object_is_xref_stream(object : PdfObject) -> Bool {
match object {
PdfStreamObject(stream) => pdf_reader_xref_stream_type_ok(stream.dictionary)
_ => false
}
}
///|
fn pdf_xref_stream_int_array(
dictionary : PdfObject,
key : PdfName,
) -> Array[Int]? raise PdfError {
match dictionary.lookup_immediate(key) {
Some(PdfArray(values)) => {
let output : Array[Int] = Array::new(capacity=values.length())
for value in values {
match value {
PdfInteger(number) => output.push(number)
_ => raise XRefEntryExpected
}
}
Some(output)
}
Some(_) => raise XRefEntryExpected
None => None
}
}
///|
fn pdf_xref_stream_widths(
dictionary : PdfObject,
) -> (Int, Int, Int) raise PdfError {
match pdf_xref_stream_int_array(dictionary, pdf_reader_w_name()) {
Some([w0, w1, w2]) if w0 >= 0 && w1 >= 0 && w2 >= 0 && w0 + w1 + w2 > 0 =>
(w0, w1, w2)
_ => raise XRefEntryExpected
}
}
///|
fn pdf_xref_stream_index_pairs(
dictionary : PdfObject,
) -> Array[(Int, Int)] raise PdfError {
match pdf_xref_stream_int_array(dictionary, pdf_reader_index_name()) {
Some(values) => {
if values.length() % 2 != 0 {
raise XRefEntryExpected
}
let pairs : Array[(Int, Int)] = Array::new(capacity=values.length() / 2)
for pair_index in 0..<(values.length() / 2) {
let i = pair_index * 2
let start = values[i]
let length = values[i + 1]
if start < 0 || length < 0 {
raise XRefEntryExpected
}
pairs.push((start, length))
}
pairs
}
None =>
match dictionary.lookup_immediate(pdf_reader_size_name()) {
Some(PdfInteger(size)) if size >= 0 => [(0, size)]
_ => raise XRefEntryExpected
}
}
}
///|
fn pdf_xref_stream_entry_capacity(
index_pairs : ArrayView[(Int, Int)],
) -> Int raise PdfError {
let mut capacity = 0
for pair in index_pairs {
let count = pair.1
let next = capacity + count
if next < capacity {
raise XRefEntryExpected
}
capacity = next
}
capacity
}
///|
fn pdf_xref_stream_read_field(
data : BytesView,
position : Int,
width : Int,
) -> (Int, Int) raise PdfError {
if width == 0 {
(0, position)
} else if position < 0 || position + width > data.length() {
raise XRefEntryExpected
} else {
let mut value = 0
for i in position..<(position + width) {
value = value * 256 + data[i].to_int()
}
(value, position + width)
}
}
///|
fn pdf_xref_stream_read_entry(
data : BytesView,
position : Int,
w0 : Int,
w1 : Int,
w2 : Int,
) -> (Int, Int, Int, Int) raise PdfError {
let (raw_kind, after_kind) = pdf_xref_stream_read_field(data, position, w0)
let (field2, after_field2) = pdf_xref_stream_read_field(data, after_kind, w1)
let (field3, after_field3) = pdf_xref_stream_read_field(
data, after_field2, w2,
)
let kind = if w0 == 0 { 1 } else { raw_kind }
(kind, field2, field3, after_field3)
}
///|
fn pdf_xref_stream_entries(
data : BytesView,
widths : (Int, Int, Int),
index_pairs : ArrayView[(Int, Int)],
) -> Array[PdfClassicXRefEntry] raise PdfError {
let (w0, w1, w2) = widths
let entries : Array[PdfClassicXRefEntry] = Array::new(
capacity=pdf_xref_stream_entry_capacity(index_pairs),
)
let mut position = 0
for pair in index_pairs {
let (start_object, count) = pair
for offset in 0..
entries.push({
object_number,
offset: field2,
generation: field3,
in_use: false,
object_stream: None,
})
1 =>
entries.push({
object_number,
offset: field2,
generation: field3,
in_use: true,
object_stream: None,
})
// Object-stream entries hide older xrefs and are loaded after their
// containing object stream has been parsed as a plain entry.
2 =>
entries.push({
object_number,
offset: field2,
generation: field3,
in_use: true,
object_stream: Some({ object_stream_number: field2, index: field3 }),
})
// Unknown entry types are retained as non-loadable markers so a newer
// xref stream can still hide older or physically reconstructed objects.
_ =>
entries.push({
object_number,
offset: field2,
generation: field3,
in_use: false,
object_stream: None,
})
}
}
}
entries
}
///|
fn ByteCursor::read_pdf_xref_stream_section(
self : ByteCursor,
) -> PdfXRefSection raise PdfError {
let stream_object_offset = self.absolute_position()
if !pdf_is_digit_byte(self.peek_byte()) {
raise XRefExpected
}
let parsed = self.read_first_indirect_object_at_current([])
guard parsed.object is PdfStreamObject(stream) else { raise XRefExpected }
if !pdf_reader_xref_stream_type_ok(stream.dictionary) {
raise XRefExpected
}
let decoded_stream = pdf_decode_stream_value(stream)
let widths = pdf_xref_stream_widths(decoded_stream.dictionary)
let index_pairs = pdf_xref_stream_index_pairs(decoded_stream.dictionary)
let data = pdf_stream_data_bytes(decoded_stream.data)
{
entries: pdf_xref_stream_entries(data, widths, index_pairs),
trailer: pdf_reader_stream_dictionary_without_xref_keys(
decoded_stream.dictionary,
),
stream_object_number: Some(parsed.number),
stream_object_offset: Some(stream_object_offset),
}
}