///|
fn pdf_toc_title_elements(
fontpack : PdfFontPack,
font_size : Double,
title : BytesView,
process_struct_tree : Bool,
) -> Array[PdfTypeElement] raise @core.PdfError {
let output : Array[PdfTypeElement] = []
pdf_toc_append_optional_elements(output, process_struct_tree, [
PdfTypeTag("P", 0),
])
for line in pdf_toc_split_title(title) {
pdf_toc_append_elements(
output,
pdf_toc_of_utf8(fontpack, font_size * 2.0, line),
)
output.push(PdfTypeNewLine)
}
pdf_toc_append_optional_elements(output, process_struct_tree, [PdfTypeEndTag])
output.push(PdfTypeVGlue(font_size * 2.0))
output
}
///|
fn pdf_toc_first_font(
elements : ArrayView[PdfTypeElement],
) -> PdfTypeElement raise @core.PdfError {
for element in elements {
match element {
PdfTypeFont(_, _, _) => return element
_ => ()
}
}
raise SoftError("toc_type_elements: no font")
}
///|
fn pdf_toc_rectangle_object(
rectangle : @geometry.PdfRectangle,
) -> @syntax.PdfObject {
PdfArray([
PdfReal(rectangle.min_x),
PdfReal(rectangle.min_y),
PdfReal(rectangle.max_x),
PdfReal(rectangle.max_y),
])
}
///|
fn pdf_toc_cropbox_page(
page : @page.PdfPage,
cropbox : @syntax.PdfObject?,
) -> @page.PdfPage raise @core.PdfError {
match cropbox {
Some(cropbox) =>
{
..page,
rest: page.rest.add_dict_entry(pdf_page_cropbox_key(), cropbox),
}
None => page
}
}
///|
fn pdf_toc_cropbox_pages(
pages : ArrayView[@page.PdfPage],
cropbox : @syntax.PdfObject?,
) -> Array[@page.PdfPage] raise @core.PdfError {
let output : Array[@page.PdfPage] = []
for page in pages {
output.push(pdf_toc_cropbox_page(page, cropbox))
}
output
}
///|
/// Split a TOC title on the literal byte sequence `\n`.
///
/// This mirrors `cpdftoc.ml`'s `split_toc_title`, which splits before UTF-8
/// conversion and preserves leading, trailing, and adjacent empty title lines.
pub fn pdf_toc_split_title(title : BytesView) -> Array[@core.PdfBytes] {
let lines : Array[@core.PdfBytes] = []
let mut line_start = 0
let mut index = 0
while index < title.length() {
if title[index] == b'\\' &&
index + 1 < title.length() &&
title[index + 1] == b'n' {
lines.push(@core.pdf_bytes_of_view(title[line_start:index]))
index += 2
line_start = index
} else {
index += 1
}
}
lines.push(@core.pdf_bytes_of_view(title[line_start:]))
lines
}
///|
/// Replace literal `\n` byte pairs in a TOC title with real line-feed bytes.
pub fn pdf_toc_title_real_newlines(title : BytesView) -> @core.PdfBytes {
let mut length = 0
let mut index = 0
while index < title.length() {
length += 1
if title[index] == b'\\' &&
index + 1 < title.length() &&
title[index + 1] == b'n' {
index += 2
} else {
index += 1
}
}
let output = Array::make(length, b'\x00')
let mut position = 0
index = 0
while index < title.length() {
if title[index] == b'\\' &&
index + 1 < title.length() &&
title[index + 1] == b'n' {
output[position] = b'\n'
position += 1
index += 2
} else {
output[position] = title[index]
position += 1
index += 1
}
}
Bytes::from_array(output)
}