#![forbid(unsafe_code)]
#![warn(missing_docs)]
use thiserror::Error;
#[derive(Debug, Error)]
pub enum OfdError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("container error: {0}")]
Package(#[from] PackageError),
#[error("XML error: {0}")]
Xml(#[from] XmlError),
#[error("format error: {0}")]
Format(#[from] FormatError),
#[error("render error: {0}")]
Render(#[from] RenderError),
#[error("font error: {0}")]
Font(#[from] FontError),
#[error("signature error: {0}")]
Signature(#[from] SignatureError),
#[error("crypto error: {0}")]
Crypto(#[from] CryptoError),
#[error("conversion error: {0}")]
Convert(#[from] ConvertError),
#[error("edit error: {0}")]
Edit(#[from] EditError),
#[error("print error: {0}")]
Print(#[from] PrintError),
#[error("document is encrypted, password required")]
PasswordRequired,
#[error("compliance check failed: {0}")]
Compliance(String),
#[error("resource limit exceeded: {0}")]
ResourceLimit(String),
}
impl OfdError {
pub fn is_corruption(&self) -> bool {
matches!(
self,
OfdError::Format(_) | OfdError::Package(PackageError::Corrupted { .. })
)
}
}
pub type Result<T, E = OfdError> = std::result::Result<T, E>;
#[derive(Debug, Error)]
pub enum PackageError {
#[error("container structure is corrupted: {reason}")]
Corrupted {
reason: String,
},
#[error("container is missing required entry: {entry}")]
MissingEntry {
entry: String,
},
#[error("illegal entry path (possible path traversal): {path}")]
IllegalPath {
path: String,
},
#[error("decompressed size limit exceeded: {actual} bytes, limit {limit} bytes")]
DecompressionLimit {
actual: u64,
limit: u64,
},
#[error("entry count limit exceeded: {actual} > {limit}")]
EntryCountLimit {
actual: usize,
limit: usize,
},
}
#[derive(Debug, Error)]
pub enum XmlError {
#[error("XML syntax error at line {line}, column {col}: {msg}")]
Syntax {
line: u64,
col: u64,
msg: String,
},
#[error("element <{element}> is missing required attribute {attr}")]
MissingAttribute {
element: String,
attr: String,
},
#[error("element <{element}> attribute {attr}={value:?} could not be parsed")]
InvalidAttribute {
element: String,
attr: String,
value: String,
},
#[error("unsupported extension element: {0}")]
UnsupportedExtension(String),
}
#[derive(Debug, Error)]
pub enum FormatError {
#[error("not a valid OFD file (missing OFD.xml entry point)")]
NotOfd,
#[error("document body declares no root path")]
MissingRoot,
#[error("path object declares no drawing data")]
MissingPathData,
#[error("unsupported OFD version: {0}")]
UnsupportedVersion(String),
#[error("dangling reference: {kind} with id {id}")]
DanglingReference {
kind: &'static str,
id: String,
},
#[error("circular reference detected: {chain}")]
CircularReference {
chain: String,
},
#[error("page index out of range: {index} (document has {total} pages)")]
PageOutOfRange {
index: usize,
total: usize,
},
#[error("document declares no signatures")]
MissingSignatures,
#[error("parse recursion depth exceeded: {0}")]
MaxDepthExceeded(usize),
}
#[derive(Debug, Error)]
pub enum RenderError {
#[error("backend {backend} does not support operation: {op}")]
UnsupportedOp {
backend: &'static str,
op: &'static str,
},
#[error("invalid output size: {w}x{h}")]
InvalidSize {
w: u32,
h: u32,
},
#[error("image decode failed on page {page}: {reason}")]
ImageDecode {
page: usize,
reason: String,
},
#[error("image encode failed on page {page}: {reason}")]
ImageEncode {
page: usize,
reason: String,
},
#[error("render aborted after exceeding budget: {0}")]
BudgetExceeded(String),
}
#[derive(Debug, Error)]
pub enum FontError {
#[error("font parse failed: {0}")]
Parse(String),
#[error("font missing with no substitute available: {family}")]
Missing {
family: String,
},
#[error("font {family} is missing glyph U+{codepoint:04X}")]
GlyphMissing {
family: String,
codepoint: u32,
},
}
#[derive(Debug, Error)]
pub enum SignatureError {
#[error("signature verification failed: {0}")]
Invalid(String),
#[error("document content has been tampered with; signed range mismatch")]
Tampered,
#[error("certificate chain validation failed: {0}")]
CertificateChain(String),
#[error("certificate has expired: {subject}")]
CertificateExpired {
subject: String,
},
#[error("unsupported signature format: {0}")]
UnsupportedFormat(String),
#[error("seal impression verification failed")]
SealMismatch,
}
#[derive(Debug, Error)]
pub enum CryptoError {
#[error("cryptographic operation failed: {0}")]
Operation(String),
#[error("invalid key material: {0}")]
InvalidKey(String),
#[error("decryption failed: wrong password or corrupted data")]
DecryptFailed,
#[error("unsupported algorithm: {0}")]
UnsupportedAlgorithm(String),
}
#[derive(Debug, Error)]
pub enum ConvertError {
#[error("unsupported source format: {0}")]
UnsupportedSource(String),
#[error("unsupported target format: {0}")]
UnsupportedTarget(String),
#[error("structure mapping failed on page {page}, object {object}: {reason}")]
MappingFailed {
page: usize,
object: String,
reason: String,
},
#[error(
"conversion quality below threshold: difference ratio {diff_ratio:.4} exceeds {threshold:.4}"
)]
QualityBelowThreshold {
diff_ratio: f64,
threshold: f64,
},
#[error("conversion was cancelled")]
Cancelled,
}
#[derive(Debug, Error)]
pub enum EditError {
#[error("edit target not found: {0}")]
TargetNotFound(String),
#[error("operation denied by permission policy: {0}")]
PermissionDenied(String),
#[error(
"this operation would invalidate {count} existing signature(s) and needs explicit confirmation"
)]
WouldInvalidateSignature {
count: usize,
},
#[error("nothing to undo")]
NothingToUndo,
}
#[derive(Debug, Error)]
pub enum PrintError {
#[error("cannot parse page selection: {spec}")]
InvalidSelection {
spec: String,
},
#[error("invalid page range {start}-{end}")]
InvalidRange {
start: usize,
end: usize,
},
#[error("page {page} is out of range: document has {count} pages")]
PageOutOfRange {
page: usize,
count: usize,
},
#[error("copies must be at least one, got {copies}")]
InvalidCopies {
copies: usize,
},
#[error("scale factor must be positive and finite, got {scale}")]
InvalidScale {
scale: f64,
},
#[error("paper size must be positive, got {w}x{h} mm")]
InvalidPaper {
w: f64,
h: f64,
},
}