use crate::{PdfUAError, Result};
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PdfUALevel {
PdfUA1,
PdfUA2,
}
impl PdfUALevel {
#[must_use]
pub const fn iso_identifier(&self) -> &'static str {
match self {
Self::PdfUA1 => "ISO 14289-1",
Self::PdfUA2 => "ISO 14289-2",
}
}
#[must_use]
pub const fn part(&self) -> i32 {
match self {
Self::PdfUA1 => 1,
Self::PdfUA2 => 2,
}
}
}
impl std::fmt::Display for PdfUALevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PDF/UA-{}", self.part())
}
}
#[derive(Debug, Clone)]
pub struct ParsePdfUALevelError(String);
impl std::fmt::Display for ParsePdfUALevelError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::error::Error for ParsePdfUALevelError {}
impl std::str::FromStr for PdfUALevel {
type Err = ParsePdfUALevelError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"1" | "ua1" | "pdfua1" | "pdf/ua-1" => Ok(Self::PdfUA1),
"2" | "ua2" | "pdfua2" | "pdf/ua-2" => Ok(Self::PdfUA2),
_ => Err(ParsePdfUALevelError(format!(
"Unknown PDF/UA level: {s}. Use '1' or '2'."
))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IssueSeverity {
Error,
Warning,
Info,
}
impl std::fmt::Display for IssueSeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Error => write!(f, "error"),
Self::Warning => write!(f, "warning"),
Self::Info => write!(f, "info"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IssueCategory {
Structure,
AltText,
Language,
Headings,
Tables,
Lists,
Links,
Forms,
Color,
ReadingOrder,
}
impl std::fmt::Display for IssueCategory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Structure => write!(f, "Structure"),
Self::AltText => write!(f, "Alt Text"),
Self::Language => write!(f, "Language"),
Self::Headings => write!(f, "Headings"),
Self::Tables => write!(f, "Tables"),
Self::Lists => write!(f, "Lists"),
Self::Links => write!(f, "Links"),
Self::Forms => write!(f, "Forms"),
Self::Color => write!(f, "Color"),
Self::ReadingOrder => write!(f, "Reading Order"),
}
}
}
#[derive(Debug, Clone)]
pub struct PdfUAIssue {
pub severity: IssueSeverity,
pub category: IssueCategory,
pub description: String,
pub clause: Option<String>,
pub suggestion: Option<String>,
pub wcag_ref: Option<String>,
pub page: u32,
pub element: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub level: PdfUALevel,
pub is_compliant: bool,
pub issues: Vec<PdfUAIssue>,
pub error_count: usize,
pub warning_count: usize,
pub pages_checked: usize,
pub tagged_elements: usize,
}
impl ValidationResult {
#[must_use]
pub const fn passed(&self) -> bool {
self.error_count == 0
}
}
pub fn validate_pdfua(_pdf_data: &[u8], _level: PdfUALevel) -> Result<ValidationResult> {
Err(PdfUAError::RequiresLicense.into())
}
#[must_use]
pub const fn generate_pdfua_xmp(
_level: PdfUALevel,
_language: &str,
_title: Option<&str>,
_author: Option<&str>,
) -> String {
String::new()
}
pub fn add_pdfua_metadata(
_pdf_data: &[u8],
_level: PdfUALevel,
_options: &AccessibilityOptions,
) -> Result<Vec<u8>> {
Err(PdfUAError::RequiresLicense.into())
}
#[derive(Debug, Clone, Default, TypedBuilder)]
#[builder(field_defaults(default, setter(into)))]
pub struct AccessibilityOptions {
#[builder(default)]
pub language: String,
#[builder(default)]
pub title: String,
#[builder(default)]
pub level: Option<PdfUALevel>,
}