///|
fn PdfPageSpecContext::parse_tokens(
self : PdfPageSpecContext,
tokens : ArrayView[PdfPageSpecToken],
) -> Array[Int] raise PdfError {
if tokens.length() == 0 {
self.bad()
}
let comma_count = pdf_pagespec_count_commas(tokens)
if comma_count > 0 {
let parts : Array[Array[Int]] = Array::new(capacity=comma_count + 1)
for part in pdf_pagespec_split_commas(tokens, self, comma_count) {
parts.push(self.parse_tokens(part))
}
pdf_pagespec_flatten(parts)
} else if tokens.length() == 1 {
match tokens[0] {
PageSpecWord("all") => pdf_pagespec_all_pages(self.end_page)
PageSpecWord("reverse") => pdf_pagespec_range(self.end_page, 1)
PageSpecWord("empty") => []
PageSpecWord("odd") =>
pdf_pagespec_filter_odd(pdf_pagespec_all_pages(self.end_page))
PageSpecWord("even") =>
pdf_pagespec_filter_even(pdf_pagespec_all_pages(self.end_page))
PageSpecWord("portrait") =>
self.select_dimensions(pdf_pagespec_all_pages(self.end_page), true)
PageSpecWord("landscape") =>
self.select_dimensions(pdf_pagespec_all_pages(self.end_page), false)
PageSpecWord("annotated") =>
self.select_annotated(pdf_pagespec_all_pages(self.end_page))
token =>
match pdf_pagespec_token_number(self, token) {
Some(page) => [page]
None => {
self.bad()
[]
}
}
}
} else if tokens.length() == 3 && tokens[1] is PageSpecDash {
match
(
pdf_pagespec_token_number(self, tokens[0]),
pdf_pagespec_token_number(self, tokens[2]),
) {
(Some(start), Some(finish)) => pdf_pagespec_range(start, finish)
_ => {
self.bad()
[]
}
}
} else {
self.bad()
[]
}
}
///|
fn pdf_pagespec_digit_dup_prefix(text : String) -> (Int, String)? {
let bytes = @utf8.encode(text)
let mut index = 0
while index < bytes.length() && pdf_pagespec_is_digit(bytes[index].to_int()) {
index += 1
}
if index > 0 &&
index + 3 <= bytes.length() &&
bytes[index].to_int() == 68 &&
bytes[index + 1].to_int() == 85 &&
bytes[index + 2].to_int() == 80 {
let count_text = @ascii.decode_lossy(bytes[0:index])
match pdf_pagespec_parse_nonnegative_int(count_text) {
Some(count) =>
Some((count, text.unsafe_substring(start=index + 3, end=text.length())))
None => None
}
} else {
None
}
}
///|
fn PdfPageSpecContext::filter_valid_pages(
self : PdfPageSpecContext,
pages : ArrayView[Int],
) -> Array[Int] {
let output : Array[Int] = Array::new(capacity=pages.length())
for page in pages {
if page > 0 && page <= self.end_page {
output.push(page)
}
}
output
}
///|
fn PdfPageSpecContext::parse_resolved_text(
self : PdfPageSpecContext,
text : String,
) -> Array[Int] raise PdfError {
match pdf_pagespec_digit_dup_prefix(text) {
Some((count, rest)) =>
return pdf_pagespec_duplicate_range(count, self.parse_inner(rest))
None => ()
}
if text.has_prefix("NOT") {
return pdf_pagespec_invert_range(
self.end_page,
self.parse_inner(text.unsafe_substring(start=3, end=text.length())),
)
}
if text == "odd" {
return pdf_pagespec_filter_odd(pdf_pagespec_all_pages(self.end_page))
}
if text == "even" {
return pdf_pagespec_filter_even(pdf_pagespec_all_pages(self.end_page))
}
if text == "portrait" {
return self.select_dimensions(pdf_pagespec_all_pages(self.end_page), true)
}
if text == "landscape" {
return self.select_dimensions(pdf_pagespec_all_pages(self.end_page), false)
}
if text == "annotated" {
return self.select_annotated(pdf_pagespec_all_pages(self.end_page))
}
if text.has_suffix("odd") {
return pdf_pagespec_filter_odd(
self.parse_inner(text.unsafe_substring(start=0, end=text.length() - 3)),
)
}
if text.has_suffix("even") {
return pdf_pagespec_filter_even(
self.parse_inner(text.unsafe_substring(start=0, end=text.length() - 4)),
)
}
if text.has_suffix("portrait") {
return self.select_dimensions(
self.parse_inner(text.unsafe_substring(start=0, end=text.length() - 8)),
true,
)
}
if text.has_suffix("landscape") {
return self.select_dimensions(
self.parse_inner(text.unsafe_substring(start=0, end=text.length() - 9)),
false,
)
}
self.parse_tokens(pdf_pagespec_tokenize(text, self))
}
///|
fn PdfPageSpecContext::parse_inner(
self : PdfPageSpecContext,
spec : String,
) -> Array[Int] raise PdfError {
let effective = if spec == "" { "all" } else { spec }
let resolved = self.resolve_page_labels(@utf8.encode(effective))
let resolved_text = @ascii.decode_lossy(resolved)
self.filter_valid_pages(self.parse_resolved_text(resolved_text))
}
///|
/// Parse a cpdf page specification against a document with `end_page` pages.
pub fn pdf_parse_pagespec_for_count(
end_page : Int,
spec : String,
) -> Array[Int] raise PdfError {
if end_page < 1 {
raise BadPageSpecification(spec)
}
{ end_page, document: None, source: spec }.parse_inner(spec)
}
///|
/// Parse a cpdf page specification without a concrete PDF document.
pub fn pdf_parse_pagespec_without_pdf(
spec : String,
) -> Array[Int] raise PdfError {
pdf_parse_pagespec_for_count(500000, spec)
}
///|
/// Parse a cpdf page specification against this document.
pub fn PdfDocument::parse_pagespec(
self : PdfDocument,
spec : String,
) -> Array[Int] raise PdfError {
let end_page = self.endpage()
if end_page < 1 {
raise BadPageSpecification(spec)
}
{ end_page, document: Some(self), source: spec }.parse_inner(spec)
}
///|
/// Source-spelled wrapper for `PdfDocument::parse_pagespec`.
pub fn pdf_parse_pagespec(
document : PdfDocument,
spec : String,
) -> Array[Int] raise PdfError {
document.parse_pagespec(spec)
}