///|
fn pdf_toc_push_font_text_runs(
output : Array[PdfTypeElement],
runs : ArrayView[(Int, Int, PdfFont)],
font_size : Double,
) -> Unit raise @core.PdfError {
for run in pdf_fontpack_collate_runs(runs) {
if run.length() > 0 {
let (_, font_index, font) = run[0]
let text = Array::make(run.length(), b'\x00')
for index, item in run {
let (charcode, _, _) = item
guard charcode >= 0 && charcode < 256 else { raise BadText }
text[index] = charcode.to_byte()
}
output.push(
PdfTypeFont(pdf_toc_type_font_id(font_index), font, font_size),
)
output.push(PdfTypeText(Bytes::from_array(text)))
}
}
}
///|
fn pdf_toc_width_of_font_text(
font : PdfFont,
font_size : Double,
text : BytesView,
) -> Double raise @core.PdfError {
pdf_type_width_of_bytes(pdf_type_font_widths(font, font_size), text)
}
///|
fn pdf_toc_dot_run(
fontpack : PdfFontPack,
font_size : Double,
) -> Array[PdfTypeElement] raise @core.PdfError {
pdf_toc_of_utf8(fontpack, font_size, pdf_toc_ascii("."))
}
///|
fn pdf_toc_ellipsis_run(
fontpack : PdfFontPack,
font_size : Double,
) -> Array[PdfTypeElement] raise @core.PdfError {
match fontpack.get_char(46) {
Some((charcode, font_index, font)) => {
guard charcode >= 0 && charcode < 256 else { raise BadText }
let dot = charcode.to_byte()
[
PdfTypeFont(pdf_toc_type_font_id(font_index), font, font_size),
PdfTypeText(Bytes::from_array([dot, dot, dot])),
]
}
None => raise BadText
}
}
///|
fn pdf_toc_last_text_width(
elements : ArrayView[PdfTypeElement],
) -> (Int, PdfTypeElement, Double)? raise @core.PdfError {
if elements.length() < 2 {
None
} else {
match (elements[elements.length() - 2], elements[elements.length() - 1]) {
(PdfTypeFont(_, font, font_size) as font_element, PdfTypeText(text)) =>
Some(
(
elements.length() - 1,
font_element,
pdf_toc_width_of_font_text(font, font_size, text),
),
)
_ => None
}
}
}
///|
fn pdf_toc_trim_last_text_once(
elements : Array[PdfTypeElement],
text_index : Int,
) -> Bool {
match elements[text_index] {
PdfTypeText(text) =>
if text.length() == 0 {
false
} else {
elements[text_index] = PdfTypeText(text[:text.length() - 1].to_owned())
true
}
_ => false
}
}
///|
fn pdf_toc_append_elements(
output : Array[PdfTypeElement],
elements : ArrayView[PdfTypeElement],
) -> Unit {
for element in elements {
output.push(element)
}
}
///|
fn pdf_toc_append_optional_elements(
output : Array[PdfTypeElement],
enabled : Bool,
elements : ArrayView[PdfTypeElement],
) -> Unit {
if enabled {
pdf_toc_append_elements(output, elements)
}
}
///|
fn pdf_toc_destination_contents(
text : BytesView,
) -> String raise @core.PdfError {
@utf8.decode(pdf_utf8_of_pdfdocstring(text)) catch {
_ => raise BadText
}
}
///|
fn PdfDocument::pdf_toc_bookmark_label_runs(
self : PdfDocument,
fontpack : PdfFontPack,
font_size : Double,
labels : ArrayView[@label.PdfPageLabel],
bookmark : @bookmark.PdfBookmark,
) -> Array[PdfTypeElement] raise @core.PdfError {
if bookmark.target == DestNull {
[]
} else {
let page_number = self.page_number_of_target(bookmark.target)
pdf_toc_of_pdfdocencoding(
fontpack,
font_size,
pdf_toc_page_label_text(page_number, labels),
)
}
}
///|
fn PdfDocument::pdf_toc_bookmark_elements(
self : PdfDocument,
fontpack : PdfFontPack,
font_size : Double,
labels : ArrayView[@label.PdfPageLabel],
page_width : Double,
margin : Double,
dotleader : Bool,
process_struct_tree : Bool,
bookmark : @bookmark.PdfBookmark,
) -> Array[PdfTypeElement] raise @core.PdfError {
let indent = bookmark.level.to_double() * font_size * 2.0
let textruns = pdf_toc_of_pdfdocencoding(fontpack, font_size, bookmark.text)
let labelruns = self.pdf_toc_bookmark_label_runs(
fontpack, font_size, labels, bookmark,
)
let textgap = page_width -
margin * 2.0 -
indent -
pdf_toc_width_of_runs(labelruns)
let shortened = pdf_toc_shorten_text(
fontpack,
font_size,
textgap - font_size * 3.0,
textruns,
)
let space = textgap - pdf_toc_width_of_runs(shortened)
let output : Array[PdfTypeElement] = [
PdfTypeBeginDest(
bookmark.target,
Some(pdf_toc_destination_contents(bookmark.text)),
),
PdfTypeHGlue(indent),
]
pdf_toc_append_optional_elements(output, process_struct_tree, [
PdfTypeTag("Link", 0),
])
pdf_toc_append_elements(output, shortened)
pdf_toc_append_optional_elements(output, process_struct_tree, [PdfTypeEndTag])
if dotleader && labelruns.length() > 0 {
pdf_toc_append_elements(
output,
pdf_toc_make_dots(space, fontpack, font_size),
)
} else {
output.push(PdfTypeHGlue(space))
}
pdf_toc_append_optional_elements(output, process_struct_tree, [
PdfTypeTag("Link", 0),
])
pdf_toc_append_elements(output, labelruns)
pdf_toc_append_optional_elements(output, process_struct_tree, [PdfTypeEndTag])
output.push(PdfTypeEndDest)
output.push(PdfTypeNewLine)
output
}