pdf_engine/xfa.rs
1//! XFA feature surface for `pdf-engine`.
2//!
3//! This module is compiled only with the `xfa` feature and re-exports the
4//! high-level helpers needed to extract and flatten XFA content without making
5//! the base crate depend on the XFA stack by default.
6
7// Copyright (c) 2026 Innovation Trigger B.V.
8//
9// PDFluent is available under two licences, at your option: the GNU AGPLv3, or
10// the PDFluent Commercial Licence. See the LICENSE file in this repository --
11// that file travels with the copy you received, which a URL does not.
12
13use crate::PdfDocument;
14
15pub use pdf_xfa::error::XfaError;
16pub use pdf_xfa::extract::XfaPackets;
17pub use pdf_xfa::session::{
18 XfaFieldModel, XfaFieldOption, XfaFieldType, XfaRect, XfaSession, XfaSetOutcome, XfaWidget,
19 XfaWriteValue, XfaWritebackReport,
20};
21pub use xfa_json::{export_schema, form_tree_to_json, form_tree_to_value, FormData, FormSchema};
22pub use xfa_layout_engine::form::{FormNodeId, FormTree};
23
24/// Extract the XFA packets embedded in a document.
25pub fn extract_packets(document: &PdfDocument) -> Result<XfaPackets, XfaError> {
26 pdf_xfa::extract::extract_xfa(document.pdf())
27}
28
29/// Returns `true` if the document has an XFA template packet.
30///
31/// Only an active `/AcroForm` `/XFA` entry counts here. Orphaned XFA streams
32/// left behind after flattening should not trigger another flatten/render pass.
33/// XFA PDFs without a template are treated as non-XFA (they have no content to
34/// flatten), so this returns `false` for those documents.
35pub fn has_xfa(document: &PdfDocument) -> bool {
36 pdf_xfa::extract::extract_xfa_from_acroform(document.pdf())
37 .is_some_and(|packets| packets.template().is_some())
38}
39
40/// Flatten an XFA document into static PDF bytes.
41pub fn flatten(document: &PdfDocument) -> Result<Vec<u8>, XfaError> {
42 pdf_xfa::flatten_xfa_to_pdf(document.pdf().data().as_ref())
43}
44
45/// Flatten an encrypted XFA document, using `password` to open it.
46///
47/// [`flatten`] tries the empty password only, which covers owner-only
48/// encryption and nothing else. See #149.
49pub fn flatten_with_password(document: &PdfDocument, password: &str) -> Result<Vec<u8>, XfaError> {
50 pdf_xfa::flatten::flatten_xfa_to_pdf_with_password(document.pdf().data().as_ref(), password)
51}