///|
/// Return the files cpdfimage's `extract_images` would write for page XObjects.
///
/// This ports the in-memory extraction side: page image XObjects, indirect
/// soft masks, recursive Form XObject image traversal, optional inline images,
/// cpdf filename expansion, and cpdf's indirect-image deduplication modes.
/// Decoded image data is emitted as raw PNM; external conversion and mask
/// composition are handled by native/file wrappers when available.
pub fn PdfDocument::extract_images_files(
  self : PdfDocument,
  page_numbers : ArrayView[Int],
  stem : String,
  encoding? : @metadata.PdfMetadataEncoding = PdfMetadataUTF8,
  inline? : Bool = false,
  dedup? : Bool = false,
  dedup_per_page? : Bool = false,
) -> Array[PdfExtractedImageFile] raise @core.PdfError {
  let pages = self.pages_of_pagetree()
  pdf_page_box_validate_page_numbers(page_numbers, pages.length(), "images")
  let state = PdfImageExtractionRunState::{
    serial: { val: 0, },
    written: [],
    jbig2: { next_jbig2_serial: { val: 0, }, globals: [], },
  }
  let files : Array[PdfExtractedImageFile] = Array(
    capacity=page_numbers.length(),
  )
  let mut selected_page_number = 1
  for page_number in page_numbers {
    if dedup_per_page {
      state.reset_written()
    }
    let page = pages[page_number - 1]
    let (page_images, page_forms) = self.pdf_image_partition_xobjects(
      page.resources,
    )
    let claimed_page_images = state.claim_images(
      page_images, dedup, dedup_per_page,
    )
    let (page_image_files, _) = self.pdf_image_extract_named_images(
      page.resources,
      claimed_page_images,
      stem,
      selected_page_number,
      encoding,
      state,
    )
    for file in page_image_files {
      files.push(file)
    }
    for index in 0.. Array[PdfExtractedImageFile] raise @core.PdfError {
  document.extract_images_files(
    page_numbers,
    stem,
    encoding~,
    inline~,
    dedup~,
    dedup_per_page~,
  )
}

///|
/// Return the files cpdfimage's `extract_single_image` would write.
///
/// Encoded JPEG, JPEG2000, and JBIG2 images keep their encoded payloads.
/// Other supported images are decoded through `get_image_24bpp` and emitted as
/// binary PNM (`P6`) RGB files. An indirect `/SMask` is emitted with the
/// `-smask` stem, matching cpdf's single-image extraction path.
pub fn PdfDocument::extract_single_image_files(
  self : PdfDocument,
  object_number : Int,
  stem : String,
) -> Array[PdfExtractedImageFile] raise @core.PdfError {
  let state = PdfImageExtractionState::{
    next_jbig2_serial: { val: 0, },
    globals: [],
  }
  let files = self.pdf_image_extracted_files_of_image(
    PdfDictionary([]),
    stem,
    PdfIndirect(object_number),
    state,
  )
  let image = self.direct(PdfIndirect(object_number))
  match image {
    PdfStreamObject(_) =>
      match image.lookup_immediate(pdf_image_smask_key()) {
        Some(PdfIndirect(mask_number)) =>
          for
            file in self.pdf_image_extracted_files_of_image(
              PdfDictionary([]),
              stem + "-smask",
              PdfIndirect(mask_number),
              state,
            ) {
            files.push(file)
          }
        _ => ()
      }
    _ => ()
  }
  files
}

///|
/// Compatibility wrapper for `PdfDocument::extract_single_image_files`.
pub fn pdf_image_extract_single_image_files(
  document : PdfDocument,
  object_number : Int,
  stem : String,
) -> Array[PdfExtractedImageFile] raise @core.PdfError {
  document.extract_single_image_files(object_number, stem)
}