///|
fn pdf_addtext_roman_upper(number : Int) -> String {
@ascii.decode_lossy(@label.pdf_page_label_roman_upper(number))
}
///|
fn pdf_addtext_roman_lower(number : Int) -> String {
@ascii.decode_lossy(
@label.pdf_page_label_lowercase(@label.pdf_page_label_roman_upper(number)),
)
}
///|
/// Format cpdf add-text's `%Bates` replacement for one page.
pub fn pdf_addtext_bates_number(
bates : Int,
page_number : Int,
bates_pad : Int?,
) -> String {
let number_text = (bates + page_number - 1).to_string()
match bates_pad {
None => number_text
Some(width) =>
if number_text.length() >= width {
number_text
} else {
let mut output = number_text
for _ in 0..<(width - number_text.length()) {
output = "0" + output
}
output
}
}
}
///|
fn pdf_addtext_bookmark_replacement(
values : @addtext.PdfAddTextReplacementValues,
level : Int,
) -> String {
if level >= 0 && level < values.bookmarks.length() {
values.bookmarks[level]
} else {
""
}
}
///|
/// Build cpdf add-text replacement callbacks for page-local fields.
///
/// Page labels and bookmark strings are supplied by the caller; this helper
/// preserves cpdf's marker names and Bates/roman/page-number formatting.
pub fn pdf_addtext_replacement_pairs(
values : @addtext.PdfAddTextReplacementValues,
) -> Array[(String, () -> String)] {
[
("%PageDiv2", fn() { ((values.page_number + 1) / 2).to_string() }),
("%Page", fn() { values.page_number.to_string() }),
("%Roman", fn() { pdf_addtext_roman_upper(values.page_number) }),
("%roman", fn() { pdf_addtext_roman_lower(values.page_number) }),
("%filename", fn() { values.filename }),
("%Label", fn() { values.page_label }),
("%EndPage", fn() { values.end_page.to_string() }),
("%EndLabel", fn() { values.end_label }),
("%Bookmark0", fn() { pdf_addtext_bookmark_replacement(values, 0) }),
("%Bookmark1", fn() { pdf_addtext_bookmark_replacement(values, 1) }),
("%Bookmark2", fn() { pdf_addtext_bookmark_replacement(values, 2) }),
("%Bookmark3", fn() { pdf_addtext_bookmark_replacement(values, 3) }),
("%Bookmark4", fn() { pdf_addtext_bookmark_replacement(values, 4) }),
(
"%Bates",
fn() {
pdf_addtext_bates_number(
values.bates,
values.page_number,
values.bates_pad,
)
},
),
]
}
///|
fn pdf_addtext_pdfdocstring_to_string(
bytes : BytesView,
) -> String raise @core.PdfError {
@utf8.decode(pdf_utf8_of_pdfdocstring(bytes)) catch {
_ => raise InvalidUTF8
}
}
///|
fn pdf_addtext_page_label_prefix(
label : @label.PdfPageLabel,
) -> String raise @core.PdfError {
match label.label_prefix {
Some(prefix) => pdf_addtext_pdfdocstring_to_string(prefix)
None => ""
}
}
///|
fn pdf_addtext_page_label_number(label : @label.PdfPageLabel) -> String {
@ascii.decode_lossy(
@label.pdf_page_label_number_bytes(label.start_value, label.label_style),
)
}
///|
/// Return cpdf add-text's visible page label for a 1-based page number.
///
/// Documents without page labels fall back to decimal page numbers.
pub fn PdfDocument::addtext_page_label(
self : PdfDocument,
page_number : Int,
) -> String raise @core.PdfError {
let labels = @label.pdf_page_label_complete(self.read_page_labels())
match @label.pdf_page_label_of_page_number(page_number, labels) {
Some(label) =>
pdf_addtext_page_label_prefix(label) +
pdf_addtext_page_label_number(label)
None => page_number.to_string()
}
}
///|
/// Compatibility wrapper for `PdfDocument::addtext_page_label`.
pub fn pdf_addtext_page_label(
document : PdfDocument,
page_number : Int,
) -> String raise @core.PdfError {
document.addtext_page_label(page_number)
}
///|
fn pdf_addtext_bookmark_text(
bookmark : @bookmark.PdfBookmark,
) -> String raise @core.PdfError {
pdf_addtext_pdfdocstring_to_string(bookmark.text)
}
///|
/// Return cpdf add-text's current bookmark text at `level` for `page_number`.
///
/// The search is constrained to the current bookmark branch so section
/// bookmarks from a previous chapter do not leak into a later chapter.
pub fn PdfDocument::addtext_bookmark_for_page(
self : PdfDocument,
bookmarks : ArrayView[@bookmark.PdfBookmark],
level : Int,
page_number : Int,
) -> String raise @core.PdfError {
let before : Array[@bookmark.PdfBookmark] = Array(capacity=bookmarks.length())
for bookmark in bookmarks {
if self.page_number_of_target(bookmark.target) <= page_number {
before.push(bookmark)
} else {
break
}
}
let branch : Array[@bookmark.PdfBookmark] = Array(capacity=before.length())
let mut before_index = before.length()
while before_index > 0 {
before_index -= 1
let bookmark = before[before_index]
if bookmark.level >= level {
branch.push(bookmark)
} else {
break
}
}
let pickfrom : Array[@bookmark.PdfBookmark] = Array(capacity=branch.length())
let mut branch_index = branch.length()
while branch_index > 0 {
branch_index -= 1
let bookmark = branch[branch_index]
if bookmark.level == level {
pickfrom.push(bookmark)
}
}
let mut fallback : @bookmark.PdfBookmark? = None
for bookmark in pickfrom {
if self.page_number_of_target(bookmark.target) == page_number {
return pdf_addtext_bookmark_text(bookmark)
}
fallback = Some(bookmark)
}
match fallback {
Some(bookmark) => pdf_addtext_bookmark_text(bookmark)
None => ""
}
}
///|
/// Compatibility wrapper for `PdfDocument::addtext_bookmark_for_page`.
pub fn pdf_addtext_bookmark_for_page(
document : PdfDocument,
bookmarks : ArrayView[@bookmark.PdfBookmark],
level : Int,
page_number : Int,
) -> String raise @core.PdfError {
document.addtext_bookmark_for_page(bookmarks, level, page_number)
}
///|
/// Build cpdf add-text replacement values from document-local state.
pub fn PdfDocument::addtext_replacement_values(
self : PdfDocument,
bookmarks : ArrayView[@bookmark.PdfBookmark],
page_number : Int,
end_page : Int,
filename : String,
bates : Int,
bates_pad : Int?,
) -> @addtext.PdfAddTextReplacementValues raise @core.PdfError {
let bookmark_values : Array[String] = Array(capacity=5)
for level in 0..<5 {
bookmark_values.push(
self.addtext_bookmark_for_page(bookmarks, level, page_number),
)
}
{
page_number,
end_page,
filename,
page_label: self.addtext_page_label(page_number),
end_label: self.addtext_page_label(end_page),
bates,
bates_pad,
bookmarks: bookmark_values,
}
}
///|
/// Compatibility wrapper for `PdfDocument::addtext_replacement_values`.
pub fn pdf_addtext_replacement_values(
document : PdfDocument,
bookmarks : ArrayView[@bookmark.PdfBookmark],
page_number : Int,
end_page : Int,
filename : String,
bates : Int,
bates_pad : Int?,
) -> @addtext.PdfAddTextReplacementValues raise @core.PdfError {
document.addtext_replacement_values(
bookmarks, page_number, end_page, filename, bates, bates_pad,
)
}
///|
/// Source-spelled compatibility wrapper matching cpdfaddtext's `replace_pairs`.
///
/// The OCaml API receives precomputed page-reference lookup state and the page
/// record; this wrapper derives the same page-local values from `document`.
pub fn pdf_replace_pairs(
bookmarks : ArrayView[@bookmark.PdfBookmark],
document : PdfDocument,
end_page : Int,
filename : String,
bates : Int,
bates_pad : Int?,
page_number : Int,
) -> Array[(String, () -> String)] raise @core.PdfError {
let values = document.addtext_replacement_values(
bookmarks, page_number, end_page, filename, bates, bates_pad,
)
pdf_addtext_replacement_pairs(values)
}