Skip to main content

pdf_engine/
error.rs

1//! Error types for the rendering engine.
2
3// Copyright (c) 2026 Innovation Trigger B.V.
4//
5// PDFluent is available under two licences, at your option: the GNU AGPLv3, or
6// the PDFluent Commercial Licence. See the LICENSE file in this repository --
7// that file travels with the copy you received, which a URL does not.
8
9use crate::api_error::PdfError;
10
11/// Errors that can occur in the rendering engine.
12#[derive(Debug, thiserror::Error)]
13pub enum EngineError {
14    /// The PDF data is invalid or could not be parsed.
15    #[error("invalid PDF: {0}")]
16    InvalidPdf(String),
17
18    /// A page index is out of range.
19    #[error("page {index} out of range (document has {count} pages)")]
20    PageOutOfRange {
21        /// The requested page index.
22        index: usize,
23        /// Total number of pages.
24        count: usize,
25    },
26
27    /// A rendering error occurred.
28    #[error("render error: {0}")]
29    RenderError(String),
30
31    /// An I/O error occurred.
32    #[error(transparent)]
33    Io(#[from] std::io::Error),
34
35    /// The PDF is encrypted and requires a password to open.
36    #[error("PDF is encrypted: {0}")]
37    Encrypted(String),
38
39    /// The page has invalid or degenerate dimensions.
40    #[error("invalid page geometry (w={width:.2}pt, h={height:.2}pt): {reason}")]
41    InvalidPageGeometry {
42        /// Page width in points.
43        width: f32,
44        /// Page height in points.
45        height: f32,
46        /// Human-readable reason for the failure.
47        reason: String,
48    },
49
50    /// XFA flattening failed.
51    #[error("XFA flatten failed: {0}")]
52    XfaFlattenFailed(String),
53
54    /// A configured resource limit was exceeded during rendering or
55    /// text extraction.  Carries the [`LimitError`] so callers can
56    /// branch on the specific cap that fired.
57    ///
58    /// [`LimitError`]: crate::limits::LimitError
59    #[error("resource limit exceeded: {0}")]
60    LimitExceeded(crate::limits::LimitError),
61}
62
63impl PdfError for EngineError {
64    fn code(&self) -> &str {
65        match self {
66            EngineError::InvalidPdf(_) => "INVALID_PDF",
67            EngineError::PageOutOfRange { .. } => "PAGE_OUT_OF_RANGE",
68            EngineError::RenderError(_) => "RENDER_ERROR",
69            EngineError::Io(_) => "IO_ERROR",
70            EngineError::Encrypted(_) => "ENCRYPTED",
71            EngineError::InvalidPageGeometry { .. } => "INVALID_PAGE_GEOMETRY",
72            EngineError::XfaFlattenFailed(_) => "XFA_FLATTEN_FAILED",
73            EngineError::LimitExceeded(_) => "LIMIT_EXCEEDED",
74        }
75    }
76
77    fn help(&self) -> Option<String> {
78        match self {
79            EngineError::InvalidPdf(_) => {
80                Some("The PDF file is corrupt or invalid. Try using a PDF repair tool or re-save the document.".to_string())
81            }
82            EngineError::PageOutOfRange { index, count } => {
83                Some(format!("Page {} does not exist. This document has {} pages (0-{}).", index, count, count - 1))
84            }
85            EngineError::RenderError(_) => {
86                Some("Rendering failed. Check that the page exists and the document is not corrupted.".to_string())
87            }
88            EngineError::Io(_) => {
89                Some("An I/O error occurred. Check file permissions and disk space.".to_string())
90            }
91            EngineError::Encrypted(_) => {
92                Some("The PDF is encrypted. Provide the correct password to open it.".to_string())
93            }
94            EngineError::InvalidPageGeometry { .. } => {
95                Some("The page has invalid or degenerate dimensions and cannot be rendered.".to_string())
96            }
97            EngineError::XfaFlattenFailed(_) => {
98                Some("XFA flattening produced an invalid PDF. The document may use unsupported XFA features.".to_string())
99            }
100            EngineError::LimitExceeded(_) => {
101                Some("A processing resource limit was exceeded. Raise the relevant cap in ProcessingLimits or check that the input is not adversarial.".to_string())
102            }
103        }
104    }
105}
106
107/// Convenience type alias.
108pub type Result<T> = std::result::Result<T, EngineError>;
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn encrypted_error_code() {
116        let e = EngineError::Encrypted("bad password".into());
117        assert_eq!(e.code(), "ENCRYPTED");
118    }
119
120    #[test]
121    fn invalid_page_geometry_error_code() {
122        let e = EngineError::InvalidPageGeometry {
123            width: 0.5,
124            height: 0.5,
125            reason: "test".into(),
126        };
127        assert_eq!(e.code(), "INVALID_PAGE_GEOMETRY");
128    }
129
130    #[test]
131    fn xfa_flatten_failed_error_code() {
132        let e = EngineError::XfaFlattenFailed("corrupt output".into());
133        assert_eq!(e.code(), "XFA_FLATTEN_FAILED");
134    }
135}