///|
/// `page_numbers` are 1-based. Bookmarks, destinations, annotations, and page
/// labels are repaired for the retained pages where possible. `retain_numbering`
/// preserves original page-label numbering; `process_struct_tree` trims tagged
/// structure references to deleted pages.
pub fn PdfDocument::pdf_of_pages(
  self : PdfDocument,
  page_numbers : ArrayView[Int],
  retain_numbering? : Bool = false,
  process_struct_tree? : Bool = false,
) -> PdfDocument raise @core.PdfError {
  let source = self.copy()
  let all_page_numbers = source.page_reference_numbers()
  let old_page_numbers = pdf_page_select_numbers(all_page_numbers, page_numbers)
  source.replace_inherit(old_page_numbers)
  let has_page_labels = source.pdf_page_label_has_labels()
  let retained_page_labels = if retain_numbering && has_page_labels {
    pdf_page_label_merge([source], [
      [
        for page_number in page_numbers => page_number
      ],
    ])
  } else {
    []
  }
  let has_bookmarks = source.pdf_bookmark_has_outlines()
  let retained_bookmarks = if has_bookmarks {
    source.pdf_bookmarks_for_pages(page_numbers) catch {
      _ => []
    }
  } else {
    []
  }
  let selected = pdf_page_select_pages(source.pages_of_pagetree(), page_numbers)
  let deleted_page_numbers = if process_struct_tree {
    pdf_page_deleted_numbers(all_page_numbers, old_page_numbers)
  } else {
    []
  }
  let pdf = source.change_pages(selected)
  let pdf = if has_bookmarks {
    pdf.add_bookmarks(retained_bookmarks)
  } else {
    pdf
  }
  let new_page_numbers = pdf.page_reference_numbers()
  pdf.pdf_page_preserve_materialized_inherited_entries(
    source, old_page_numbers, new_page_numbers,
  )
  pdf.pdf_page_renumber_references(
    pdf_page_reference_changes(old_page_numbers, new_page_numbers),
  )
  if process_struct_tree {
    pdf.pdf_structure_trim_deleted_pages(deleted_page_numbers)
  }
  pdf.pdf_page_fixup_duplicate_pages()
  pdf.pdf_page_fixup_parents()
  pdf.pdf_page_fixup_duplicate_annots()
  pdf.remove_unreferenced()
  pdf.pdf_page_fixup_destination_name_tree()
  pdf.pdf_page_fixup_catalog_destinations()
  let pdf = if has_page_labels {
    if retain_numbering {
      pdf.write_page_labels(retained_page_labels)
    } else {
      pdf.remove_page_labels()
    }
  } else {
    pdf
  }
  pdf.remove_unreferenced()
  pdf
}

///|
/// Compatibility wrapper for `PdfDocument::pdf_of_pages`.
pub fn pdf_of_pages(
  document : PdfDocument,
  page_numbers : ArrayView[Int],
  retain_numbering? : Bool = false,
  process_struct_tree? : Bool = false,
) -> PdfDocument raise @core.PdfError {
  document.pdf_of_pages(page_numbers, retain_numbering~, process_struct_tree~)
}

///|
fn pdf_page_deleted_numbers(
  all_page_numbers : ArrayView[Int],
  active_page_numbers : ArrayView[Int],
) -> Array[Int] {
  let active_pages = @core.pdf_number_set(active_page_numbers)
  let deleted : Array[Int] = Array(capacity=all_page_numbers.length())
  for number in all_page_numbers {
    if !active_pages.contains(number) {
      deleted.push(number)
    }
  }
  deleted
}