///|
fn pdf_toc_ascii(text : String) -> PdfBytes {
@ascii.encode(text)
}
///|
fn pdf_toc_type_font_id(font_index : Int) -> String {
font_index.to_string()
}
///|
/// Font source options for table-of-contents generation.
///
/// This ports the non-embedding cpdftoc font-source dispatch. TrueType
/// `EmbedInfo` remains a separate embedding slice.
pub(all) enum PdfTocFontSource {
PdfTocPreMadeFontPack(PdfFontPack)
PdfTocExistingNamedFont
} derive(Debug, Eq, ToJson)
///|
fn pdf_toc_push_font_text_runs(
output : Array[PdfTypeElement],
runs : ArrayView[(Int, Int, PdfFont)],
font_size : Double,
) -> Unit raise PdfError {
for run in pdf_fontpack_collate_runs(runs) {
if run.length() > 0 {
let (_, font_index, font) = run[0]
let text : Array[Byte] = []
for item in run {
let (charcode, _, _) = item
guard charcode >= 0 && charcode < 256 else { raise BadText }
text.push(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 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 PdfError {
pdf_toc_of_utf8(fontpack, font_size, pdf_toc_ascii("."))
}
///|
fn pdf_toc_ellipsis_run(
fontpack : PdfFontPack,
font_size : Double,
) -> Array[PdfTypeElement] raise 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 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 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[PdfPageLabel],
bookmark : PdfBookmark,
) -> Array[PdfTypeElement] raise 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[PdfPageLabel],
page_width : Double,
margin : Double,
dotleader : Bool,
process_struct_tree : Bool,
bookmark : PdfBookmark,
) -> Array[PdfTypeElement] raise 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
}
///|
fn pdf_toc_title_elements(
fontpack : PdfFontPack,
font_size : Double,
title : BytesView,
process_struct_tree : Bool,
) -> Array[PdfTypeElement] raise 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 PdfError {
for element in elements {
match element {
PdfTypeFont(_, _, _) => return element
_ => ()
}
}
raise SoftError("toc_type_elements: no font")
}
///|
fn pdf_toc_rectangle_object(rectangle : PdfRectangle) -> PdfObject {
PdfArray([
PdfReal(rectangle.min_x),
PdfReal(rectangle.min_y),
PdfReal(rectangle.max_x),
PdfReal(rectangle.max_y),
])
}
///|
fn pdf_toc_cropbox_page(
page : PdfPage,
cropbox : PdfObject?,
) -> PdfPage raise 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[PdfPage],
cropbox : PdfObject?,
) -> Array[PdfPage] raise PdfError {
let output : Array[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[PdfBytes] {
let lines : Array[PdfBytes] = []
let mut current : Array[Byte] = []
let mut index = 0
while index < title.length() {
if title[index] == b'\\' &&
index + 1 < title.length() &&
title[index + 1] == b'n' {
lines.push(Bytes::from_array(current))
current = []
index += 2
} else {
current.push(title[index])
index += 1
}
}
lines.push(Bytes::from_array(current))
lines
}
///|
/// Replace literal `\n` byte pairs in a TOC title with real line-feed bytes.
pub fn pdf_toc_title_real_newlines(title : BytesView) -> PdfBytes {
let output : Array[Byte] = []
let mut index = 0
while index < title.length() {
if title[index] == b'\\' &&
index + 1 < title.length() &&
title[index + 1] == b'n' {
output.push(b'\n')
index += 2
} else {
output.push(title[index])
index += 1
}
}
Bytes::from_array(output)
}
///|
/// Convert UTF-8 text into the font/text runs used by cpdftoc.
///
/// Unrepresentable codepoints are skipped, matching cpdf's `option_map
/// (Cpdfembed.get_char fontpack)` behavior. Unlike the text-to-PDF paragraph
/// helper, this function treats input as a single run and does not interpret
/// newline characters.
pub fn pdf_toc_of_utf8(
fontpack : PdfFontPack,
font_size : Double,
text : BytesView,
) -> Array[PdfTypeElement] raise PdfError {
let mapped : Array[(Int, Int, PdfFont)] = []
for codepoint in pdf_codepoints_of_utf8(text) {
match fontpack.get_char(codepoint) {
Some(run) => mapped.push(run)
None => ()
}
}
let output : Array[PdfTypeElement] = []
pdf_toc_push_font_text_runs(output, mapped, font_size)
output
}
///|
fn pdf_toc_codepoints_contains(
codepoints : ArrayView[Int],
codepoint : Int,
) -> Bool {
for current in codepoints {
if current == codepoint {
return true
}
}
false
}
///|
fn pdf_toc_append_unique_codepoints(
output : Array[Int],
codepoints : ArrayView[Int],
) -> Unit {
for codepoint in codepoints {
if !pdf_toc_codepoints_contains(output, codepoint) {
output.push(codepoint)
}
}
}
///|
fn pdf_toc_append_utf8_codepoints(
output : Array[Int],
text : BytesView,
) -> Unit raise PdfError {
pdf_toc_append_unique_codepoints(output, pdf_codepoints_of_utf8(text))
}
///|
fn pdf_toc_append_pdfdocstring_codepoints(
output : Array[Int],
text : BytesView,
) -> Unit raise PdfError {
pdf_toc_append_utf8_codepoints(output, pdf_utf8_of_pdfdocstring(text))
}
///|
fn pdf_toc_page_label_text(
page_number : Int,
labels : ArrayView[PdfPageLabel],
) -> PdfBytes {
if page_number < 1 {
pdf_toc_ascii(page_number.to_string())
} else {
match pdf_page_label_text_of_page_number(page_number, labels) {
Some(label) => label
None => pdf_toc_ascii(page_number.to_string())
}
}
}
///|
/// Return the unique Unicode codepoints cpdftoc needs before embedding a font.
///
/// The scan includes the dot used by ellipses/leaders, the TOC title, bookmark
/// titles, and the visible page-label text for each bookmark target. The
/// returned order is first occurrence for deterministic callers and tests.
pub fn PdfDocument::toc_used_codepoints(
self : PdfDocument,
labels : ArrayView[PdfPageLabel],
title : BytesView,
bookmarks : ArrayView[PdfBookmark],
) -> Array[Int] raise PdfError {
let used : Array[Int] = [46]
pdf_toc_append_utf8_codepoints(used, title)
for bookmark in bookmarks {
pdf_toc_append_pdfdocstring_codepoints(used, bookmark.text)
let page_number = self.page_number_of_target(bookmark.target)
pdf_toc_append_pdfdocstring_codepoints(
used,
pdf_toc_page_label_text(page_number, labels),
)
}
used
}
///|
/// Compatibility wrapper for `PdfDocument::toc_used_codepoints`.
pub fn pdf_toc_used_codepoints(
document : PdfDocument,
labels : ArrayView[PdfPageLabel],
title : BytesView,
bookmarks : ArrayView[PdfBookmark],
) -> Array[Int] raise PdfError {
document.toc_used_codepoints(labels, title, bookmarks)
}
///|
/// Convert PDFDocEncoding/PDFDocString bytes into the font/text runs used by
/// cpdftoc.
pub fn pdf_toc_of_pdfdocencoding(
fontpack : PdfFontPack,
font_size : Double,
text : BytesView,
) -> Array[PdfTypeElement] raise PdfError {
pdf_toc_of_utf8(fontpack, font_size, pdf_utf8_of_pdfdocstring(text))
}
///|
/// Return the total width of cpdftoc's alternating `Font`/`Text` run list.
pub fn pdf_toc_width_of_runs(
runs : ArrayView[PdfTypeElement],
) -> Double raise PdfError {
let mut width = 0.0
let mut index = 0
while index < runs.length() {
guard index + 1 < runs.length() else { raise SoftError("width_of_runs") }
match (runs[index], runs[index + 1]) {
(PdfTypeFont(_, font, font_size), PdfTypeText(text)) => {
width += pdf_toc_width_of_font_text(font, font_size, text)
index += 2
}
_ => raise SoftError("width_of_runs")
}
}
width
}
///|
/// Shorten the final text run to fit `max_width`, appending `...` if shortened.
///
/// This intentionally mirrors cpdf's narrow helper: only the final `Font`/`Text`
/// pair is trimmed and measured.
pub fn pdf_toc_shorten_text(
fontpack : PdfFontPack,
font_size : Double,
max_width : Double,
runs : ArrayView[PdfTypeElement],
) -> Array[PdfTypeElement] raise PdfError {
let output = runs.to_owned()
let mut shortened = false
let mut done = false
while !done {
match pdf_toc_last_text_width(output) {
Some((text_index, _, width)) if width > max_width =>
if pdf_toc_trim_last_text_once(output, text_index) {
shortened = true
} else {
done = true
}
_ => done = true
}
}
if shortened {
let ellipsis = pdf_toc_ellipsis_run(fontpack, font_size)
pdf_toc_append_elements(output, ellipsis)
}
output
}
///|
/// Build cpdftoc's dot-leader run for an available horizontal space.
pub fn pdf_toc_make_dots(
space : Double,
fontpack : PdfFontPack,
font_size : Double,
) -> Array[PdfTypeElement] raise PdfError {
let dot_runs = pdf_toc_dot_run(fontpack, font_size)
let dot_width = pdf_toc_width_of_runs(dot_runs)
let raw_count = (space / dot_width).floor().to_int()
let count = if raw_count < 0 { 0 } else { raw_count }
let output : Array[PdfTypeElement] = [
PdfTypeHGlue(space - dot_width * count.to_double()),
]
for _ in 0.. Array[PdfTypeElement] raise PdfError {
let title_elements = pdf_toc_title_elements(
fontpack, font_size, title, process_struct_tree,
)
let body : Array[PdfTypeElement] = []
pdf_toc_append_elements(body, title_elements)
for bookmark in bookmarks {
pdf_toc_append_elements(
body,
self.pdf_toc_bookmark_elements(
fontpack, font_size, labels, page_width, margin, dotleader, process_struct_tree,
bookmark,
),
)
}
let output : Array[PdfTypeElement] = [
pdf_toc_first_font(body),
PdfTypeBeginDocument,
]
pdf_toc_append_elements(output, body)
output
}
///|
/// Compatibility wrapper for `PdfDocument::toc_type_elements`.
pub fn pdf_toc_type_elements(
document : PdfDocument,
fontpack : PdfFontPack,
font_size : Double,
title : BytesView,
dotleader : Bool,
process_struct_tree : Bool,
labels : ArrayView[PdfPageLabel],
bookmarks : ArrayView[PdfBookmark],
page_width : Double,
margin : Double,
) -> Array[PdfTypeElement] raise PdfError {
document.toc_type_elements(
fontpack, font_size, title, dotleader, process_struct_tree, labels, bookmarks,
page_width, margin,
)
}
///|
/// Typeset a TOC element stream into pages using cpdftoc's first-page geometry.
///
/// The first page's `/MediaBox` defines the paper size and base margin, while an
/// inherited `/CropBox`, when present, narrows the content width, offsets the
/// margins, and is copied onto the generated TOC pages. This helper does not
/// insert the pages into the source document.
pub fn PdfDocument::toc_typeset_pages(
self : PdfDocument,
fontpack : PdfFontPack,
font_size : Double,
title : BytesView,
dotleader : Bool,
process_struct_tree : Bool,
labels : ArrayView[PdfPageLabel],
bookmarks : ArrayView[PdfBookmark],
) -> PdfTypeTypesetResult raise PdfError {
let source_pages = self.pages_of_pagetree()
guard source_pages.length() > 0 else {
raise SoftError("toc_typeset_pages: no pages")
}
let first_page = source_pages[0]
let media = self.parse_rectangle(first_page.mediabox)
let media_width = media.max_x - media.min_x
let media_height = media.max_y - media.min_y
let paper = paper_size_make(PdfPoint, media_width, media_height)
let margin = double_min(media_width, media_height) * 0.1
let cropbox = match
self.lookup_direct(pdf_page_cropbox_key(), first_page.rest) {
Some(raw_cropbox) => Some(self.parse_rectangle(raw_cropbox))
None => None
}
let page_width = match cropbox {
Some(crop) => crop.max_x - crop.min_x
None => media_width
}
let (left_margin, right_margin, bottom_margin, top_margin) = match cropbox {
Some(crop) =>
(
crop.min_x + margin,
media.max_x - crop.max_x + margin,
crop.min_y + margin,
media.max_y - crop.max_y + margin,
)
None => (margin, margin, margin, margin)
}
let elements = self.toc_type_elements(
fontpack, font_size, title, dotleader, process_struct_tree, labels, bookmarks,
page_width, margin,
)
let result = self.pdf_type_typeset(
process_struct_tree, left_margin, right_margin, top_margin, bottom_margin, paper,
elements,
)
let cropbox_object = match cropbox {
Some(crop) => Some(pdf_toc_rectangle_object(crop))
None => None
}
{ ..result, pages: pdf_toc_cropbox_pages(result.pages, cropbox_object) }
}
///|
/// Compatibility wrapper for `PdfDocument::toc_typeset_pages`.
pub fn pdf_toc_typeset_pages(
document : PdfDocument,
fontpack : PdfFontPack,
font_size : Double,
title : BytesView,
dotleader : Bool,
process_struct_tree : Bool,
labels : ArrayView[PdfPageLabel],
bookmarks : ArrayView[PdfBookmark],
) -> PdfTypeTypesetResult raise PdfError {
document.toc_typeset_pages(
fontpack, font_size, title, dotleader, process_struct_tree, labels, bookmarks,
)
}
///|
fn pdf_toc_original_page_changes(
original_page_count : Int,
toc_page_count : Int,
) -> Array[(Int, Int)] {
[
for index in 0.. {
(index + 1, index + 1 + toc_page_count)
}
]
}
///|
fn pdf_toc_prepend_pages(
toc_pages : ArrayView[PdfPage],
original_pages : ArrayView[PdfPage],
) -> Array[PdfPage] {
let output : Array[PdfPage] = []
for page in toc_pages {
output.push(page)
}
for page in original_pages {
output.push(page)
}
output
}
///|
/// Shift existing page-label ranges after prepended TOC pages.
///
/// cpdftoc writes a no-label range for the inserted TOC pages, then preserves
/// existing labels by moving their start pages forward by the TOC page count.
pub fn pdf_toc_shift_page_labels(
toc_page_count : Int,
labels : ArrayView[PdfPageLabel],
) -> Array[PdfPageLabel] {
let output : Array[PdfPageLabel] = [
{
label_style: PageLabelNoLabelPrefixOnly,
label_prefix: None,
start_page: 1,
start_value: 1,
},
]
for label in labels {
output.push({ ..label, start_page: label.start_page + toc_page_count })
}
output
}
///|
/// Prepend already-typeset TOC pages and shift existing page labels.
///
/// Page references in destinations, annotations, open actions, and bookmarks
/// are rewritten from original one-based page serials to their new serials
/// after the inserted TOC pages.
pub fn PdfDocument::toc_insert_pages(
self : PdfDocument,
toc_pages : ArrayView[PdfPage],
labels : ArrayView[PdfPageLabel],
) -> PdfDocument raise PdfError {
let original_pages = self.pages_of_pagetree()
let toc_page_count = toc_pages.length()
let pages = pdf_toc_prepend_pages(toc_pages, original_pages)
let inserted = self.change_pages(
pages,
change_references=true,
changes=pdf_toc_original_page_changes(
original_pages.length(),
toc_page_count,
),
)
inserted.write_page_labels(pdf_toc_shift_page_labels(toc_page_count, labels))
}
///|
/// Compatibility wrapper for `PdfDocument::toc_insert_pages`.
pub fn pdf_toc_insert_pages(
document : PdfDocument,
toc_pages : ArrayView[PdfPage],
labels : ArrayView[PdfPageLabel],
) -> PdfDocument raise PdfError {
document.toc_insert_pages(toc_pages, labels)
}
///|
/// Convert a TOC title to the PDFDocString stored in the generated bookmark.
///
/// Literal `\n` byte pairs are converted to line-feed bytes before PDFDocString
/// encoding, matching cpdftoc's `real_newline` helper.
pub fn pdf_toc_bookmark_text(title : BytesView) -> PdfBytes raise PdfError {
pdf_pdfdocstring_of_utf8(pdf_toc_title_real_newlines(title))
}
///|
/// Prepend the generated table-of-contents bookmark.
///
/// Existing bookmarks are preserved at their current levels and order. The new
/// bookmark targets the first page of the current document, which should be the
/// first inserted TOC page after `toc_insert_pages`.
pub fn PdfDocument::toc_add_bookmark(
self : PdfDocument,
title : BytesView,
) -> PdfDocument raise PdfError {
let page_refs = self.page_reference_numbers()
guard page_refs.length() > 0 else {
raise SoftError("toc_add_bookmark: no pages")
}
let bookmarks = self.read_bookmarks(preserve_actions=true)
let output : Array[PdfBookmark] = [
{
level: 0,
text: pdf_toc_bookmark_text(title),
target: DestXYZ(TargetPageObject(page_refs[0]), None, None, None),
is_open: false,
colour: pdf_bookmark_black,
flags: 0,
},
]
for bookmark in bookmarks {
output.push(bookmark)
}
self.add_bookmarks(output)
}
///|
/// Compatibility wrapper for `PdfDocument::toc_add_bookmark`.
pub fn pdf_toc_add_bookmark(
document : PdfDocument,
title : BytesView,
) -> PdfDocument raise PdfError {
document.toc_add_bookmark(title)
}
///|
/// Add a table of contents using an already prepared font pack.
///
/// This composes the non-structure-tree cpdftoc path: read existing bookmarks
/// and labels, typeset TOC pages, prepend them, shift page labels, and
/// optionally add the generated TOC bookmark. Documents without bookmarks are
/// returned unchanged.
pub fn PdfDocument::toc_with_fontpack(
self : PdfDocument,
fontpack : PdfFontPack,
font_size : Double,
title : BytesView,
bookmark? : Bool = false,
dotleader? : Bool = false,
) -> PdfDocument raise PdfError {
let source_bookmarks = self.read_bookmarks(preserve_actions=true)
if source_bookmarks.length() == 0 {
self
} else {
let labels = self.read_page_labels()
let row_bookmarks = self.read_bookmarks(preserve_actions=false)
let result = self.toc_typeset_pages(
fontpack, font_size, title, dotleader, false, labels, row_bookmarks,
)
let inserted = self.toc_insert_pages(result.pages, labels)
if bookmark {
inserted.toc_add_bookmark(title)
} else {
inserted
}
}
}
///|
/// Compatibility wrapper for `PdfDocument::toc_with_fontpack`.
pub fn pdf_toc_with_fontpack(
document : PdfDocument,
fontpack : PdfFontPack,
font_size : Double,
title : BytesView,
bookmark? : Bool = false,
dotleader? : Bool = false,
) -> PdfDocument raise PdfError {
document.toc_with_fontpack(fontpack, font_size, title, bookmark~, dotleader~)
}
///|
fn pdf_toc_fontpack_of_source(
source : PdfTocFontSource,
) -> PdfFontPack raise PdfError {
match source {
PdfTocPreMadeFontPack(pack) => pack
PdfTocExistingNamedFont =>
raise SoftError("Cannot use existing font with -table-of-contents")
}
}
///|
/// Add a table of contents using a cpdftoc font source.
///
/// Existing named fonts are rejected for TOCs, matching cpdf. Embeddable
/// TrueType font sources will be handled by a later embedding slice.
pub fn PdfDocument::toc_with_source(
self : PdfDocument,
source : PdfTocFontSource,
font_size : Double,
title : BytesView,
bookmark? : Bool = false,
dotleader? : Bool = false,
) -> PdfDocument raise PdfError {
self.toc_with_fontpack(
pdf_toc_fontpack_of_source(source),
font_size,
title,
bookmark~,
dotleader~,
)
}
///|
/// Compatibility wrapper for `PdfDocument::toc_with_source`.
pub fn pdf_toc_with_source(
document : PdfDocument,
source : PdfTocFontSource,
font_size : Double,
title : BytesView,
bookmark? : Bool = false,
dotleader? : Bool = false,
) -> PdfDocument raise PdfError {
document.toc_with_source(source, font_size, title, bookmark~, dotleader~)
}