///|
fn pdf_writer_push_big_endian_field(
output : Array[Byte],
value : Int,
width : Int,
) -> Unit {
let mut divisor = 1
if width > 1 {
for _ in 1.. 0 {
pdf_writer_push_byte(output, value / remaining_divisor % 256)
remaining_divisor /= 256
}
}
///|
fn pdf_writer_push_xref_stream_entry(
output : Array[Byte],
kind : Int,
field2 : Int,
field3 : Int,
) -> Unit {
pdf_writer_push_big_endian_field(output, kind, 1)
pdf_writer_push_big_endian_field(output, field2, 4)
pdf_writer_push_big_endian_field(output, field3, 2)
}
///|
fn pdf_writer_xref_stream_data(
xrefs : ArrayView[(Int, Int, Int)],
size : Int,
xref_stream_number : Int,
xref_stream_offset : Int,
) -> @core.PdfBytes {
let output : Array[Byte] = Array(capacity=size * 7)
let mut xref_index = 0
for number in 0.. Array[Int] {
let numbers = pdf_writer_changed_object_numbers(document)
if !pdf_writer_int_array_contains(numbers, xref_stream_number) {
numbers.push(xref_stream_number)
}
numbers.sort()
numbers
}
///|
fn pdf_writer_xref_stream_data_for_numbers(
xrefs : ArrayView[(Int, Int, Int)],
entry_numbers : ArrayView[Int],
xref_stream_number : Int,
xref_stream_offset : Int,
) -> @core.PdfBytes {
let output : Array[Byte] = Array(capacity=entry_numbers.length() * 7)
let mut xref_index = 0
for number in entry_numbers {
if number == 0 {
pdf_writer_push_xref_stream_entry(output, 0, 0, 65535)
} else if number == xref_stream_number {
pdf_writer_push_xref_stream_entry(output, 1, xref_stream_offset, 0)
} else {
while xref_index < xrefs.length() && xrefs[xref_index].0 < number {
xref_index += 1
}
if xref_index < xrefs.length() && xrefs[xref_index].0 == number {
let (_, offset, generation) = xrefs[xref_index]
pdf_writer_push_xref_stream_entry(output, 1, offset, generation)
} else {
pdf_writer_push_xref_stream_entry(output, 0, 0, 65535)
}
}
}
Bytes::from_array(output)
}
///|
fn pdf_writer_xref_stream_index_pairs(
entry_numbers : ArrayView[Int],
) -> Array[(Int, Int)] {
let pairs : Array[(Int, Int)] = Array(capacity=entry_numbers.length())
let mut run_start = -1
let mut run_length = 0
for number in entry_numbers {
if run_length == 0 {
run_start = number
run_length = 1
} else if number == run_start + run_length {
run_length += 1
} else {
pairs.push((run_start, run_length))
run_start = number
run_length = 1
}
}
if run_length > 0 {
pairs.push((run_start, run_length))
}
pairs
}
///|
fn pdf_writer_xref_stream_index_object(
pairs : ArrayView[(Int, Int)],
) -> @syntax.PdfObject {
let values : Array[@syntax.PdfObject] = Array(capacity=pairs.length() * 2)
for pair in pairs {
let (start, length) = pair
values.push(PdfInteger(start))
values.push(PdfInteger(length))
}
PdfArray(values)
}
///|
fn pdf_writer_xref_stream_trailer(
document : PdfDocument,
size : Int,
previous_xref : Int?,
) -> @syntax.PdfObject raise @core.PdfError {
let trailer = pdf_writer_trailer_dictionary(document)
.add_dict_entry(
pdf_writer_type_name(),
PdfNameObject(pdf_writer_xref_name()),
)
.add_dict_entry(pdf_writer_size_name(), PdfInteger(size))
.add_dict_entry(
pdf_writer_w_name(),
PdfArray([PdfInteger(1), PdfInteger(4), PdfInteger(2)]),
)
match previous_xref {
Some(previous) =>
trailer.add_dict_entry(pdf_writer_prev_name(), PdfInteger(previous))
None => trailer
}
}
///|
fn pdf_write_document_with_xref_stream_filter(
document : PdfDocument,
filter : @core.PdfName?,
) -> @core.PdfBytes raise @core.PdfError {
let output : Array[Byte] = []
let (major, minor) = document.version()
if major > 1 || (major == 1 && minor >= 5) {
pdf_writer_push_header(output, major, minor)
} else {
pdf_writer_push_header(output, 1, 5)
}
let xrefs = pdf_writer_write_document_objects(output, document)
let xref_stream_offset = output.length()
let xref_stream_number = document.objects.max_object_number() + 1
let size = xref_stream_number + 1
let xref_data = pdf_writer_xref_stream_data(
xrefs, size, xref_stream_number, xref_stream_offset,
)
let stream = @syntax.pdf_stream(
pdf_writer_xref_stream_trailer(document, size, None).add_dict_entry(
pdf_writer_length_name(),
PdfInteger(xref_data.length()),
),
StreamGot(xref_data),
)
let xref_object = match filter {
Some(filter) => pdf_encode_stream_with_filter(filter, stream)
None => stream
}
pdf_writer_push_view(
output,
pdf_write_indirect_object({
number: xref_stream_number,
generation: 0,
object: xref_object,
}),
)
pdf_writer_push_ascii(output, "startxref\n")
pdf_writer_push_ascii(output, xref_stream_offset.to_string())
pdf_writer_push_ascii(output, "\n%%EOF\n")
Bytes::from_array(output)
}
///|
/// Serialize a complete document with an uncompressed xref stream.
///
/// The output header is at least PDF 1.5, because xref streams require that
/// version.
pub fn pdf_write_document_with_xref_stream(
document : PdfDocument,
) -> @core.PdfBytes raise @core.PdfError {
pdf_write_document_with_xref_stream_filter(document, None)
}
///|
fn pdf_write_document_incremental_update_with_xref_stream_filter(
original : @core.PdfBytes,
document : PdfDocument,
filter : @core.PdfName?,
) -> @core.PdfBytes raise @core.PdfError {
let previous_xref = @reader.pdf_read_startxref_position(original)
let output : Array[Byte] = Array(capacity=original.length() + 1)
for byte in original {
output.push(byte)
}
pdf_writer_push_byte(output, 10)
let xref_stream_number = document.objects.max_object_number() + 1
let entry_numbers = pdf_writer_xref_stream_entry_numbers(
document, xref_stream_number,
)
let xrefs = pdf_writer_write_document_objects_for_numbers(
output, document, entry_numbers,
)
let xref_stream_offset = output.length()
let size = xref_stream_number + 1
let xref_data = pdf_writer_xref_stream_data_for_numbers(
xrefs, entry_numbers, xref_stream_number, xref_stream_offset,
)
let index_pairs = pdf_writer_xref_stream_index_pairs(entry_numbers)
let stream = @syntax.pdf_stream(
pdf_writer_xref_stream_trailer(document, size, Some(previous_xref))
.add_dict_entry(
pdf_writer_index_name(),
pdf_writer_xref_stream_index_object(index_pairs),
)
.add_dict_entry(pdf_writer_length_name(), PdfInteger(xref_data.length())),
StreamGot(xref_data),
)
let xref_object = match filter {
Some(filter) => pdf_encode_stream_with_filter(filter, stream)
None => stream
}
pdf_writer_push_view(
output,
pdf_write_indirect_object({
number: xref_stream_number,
generation: 0,
object: xref_object,
}),
)
pdf_writer_push_ascii(output, "startxref\n")
pdf_writer_push_ascii(output, xref_stream_offset.to_string())
pdf_writer_push_ascii(output, "\n%%EOF\n")
Bytes::from_array(output)
}
///|
/// Append an incremental update using an uncompressed xref stream.
pub fn pdf_write_document_incremental_update_with_xref_stream(
original : @core.PdfBytes,
document : PdfDocument,
) -> @core.PdfBytes raise @core.PdfError {
pdf_write_document_incremental_update_with_xref_stream_filter(
original,
document,
None,
)
}
///|
/// Serialize a complete document with a Flate-compressed xref stream.
pub fn pdf_write_document_with_compressed_xref_stream(
document : PdfDocument,
) -> @core.PdfBytes raise @core.PdfError {
pdf_write_document_with_xref_stream_filter(
document,
Some(pdf_writer_flate_decode_name()),
)
}
///|
/// Append an incremental update using a Flate-compressed xref stream.
pub fn pdf_write_document_incremental_update_with_compressed_xref_stream(
original : @core.PdfBytes,
document : PdfDocument,
) -> @core.PdfBytes raise @core.PdfError {
pdf_write_document_incremental_update_with_xref_stream_filter(
original,
document,
Some(pdf_writer_flate_decode_name()),
)
}