use std::{cell::OnceCell, ffi::CString, os::raw::c_ulong, ptr::null};
use crate::{
PdfiumDocument, PdfiumError, PdfiumPage, PdfiumResult, PdfiumXObject, lib,
pdfium_types::FS_SIZEF,
};
pub struct PdfiumPages<'a> {
doc: &'a PdfiumDocument,
page_count: OnceCell<i32>,
current_page: i32,
}
impl<'a> PdfiumPages<'a> {
pub(crate) fn new(doc: &'a PdfiumDocument) -> PdfiumPages<'a> {
Self {
doc,
page_count: OnceCell::new(),
current_page: 0,
}
}
pub fn page_count(&self) -> i32 {
*self.page_count.get_or_init(|| self.doc.page_count())
}
pub fn get(&self, index: i32) -> PdfiumResult<PdfiumPage> {
self.doc.page(index)
}
#[inline]
pub fn import(
&self,
src_doc: &PdfiumDocument,
pagerange: &str,
index: i32,
) -> PdfiumResult<()> {
let pagerange = CString::new(pagerange)?;
lib().FPDF_ImportPages(self.doc, src_doc, &pagerange, index)
}
#[inline]
pub fn import_by_index(
&self,
src_doc: &PdfiumDocument,
src_indices: Option<&[i32]>,
index: i32,
) -> PdfiumResult<()> {
match src_indices {
Some(indices) => lib().FPDF_ImportPagesByIndex(
self.doc.into(),
src_doc.into(),
indices.as_ptr(),
indices.len() as c_ulong,
index,
),
None => {
lib().FPDF_ImportPagesByIndex(self.doc.into(), src_doc.into(), null(), 0, index)
}
}
}
pub fn page_size(&self) -> PdfiumResult<(f32, f32)> {
let mut size = FS_SIZEF {
width: 0.0,
height: 0.0,
};
lib().FPDF_GetPageSizeByIndexF(self.doc, self.current_page, &mut size)?;
Ok((size.width, size.height))
}
pub fn page_label(&self) -> PdfiumResult<String> {
let lib = lib();
let buf_len = lib.FPDF_GetPageLabel(self.doc, self.current_page, None, 0);
if buf_len == 0 {
return Err(PdfiumError::InvokationFailed);
}
let mut buffer = vec![0u8; buf_len as usize];
lib.FPDF_GetPageLabel(self.doc, self.current_page, Some(&mut buffer), buf_len);
let utf16_codes: Vec<_> = buffer[..buffer.len().saturating_sub(2)]
.chunks_exact(2)
.map(|pair| u16::from_le_bytes([pair[0], pair[1]])) .collect();
Ok(String::from_utf16_lossy(&utf16_codes))
}
pub fn page_as_xobject(&self, dest_doc: &PdfiumDocument) -> PdfiumResult<PdfiumXObject> {
lib().FPDF_NewXObjectFromPage(dest_doc, self.doc, self.current_page)
}
}
impl<'a> Iterator for PdfiumPages<'a> {
type Item = PdfiumResult<PdfiumPage>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_page >= self.page_count() {
None
} else {
let page = self.doc.page(self.current_page);
self.current_page += 1;
Some(page)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.page_count() as usize;
let remaining = len.saturating_sub(self.current_page as usize);
(remaining, Some(remaining))
}
fn count(self) -> usize {
self.page_count() as usize - self.current_page as usize
}
fn last(mut self) -> Option<Self::Item> {
let len = self.page_count();
if len == 0 || self.current_page >= len {
None
} else {
self.current_page = len - 1;
Some(self.doc.page(self.current_page))
}
}
}
impl<'a> DoubleEndedIterator for PdfiumPages<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let len = self.page_count();
if self.current_page >= len {
None
} else {
let page = self.doc.page(len - 1);
self.page_count = OnceCell::from(len - 1); Some(page)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_page_count() {
let document = PdfiumDocument::new_from_path("resources/groningen.pdf", None).unwrap();
let mut pages = document.pages();
assert_eq!(pages.page_count(), 2);
let _ = pages.next().unwrap().unwrap();
assert_eq!(pages.page_count(), 2);
assert_eq!(pages.count(), 1); }
#[test]
fn test_import_pages() {
let document = PdfiumDocument::new().unwrap();
let src_doc = PdfiumDocument::new_from_path("resources/pg1342-images-3.pdf", None).unwrap();
document.pages().import(&src_doc, "12,14,30-34", 0).unwrap();
document.save_to_path("pride-1.pdf", None).unwrap();
let document = PdfiumDocument::new_from_path("pride-1.pdf", None).unwrap();
let page_count = document.page_count();
assert_eq!(page_count, 7);
}
#[test]
fn test_import_pages_by_index() {
let document = PdfiumDocument::new().unwrap();
let src_doc = PdfiumDocument::new_from_path("resources/pg1342-images-3.pdf", None).unwrap();
document
.pages()
.import_by_index(&src_doc, Some(&[11, 13, 29, 30, 31, 32, 33]), 0)
.unwrap();
document.save_to_path("pride-2.pdf", None).unwrap();
let document = PdfiumDocument::new_from_path("pride-2.pdf", None).unwrap();
let page_count = document.page_count();
assert_eq!(page_count, 7);
}
}