use crate::core::{Rect, Size};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PdfSecurity {
pub user_password: Option<String>,
pub owner_password: Option<String>,
pub print_permission: bool,
pub edit_permission: bool,
pub copy_permission: bool,
pub annotation_permission: bool,
}
impl Default for PdfSecurity {
fn default() -> Self {
Self {
user_password: None,
owner_password: None,
print_permission: true,
edit_permission: true,
copy_permission: true,
annotation_permission: true,
}
}
}
#[derive(Debug, Clone)]
pub enum PdfFormField {
TextField {
name: String,
rect: Rect,
value: String,
},
CheckBox {
name: String,
rect: Rect,
checked: bool,
},
Button {
name: String,
rect: Rect,
text: String,
},
ComboBox {
name: String,
rect: Rect,
value: String,
options: Vec<String>,
},
ListBox {
name: String,
rect: Rect,
selected: Vec<usize>,
options: Vec<String>,
},
}
#[derive(Debug, Clone)]
pub(crate) struct PdfPagination {
pub(crate) enabled: bool,
pub(crate) prefix: String,
pub(crate) start_at: u32,
pub(crate) right_margin: f32,
pub(crate) bottom_margin: f32,
pub(crate) font_size: f32,
}
impl Default for PdfPagination {
fn default() -> Self {
Self {
enabled: false,
prefix: "Page".to_string(),
start_at: 1,
right_margin: 140.0,
bottom_margin: 20.0,
font_size: 10.0,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct PdfFontResource {
pub(crate) resource_name: String,
pub(crate) base_font: String,
pub(crate) source_path: Option<String>,
pub(crate) embedded_data: Vec<u8>,
}
impl PdfFontResource {
pub(crate) fn core_helvetica(resource_name: &str) -> Self {
Self {
resource_name: resource_name.to_string(),
base_font: "Helvetica".to_string(),
source_path: None,
embedded_data: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ImageEncodingRoute {
Rgb,
RgbaDropAlpha,
GrayExpand,
}
impl ImageEncodingRoute {
pub(crate) fn as_str(self) -> &'static str {
match self {
ImageEncodingRoute::Rgb => "exact-rgb",
ImageEncodingRoute::RgbaDropAlpha => "exact-rgba-drop-alpha",
ImageEncodingRoute::GrayExpand => "exact-gray-expand",
}
}
}
pub(crate) fn normalize_image_payload_to_rgb(
image: &[u8],
width: usize,
height: usize,
) -> Result<(Vec<u8>, ImageEncodingRoute), String> {
let pixel_count = width.checked_mul(height).ok_or_else(|| {
format!("PDF image dimensions {width}x{height} overflow addressable size")
})?;
let expected_rgb_len = pixel_count.checked_mul(3).ok_or_else(|| {
format!("PDF RGB image dimensions {width}x{height} overflow addressable size")
})?;
let expected_rgba_len = pixel_count.checked_mul(4).ok_or_else(|| {
format!("PDF RGBA image dimensions {width}x{height} overflow addressable size")
})?;
let expected_gray_len = pixel_count;
if image.len() == expected_rgb_len {
let mut rgb = reserve_rgb(expected_rgb_len)?;
rgb.extend_from_slice(image);
return Ok((rgb, ImageEncodingRoute::Rgb));
}
if image.len() == expected_rgba_len {
let mut rgb = reserve_rgb(expected_rgb_len)?;
for chunk in image.as_chunks::<4>().0 {
rgb.extend_from_slice(&chunk[..3]);
}
return Ok((rgb, ImageEncodingRoute::RgbaDropAlpha));
}
if image.len() == expected_gray_len {
let mut rgb = reserve_rgb(expected_rgb_len)?;
for gray in image {
rgb.push(*gray);
rgb.push(*gray);
rgb.push(*gray);
}
return Ok((rgb, ImageEncodingRoute::GrayExpand));
}
Err(format!(
"PDF image payload has {} byte(s), but {width}x{height} requires {expected_rgb_len} (RGB), \
{expected_rgba_len} (RGBA), or {expected_gray_len} (grayscale); refusing to alter pixels",
image.len()
))
}
fn reserve_rgb(length: usize) -> Result<Vec<u8>, String> {
let mut rgb = Vec::new();
rgb.try_reserve_exact(length)
.map_err(|error| format!("unable to allocate {length} bytes for PDF image: {error}"))?;
Ok(rgb)
}
pub(crate) struct ParsedPdfPage {
pub(crate) size: Size,
pub(crate) content: Vec<u8>,
}