/**
 * pdf_oxide C API
 *
 * C-compatible Foreign Function Interface for pdf_oxide.
 * Used by Go (CGO), Node.js (N-API), and C# (P/Invoke) bindings, and
 * by direct C/C++ consumers.
 *
 * AUTO-GENERATED by cbindgen from `src/ffi.rs` — do not edit by hand.
 * Regenerate: `make c-header` (or see cbindgen.toml). CI fails if this
 * file is out of sync with `src/ffi.rs`.
 *
 * Error Convention:
 *   Most functions accept an `int* error_code` out-parameter.
 *   0 = success, 1 = invalid arg, 2 = IO error, 3 = parse error,
 *   4 = extraction failed, 5 = internal error, 6 = invalid page index,
 *   7 = search error, 8 = unsupported feature.
 *
 * Memory Convention:
 *   - Strings returned as `char*` must be freed with `free_string()`.
 *   - Byte buffers returned as `uint8_t*` must be freed with `free_bytes()`.
 *   - Opaque handles must be freed with their corresponding `*_free()`.
 */


#ifndef PDF_OXIDE_H
#define PDF_OXIDE_H

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

/**
 * Default cap for [`FlateDecoder`]: 256 MB per stream.
 *
 * Prevents zip-bomb / flate-bomb attacks where a tiny compressed stream
 * expands to an arbitrarily large output, exhausting virtual memory and
 * triggering an allocator abort (SIGABRT / exit 134).
 *
 * 256 MB accommodates A4 @ 600 DPI RGB (~99 MB) with headroom.
 *
 * Override via:
 * - `PDF_OXIDE_MAX_DECOMPRESS_MB` environment variable (e.g. `64` for 64 MB)
 * - [`FlateDecoder::with_limit`] for programmatic control
 */
#define DEFAULT_MAX_DECOMPRESSED_BYTES ((256 * 1024) * 1024)

/**
 * Maximum nested `{ ... }` depth permitted by the parser. PLRM has no formal
 * cap, but real-world Type 4 streams are shallow; bounding it prevents a
 * maliciously deep stream from blowing the Rust call stack since `parse_body`
 * recurses for each brace level. Programs nested deeper return
 * [`Error::InvalidPdf`].
 */
#define MAX_PARSE_DEPTH 32

/**
 * Maximum operand stack size during execution. PLRM §7.10.5.2 requires a
 * "stack overflow" diagnostic; we surface it as [`Error::Type4Runtime`]. The
 * cap matches what Adobe accepts in practice — Acrobat's interpreter allows
 * up to a few hundred operands.
 */
#define MAX_STACK 256

/**
 * Maximum number of instructions the evaluator will execute. Type 4 has no
 * loops in the language proper, but nested `if`/`ifelse` plus large generated
 * bodies (or pathological streams crafted to consume CPU) can still produce
 * arbitrarily many steps. 100 000 is generous for any realistic tint
 * transform while still being a hard upper bound. Programs that exceed this
 * budget return [`Error::Type4Runtime`].
 */
#define MAX_INSTRUCTIONS 100000

/**
 * Field is read-only (bit 1)
 */
#define READ_ONLY 1

/**
 * Field is required (bit 2)
 */
#define REQUIRED (1 << 1)

/**
 * Field should not be exported (bit 3)
 */
#define NO_EXPORT (1 << 2)

/**
 * Text field allows multiple lines (bit 13)
 */
#define MULTILINE (1 << 12)

/**
 * Text field is a password field (bit 14)
 */
#define PASSWORD (1 << 13)

/**
 * Text field does not scroll (bit 24)
 */
#define DO_NOT_SCROLL (1 << 23)

/**
 * Text field allows comb formatting (bit 25)
 */
#define COMB (1 << 24)

/**
 * Text field is a rich text field (bit 26)
 */
#define RICH_TEXT (1 << 25)

/**
 * Button is a push button (bit 17)
 */
#define PUSH_BUTTON (1 << 16)

/**
 * Radio button (bit 16)
 */
#define RADIO (1 << 15)

/**
 * Radio buttons in group are exclusive (bit 26)
 */
#define RADIOS_IN_UNISON (1 << 25)

/**
 * Choice is a combo box (bit 18)
 */
#define COMBO (1 << 17)

/**
 * Choice field is editable (bit 19)
 */
#define EDIT (1 << 18)

/**
 * Choice field is sorted (bit 20)
 */
#define SORT (1 << 19)

/**
 * Choice field allows multiple selection (bit 22)
 */
#define MULTI_SELECT (1 << 21)

/**
 * Do not spell check (bit 23)
 */
#define DO_NOT_SPELL_CHECK (1 << 22)

/**
 * Commit on change (bit 27)
 */
#define COMMIT_ON_SEL_CHANGE (1 << 26)

/**
 * Invisible flag (bit 1) - If set, do not display if no AP.
 */
#define AnnotationFlags_INVISIBLE (1 << 0)

/**
 * Hidden flag (bit 2) - If set, do not display or print.
 */
#define AnnotationFlags_HIDDEN (1 << 1)

/**
 * Print flag (bit 3) - If set, print annotation when printing.
 */
#define AnnotationFlags_PRINT (1 << 2)

/**
 * NoZoom flag (bit 4) - If set, do not scale with page zoom.
 */
#define AnnotationFlags_NO_ZOOM (1 << 3)

/**
 * NoRotate flag (bit 5) - If set, do not rotate with page.
 */
#define AnnotationFlags_NO_ROTATE (1 << 4)

/**
 * NoView flag (bit 6) - If set, do not display on screen.
 */
#define AnnotationFlags_NO_VIEW (1 << 5)

/**
 * ReadOnly flag (bit 7) - If set, do not allow interaction.
 */
#define AnnotationFlags_READ_ONLY (1 << 6)

/**
 * Locked flag (bit 8) - If set, do not allow deletion/modification.
 */
#define AnnotationFlags_LOCKED (1 << 7)

/**
 * ToggleNoView flag (bit 9) - Invert NoView on mouse events.
 */
#define AnnotationFlags_TOGGLE_NO_VIEW (1 << 8)

/**
 * LockedContents flag (bit 10) - If set, do not allow content modification.
 */
#define AnnotationFlags_LOCKED_CONTENTS (1 << 9)

/**
 * Maximum slug length in bytes (keeps total paths well under the
 * 255-byte component limit common to ext4/APFS/NTFS).
 */
#define DEFAULT_MAX_SLUG_BYTES 80

/**
 * Default minimum edge padding, in points — the `0.5pt` floor that keeps an
 * anti-aliased glyph sliver from surviving at a region boundary
 * (feature plan §4.1).
 */
#define DEFAULT_EDGE_PADDING 0.5

/**
 * Y-band tolerance used by `row_aware_span_cmp`.
 *
 * Two spans whose top-Y differs by less than this amount are treated
 * as lying on the same row. Chosen to absorb typographic baseline
 * jitter for 10-12pt body text and glyph-cluster offsets in CJK
 * fonts without merging adjacent 14pt-leading lines.
 */
#define ROW_BAND_TOLERANCE_PT 3.0

/**
 * RGB color representation.
 */
typedef struct Color Color;

/**
 * PDF document editor.
 *
 * Provides a high-level interface for modifying PDF documents.
 * Changes are tracked and can be saved either as incremental updates
 * or as a complete rewrite.
 */
typedef struct DocumentEditor DocumentEditor;

/**
 * Embedded TrueType font for PDF generation.
 *
 * Contains parsed font data, subsetter, and encoder for generating
 * CIDFont dictionaries and ToUnicode CMaps.
 */
typedef struct EmbeddedFont EmbeddedFont;

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiAnnotationList FfiAnnotationList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Opaque handle for generated barcode image (PNG bytes)
 */
typedef struct FfiBarcodeImage FfiBarcodeImage;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiCharList FfiCharList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * FFI wrapper around [`crate::writer::DocumentBuilder`] that adds a
 * reentrancy guard around the open-page slot.
 */
typedef struct FfiDocumentBuilder FfiDocumentBuilder;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiElementList FfiElementList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiFontList FfiFontList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiFormFieldList FfiFormFieldList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiImageList FfiImageList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Page sub-handle. Holds a raw back-pointer to the parent; the parent
 * must outlive this handle (enforced by the one-page-at-a-time
 * invariant documented above).
 */
typedef struct FfiPageBuilder FfiPageBuilder;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiPathList FfiPathList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiPdfAResults FfiPdfAResults;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiPdfXResults FfiPdfXResults;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiRenderedImage FfiRenderedImage;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiRenderedImage FfiRenderedImage;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Opaque search results handle.
 */
typedef struct FfiSearchResults FfiSearchResults;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Opaque handle for SignatureInfo extracted from a PDF
 */
typedef struct FfiSignatureInfo FfiSignatureInfo;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiSignatureInfo FfiSignatureInfo;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiTableList FfiTableList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiTextLineList FfiTextLineList;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiUaResults FfiUaResults;
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
typedef struct FfiWordList FfiWordList;
#endif

/**
 * A high-level PDF document with unified DOM access.
 *
 * This type provides a simple, unified API for:
 * - Creating PDFs from Markdown, HTML, or plain text
 * - Opening existing PDFs for reading and editing
 * - Navigating the document structure with a DOM-like API
 * - Modifying text, images, and other content
 * - Saving changes back to PDF
 *
 * # Example
 *
 * ```ignore
 * use pdf_oxide::api::Pdf;
 *
 * // Create from Markdown
 * let pdf = Pdf::from_markdown("# Hello")?;
 * pdf.save("hello.pdf")?;
 *
 * // Open and edit existing PDF
 * let mut doc = Pdf::open("input.pdf")?;
 * let page = doc.page(0)?;
 * for text in page.find_text_containing("old") {
 *     doc.page(0)?.set_text(text.id(), "new")?;
 * }
 * doc.save("output.pdf")?;
 * ```
 */
typedef struct Pdf Pdf;

/**
 * PDF document.
 *
 * This structure represents an open PDF document, providing access to:
 * - Document metadata (version, catalog, trailer)
 * - Page information (count, page tree)
 * - Object loading and dereferencing
 *
 * # Example
 *
 * ```no_run
 * use pdf_oxide::document::PdfDocument;
 *
 * let mut doc = PdfDocument::open("sample.pdf")?;
 * println!("PDF version: {}.{}", doc.version().0, doc.version().1);
 * println!("Page count: {}", doc.page_count()?);
 * # Ok::<(), pdf_oxide::error::Error>(())
 * ```
 *
 * # Memory management
 *
 * The document maintains several internal caches for performance. The main
 * object cache is bounded at 64 MB (see `DEFAULT_OBJECT_CACHE_MAX_BYTES`)
 * uses FIFO eviction to prevent unbounded heap growth when processing
 * many pages sequentially.
 */
typedef struct PdfDocument PdfDocument;

/**
 * Per-CID vertical-writing metrics from a CIDFont's `/W2` array.
 *
 * Per ISO 32000-1:2008 §9.7.4.3 and the Adobe CMap & CIDFont Files
 * Specification §9.7. In vertical writing mode the glyph advances along the
 * y-axis (not the x-axis) and is shifted from its default horizontal origin
 * to a vertical origin so that the glyph stacks correctly within a column.
 *
 * All values are in 1000ths-of-em (glyph-space units), matching the
 * convention used throughout PDF font dictionaries.
 */
typedef struct VerticalMetrics VerticalMetrics;

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Options for [`pdf_sign_bytes_pades_opts`] — the same parameters as
 * [`pdf_sign_bytes_pades`], collapsed into one `#[repr(C)]` struct.
 *
 * `pdf_sign_bytes_pades` takes 18 scalar arguments, which exceeds the
 * argument-marshalling limit of some FFI clients (notably the pure-Go
 * `purego` backend on the SysV/AMD64 ABI, which panics at registration
 * above ~9 arguments). This struct-pointer variant keeps the call
 * surface at 5 arguments so every binding — including `CGO_ENABLED=0`
 * Go — can invoke PAdES signing. Field order/types mirror the scalar
 * function exactly; `#[repr(C)]` makes the layout match a C struct of
 * the same fields on every supported platform.
 */
typedef struct {
    const void *certificate_handle;
    const uint8_t *const *certs;
    const uintptr_t *cert_lens;
    uintptr_t n_certs;
    const uint8_t *const *crls;
    const uintptr_t *crl_lens;
    uintptr_t n_crls;
    const uint8_t *const *ocsps;
    const uintptr_t *ocsp_lens;
    uintptr_t n_ocsps;
    const char *tsa_url;
    const char *reason;
    const char *location;
    int32_t level;
} PadesSignOptionsC;
#endif

/**
 * Index into [`Dom::nodes`].
 */
typedef uint32_t NodeId;

/**
 * Index into [`BoxTree::boxes`].
 */
typedef uint32_t BoxId;





/**
 * Document root.
 */
#define Dom_ROOT 0

/**
 * Root box id.
 */
#define BoxTree_ROOT 0

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the global log level for the library.
 * Levels: 0 = Off, 1 = Error, 2 = Warn, 3 = Info, 4 = Debug, 5 = Trace
 */
void pdf_oxide_set_log_level(int32_t level);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get the current log level. Returns 0-5 matching the set_log_level values.
 */
int32_t pdf_oxide_get_log_level(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the global content-stream operator cap.
 * `limit < 0` restores the default (1,000,000); any non-negative
 * value (including 0) is used as the explicit cap. Returns the
 * previous cap (or -1 if the default was active).
 *
 * Bindings (Java JNI, Ruby FFI, PHP FFI, Go cgo / purego, C# P/Invoke,
 * Node N-API, WASM) call this via the cdylib's exported symbol.
 */
int64_t pdf_oxide_set_max_ops_per_stream(int64_t limit);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Toggle the global U+FFFD preservation flag for
 * the high-level extract_text / extract_words / extract_spans
 * accessors. `1` = preserve FFFD chars; `0` = filter (v0.3.54
 * default). Returns the previous value as `0` or `1`.
 */
int32_t pdf_oxide_set_preserve_unmapped_glyphs(int32_t preserve);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Returns the name of the active cryptographic provider as a
 * caller-owned C string. Free with [`free_string`].
 *
 * **Non-initializing** — if no provider has been installed yet, this
 * returns `"rust-crypto (default, lazy)"` *without* committing the
 * `OnceLock`, so a subsequent [`pdf_oxide_crypto_use_fips`] call can
 * still succeed. If a provider has already been installed (either
 * explicitly via `pdf_oxide_crypto_use_fips`, or lazily by an earlier
 * crypto operation on this process), the active provider's actual
 * name is returned (e.g. `"rust-crypto"` or `"aws-lc-rs"`).
 *
 * Bindings (Python / Node / C# / Go) call this for display/audit
 * before optionally calling `crypto_use_fips`; it is intentionally
 * safe to call first.
 */
char *pdf_oxide_crypto_active_provider(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Whether the FIPS-validated `aws-lc-rs` provider was compiled
 * into this binary. Returns 1 if available (built with
 * `--features fips`), 0 otherwise.
 *
 * Bindings query this to expose the right list to user code (Python
 * `available_providers()`, etc.).
 */
int32_t pdf_oxide_crypto_fips_available(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Install the FIPS-validated `aws-lc-rs` provider as the
 * process-wide active cryptographic backend. Set-once: returns
 * non-zero error code if a provider is already installed (either
 * explicitly or lazily via a prior crypto operation).
 *
 * Error codes:
 *   0 = success
 *   1 = FIPS feature not compiled in
 *   2 = provider already set
 */
int32_t pdf_oxide_crypto_use_fips(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Install the process-wide runtime crypto policy from its grammar
 * string (`"compat"|"strict"|"fips-strict"[;<allow|deny>:<alg>@<use>]`).
 *
 * Error codes (fail-closed):
 *   0 = success
 *   1 = invalid argument (null/non-UTF-8 spec)
 *   2 = parse error (spec rejected — policy NOT installed)
 *   3 = a policy is already set (set-once)
 */
int32_t pdf_oxide_crypto_set_policy(const char *spec);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * The active crypto policy as its canonical grammar string. Caller
 * frees via [`free_string`]. Never null on success.
 */
char *pdf_oxide_crypto_policy(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Comma-joined lowercase tokens of the algorithms exercised so far
 * this process (governance inventory). Caller frees via
 * [`free_string`]; `""` when nothing has been exercised.
 */
char *pdf_oxide_crypto_inventory(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * A CycloneDX 1.6 Cryptographic Bill of Materials (JSON) of the
 * algorithms exercised so far this process (#230 Phase F). Caller
 * frees via [`free_string`].
 */
char *pdf_oxide_crypto_cbom(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free a string returned by any FFI function.
 */
void free_string(char *ptr);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free a byte buffer returned by any FFI function.
 *
 * Byte buffers are allocated with the C system allocator (malloc) so that
 * callers can free them with a plain pointer and no length argument.
 */
void free_bytes(uint8_t *ptr);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a PDF document from a file path. Returns an opaque handle.
 *
 * # Safety
 * `path` must be a valid, null-terminated UTF-8 C string.
 * The returned handle must be freed with [`pdf_document_free`].
 */
PdfDocument *pdf_document_open(const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free a PdfDocument handle.
 *
 * # Safety
 * `handle` must have been returned by [`pdf_document_open`] and must not be used after this call.
 * Passing null is safe (no-op).
 */
void pdf_document_free(PdfDocument *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get the page count.
 */
int32_t pdf_document_get_page_count(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get the PDF version as (major, minor).
 */
void pdf_document_get_version(const PdfDocument *handle, uint8_t *major, uint8_t *minor);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Check if the document has a structure tree (tagged PDF).
 */
bool pdf_document_has_structure_tree(PdfDocument *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Extract text from a single page.
 */
char *pdf_document_extract_text(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Extract a page as structured typed regions (issue #536), returned as a JSON
 * string (a serialized `StructuredPage`: `page_index`, `page_width`,
 * `page_height`, and `regions[]` with `kind` / `text` / `bbox` / `spans` /
 * `column_index`). Bindings deserialize the JSON into their native types.
 *
 * Returns NULL on error (see `error_code`); the returned string must be freed
 * with `pdf_free_string`.
 */
char *pdf_document_extract_structured_to_json(PdfDocument *handle,
                                              int32_t page_index,
                                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert a page to Markdown.
 */
char *pdf_document_to_markdown(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert a page to HTML.
 */
char *pdf_document_to_html(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert a page to plain text.
 */
char *pdf_document_to_plain_text(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert all pages to Markdown.
 */
char *pdf_document_to_markdown_all(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert the entire PDF to DOCX bytes.
 *
 * On success `out_len` is set to the number of bytes and a heap-allocated
 * buffer is returned — the caller must free it with [`free_bytes`].
 */
uint8_t *pdf_document_to_docx(PdfDocument *handle, uintptr_t *out_len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert the entire PDF to PPTX bytes.
 *
 * On success `out_len` is set to the number of bytes and a heap-allocated
 * buffer is returned — the caller must free it with [`free_bytes`].
 */
uint8_t *pdf_document_to_pptx(PdfDocument *handle, uintptr_t *out_len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert the entire PDF to XLSX bytes.
 *
 * On success `out_len` is set to the number of bytes and a heap-allocated
 * buffer is returned — the caller must free it with [`free_bytes`].
 */
uint8_t *pdf_document_to_xlsx(PdfDocument *handle, uintptr_t *out_len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a PDF document from DOCX bytes. Returns an opaque PdfDocument handle.
 */
PdfDocument *pdf_document_open_from_docx_bytes(const uint8_t *data,
                                               uintptr_t len,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a PDF document from PPTX bytes. Returns an opaque PdfDocument handle.
 */
PdfDocument *pdf_document_open_from_pptx_bytes(const uint8_t *data,
                                               uintptr_t len,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a PDF document from XLSX bytes. Returns an opaque PdfDocument handle.
 */
PdfDocument *pdf_document_open_from_xlsx_bytes(const uint8_t *data,
                                               uintptr_t len,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a PDF for editing. Returns an opaque DocumentEditor handle.
 *
 * # Safety
 * `path` must be a valid, null-terminated UTF-8 C string.
 * The returned handle must be freed with [`document_editor_free`].
 * All calls on the same handle must be serialised — see the module-level Handle Ownership Contract.
 */
DocumentEditor *document_editor_open(const char *path,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free a DocumentEditor handle.
 *
 * # Safety
 * `handle` must have been returned by [`document_editor_open`] or [`document_editor_open_from_bytes`]
 * and must not be used after this call. Passing null is safe (no-op).
 */
void document_editor_free(DocumentEditor *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Check if the editor has modifications.
 */
bool document_editor_is_modified(const DocumentEditor *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get the source path of the editor.
 */
char *document_editor_get_source_path(const DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get PDF version from the editor.
 */
void document_editor_get_version(const DocumentEditor *handle, uint8_t *major, uint8_t *minor);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get page count from editor.
 */
int32_t document_editor_get_page_count(DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Producer — reads from `/Info.Producer`. Returns a C string owned by
 * the caller (must be freed with `free_string`); returns null if no
 * producer is set.
 */
char *document_editor_get_producer(DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_set_producer(DocumentEditor *handle,
                                     const char *value,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Creation date — reads from `/Info.CreationDate` as a raw PDF
 * date string (e.g. `D:20260421120000Z`).
 */
char *document_editor_get_creation_date(DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_set_creation_date(DocumentEditor *handle,
                                          const char *date_str,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Save the edited document.
 */
int32_t document_editor_save(DocumentEditor *handle, const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a DocumentEditor from an in-memory byte buffer.
 * Returns an opaque handle, or null on error.
 */
DocumentEditor *document_editor_open_from_bytes(const uint8_t *data,
                                                uintptr_t len,
                                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Save the editor contents to an in-memory byte buffer.
 * The returned pointer is a boxed slice; free with `free_bytes`.
 * `out_len` receives the number of bytes written.
 */
uint8_t *document_editor_save_to_bytes(DocumentEditor *handle,
                                       uintptr_t *out_len,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Save the editor contents to bytes with compression / GC / linearize options.
 */
uint8_t *document_editor_save_to_bytes_with_options(DocumentEditor *handle,
                                                    bool compress,
                                                    bool garbage_collect,
                                                    bool linearize,
                                                    uintptr_t *out_len,
                                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Extract a subset of pages to a new in-memory PDF. `pages` is a
 * C array of 0-based page indices; `count` is its length.
 */
uint8_t *document_editor_extract_pages_to_bytes(DocumentEditor *handle,
                                                const int32_t *pages,
                                                uintptr_t count,
                                                uintptr_t *out_len,
                                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Convert the edited document to PDF/A in-place.
 * level: 0=A1b 1=A1a 2=A2b 3=A2a 4=A2u 5=A3b 6=A3a 7=A3u
 */
int32_t document_editor_convert_to_pdf_a(DocumentEditor *handle,
                                         int32_t level,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Save the edited document with AES-256 encryption and return bytes.
 * The returned pointer must be freed with `free_bytes`.
 */
uint8_t *document_editor_save_encrypted_to_bytes(DocumentEditor *handle,
                                                 const char *user_password,
                                                 const char *owner_password,
                                                 uintptr_t *out_len,
                                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Merge pages from an in-memory PDF byte buffer into this document.
 */
int32_t document_editor_merge_from_bytes(DocumentEditor *handle,
                                         const uint8_t *data,
                                         uintptr_t len,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Embed a file attachment into the document.
 */
int32_t document_editor_embed_file(DocumentEditor *handle,
                                   const char *name,
                                   const uint8_t *data,
                                   uintptr_t len,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Apply redactions on a single page (burn them in).
 */
int32_t document_editor_apply_page_redactions(DocumentEditor *handle,
                                              uintptr_t page,
                                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Apply all pending redactions across the entire document.
 */
int32_t document_editor_apply_all_redactions(DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Queue an explicit destructive redaction rectangle on `page`
 * (#231). Coordinates and colour are page user-space / DeviceRGB.
 * Returns 0 on success, -1 on error. Matches the symbol the Node
 * `editing-manager` already calls.
 */
int32_t pdf_redaction_add(DocumentEditor *handle,
                          uintptr_t page,
                          double x1,
                          double y1,
                          double x2,
                          double y2,
                          double r,
                          double g,
                          double b,
                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Number of queued redaction regions for `page` (annotations +
 * programmatic). Returns the count, or -1 on error.
 */
int32_t pdf_redaction_count(DocumentEditor *handle, uintptr_t page, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Destructively apply all queued redactions (true content removal +
 * opaque overlay, ISO 32000-1 §12.5.6.23). `scrub_metadata` reserved
 * for the document-scrub pass. Returns the number of glyphs physically
 * removed, or -1 on error (e.g. composite-font content — fail closed).
 */
int32_t pdf_redaction_apply(DocumentEditor *handle,
                            bool scrub_metadata,
                            double r,
                            double g,
                            double b,
                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Standalone document sanitization without geometric redaction
 * (#231 T10): strips `/Info`, catalog XMP `/Metadata`, document
 * JavaScript (`/OpenAction`, `/AA`, `/Names/JavaScript`)
 * `/Names/EmbeddedFiles`; the removed subtrees are hard-excluded from
 * the output (G6). Returns the number of top-level constructs removed,
 * or -1 on error.
 */
int32_t pdf_redaction_scrub_metadata(DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Rotate all pages by `degrees` (relative, added to existing rotation).
 */
int32_t document_editor_rotate_all_pages(DocumentEditor *handle,
                                         int32_t degrees,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Rotate a single page by `degrees` (additive, not absolute).
 */
int32_t document_editor_rotate_page_by(DocumentEditor *handle,
                                       uintptr_t page,
                                       int32_t degrees,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get the MediaBox for a page. Returns [x, y, width, height] in f64.
 */
int32_t document_editor_get_page_media_box(DocumentEditor *handle,
                                           uintptr_t page,
                                           double *x,
                                           double *y,
                                           double *w,
                                           double *h,
                                           int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the MediaBox for a page.
 */
int32_t document_editor_set_page_media_box(DocumentEditor *handle,
                                           uintptr_t page,
                                           double x,
                                           double y,
                                           double w,
                                           double h,
                                           int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get the CropBox for a page. Writes 0,0,0,0 if no CropBox is set.
 */
int32_t document_editor_get_page_crop_box(DocumentEditor *handle,
                                          uintptr_t page,
                                          double *x,
                                          double *y,
                                          double *w,
                                          double *h,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the CropBox for a page.
 */
int32_t document_editor_set_page_crop_box(DocumentEditor *handle,
                                          uintptr_t page,
                                          double x,
                                          double y,
                                          double w,
                                          double h,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Erase multiple rectangular regions on a page.
 * `rects` is a flat array of [x, y, w, h] quads (f64).
 * `rects_count` is the number of rectangles (not the array length).
 */
int32_t document_editor_erase_regions(DocumentEditor *handle,
                                      uintptr_t page,
                                      const double *rects,
                                      uintptr_t rects_count,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Clear all pending erase-region entries for a page.
 */
int32_t document_editor_clear_erase_regions(DocumentEditor *handle,
                                            uintptr_t page,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Returns 1 if the page is marked for annotation-flatten, 0 if not, -1 on error.
 */
int32_t document_editor_is_page_marked_for_flatten(const DocumentEditor *handle, uintptr_t page);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Remove the flatten mark from a page.
 */
int32_t document_editor_unmark_page_for_flatten(DocumentEditor *handle,
                                                uintptr_t page,
                                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Returns 1 if the page is marked for redaction, 0 if not, -1 on error.
 */
int32_t document_editor_is_page_marked_for_redaction(const DocumentEditor *handle, uintptr_t page);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Remove the redaction mark from a page.
 */
int32_t document_editor_unmark_page_for_redaction(DocumentEditor *handle,
                                                  uintptr_t page,
                                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *pdf_from_markdown(const char *markdown, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *pdf_from_html(const char *html, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *pdf_from_text(const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_save(Pdf *handle, const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *pdf_save_to_bytes(Pdf *handle, int32_t *data_len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_get_page_count(Pdf *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_free(Pdf *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiSearchResults *pdf_document_search_page(PdfDocument *handle,
                                           int32_t page_index,
                                           const char *search_term,
                                           bool case_sensitive,
                                           int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiSearchResults *pdf_document_search_all(PdfDocument *handle,
                                          const char *search_term,
                                          bool case_sensitive,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_search_result_count(const FfiSearchResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_search_result_get_text(const FfiSearchResults *results,
                                       int32_t index,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_search_result_get_page(const FfiSearchResults *results,
                                         int32_t index,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_search_result_get_bbox(const FfiSearchResults *results,
                                      int32_t index,
                                      float *x,
                                      float *y,
                                      float *width,
                                      float *height,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_search_result_free(FfiSearchResults *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiFontList *pdf_document_get_embedded_fonts(PdfDocument *handle,
                                             int32_t page_index,
                                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_font_count(const FfiFontList *fonts);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_font_get_name(const FfiFontList *fonts, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_font_get_type(const FfiFontList *fonts, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_font_get_encoding(const FfiFontList *fonts, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_font_is_embedded(const FfiFontList *fonts, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_font_is_subset(const FfiFontList *fonts, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_oxide_font_get_size(const FfiFontList *_fonts, int32_t _index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_font_list_free(FfiFontList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiImageList *pdf_document_get_embedded_images(PdfDocument *handle,
                                               int32_t page_index,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_image_count(const FfiImageList *images);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_image_get_width(const FfiImageList *images, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_image_get_height(const FfiImageList *images, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_image_get_format(const FfiImageList *images, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_image_get_colorspace(const FfiImageList *images,
                                     int32_t index,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_image_get_bits_per_component(const FfiImageList *images,
                                               int32_t index,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *pdf_oxide_image_get_data(const FfiImageList *images,
                                  int32_t index,
                                  int32_t *data_len,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_image_list_free(FfiImageList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiAnnotationList *pdf_document_get_page_annotations(PdfDocument *handle,
                                                     int32_t page_index,
                                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_annotation_count(const FfiAnnotationList *annotations);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_annotation_get_type(const FfiAnnotationList *annotations,
                                    int32_t index,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_annotation_get_content(const FfiAnnotationList *annotations,
                                       int32_t index,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_annotation_get_rect(const FfiAnnotationList *annotations,
                                   int32_t index,
                                   float *x,
                                   float *y,
                                   float *width,
                                   float *height,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_annotation_list_free(FfiAnnotationList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_annotation_get_subtype(const FfiAnnotationList *annotations,
                                       int32_t index,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_annotation_is_marked_deleted(const FfiAnnotationList *_annotations,
                                            int32_t _index,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int64_t pdf_oxide_annotation_get_creation_date(const FfiAnnotationList *annotations,
                                               int32_t index,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int64_t pdf_oxide_annotation_get_modification_date(const FfiAnnotationList *annotations,
                                                   int32_t index,
                                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_annotation_get_author(const FfiAnnotationList *annotations,
                                      int32_t index,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_oxide_annotation_get_border_width(const FfiAnnotationList *annotations,
                                            int32_t index,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint32_t pdf_oxide_annotation_get_color(const FfiAnnotationList *annotations,
                                        int32_t index,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_annotation_is_hidden(const FfiAnnotationList *annotations,
                                    int32_t index,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_annotation_is_printable(const FfiAnnotationList *annotations,
                                       int32_t index,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_annotation_is_read_only(const FfiAnnotationList *annotations,
                                       int32_t index,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_link_annotation_get_uri(const FfiAnnotationList *annotations,
                                        int32_t index,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_text_annotation_get_icon_name(const FfiAnnotationList *_annotations,
                                              int32_t _index,
                                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_highlight_annotation_get_quad_points_count(const FfiAnnotationList *annotations,
                                                             int32_t index,
                                                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_highlight_annotation_get_quad_point(const FfiAnnotationList *annotations,
                                                   int32_t index,
                                                   int32_t quad_index,
                                                   float *x1,
                                                   float *y1,
                                                   float *x2,
                                                   float *y2,
                                                   float *x3,
                                                   float *y3,
                                                   float *x4,
                                                   float *y4,
                                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_page_get_width(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_page_get_height(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_page_get_rotation(PdfDocument *_handle, int32_t _page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiElementList *pdf_page_get_elements(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_element_count(const FfiElementList *elements);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_element_get_type(const FfiElementList *_elements,
                                 int32_t _index,
                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_element_get_text(const FfiElementList *elements,
                                 int32_t index,
                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_element_get_rect(const FfiElementList *elements,
                                int32_t index,
                                float *x,
                                float *y,
                                float *width,
                                float *height,
                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_elements_free(FfiElementList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiBarcodeImage *pdf_generate_qr_code(const char *data,
                                      int32_t error_correction,
                                      int32_t size_px,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiBarcodeImage *pdf_generate_barcode(const char *data,
                                      int32_t format,
                                      int32_t size_px,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *pdf_barcode_get_image_png(const FfiBarcodeImage *barcode_handle,
                                   int32_t _size_px,
                                   int32_t *out_len,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_barcode_get_svg(const FfiBarcodeImage *barcode_handle,
                          int32_t _size_px,
                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_add_barcode_to_page(DocumentEditor *document_handle,
                                int32_t page_index,
                                const FfiBarcodeImage *barcode_handle,
                                float x,
                                float y,
                                float width,
                                float height,
                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_barcode_get_format(const FfiBarcodeImage *barcode_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_barcode_get_data(const FfiBarcodeImage *barcode_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_barcode_get_confidence(const FfiBarcodeImage *_barcode_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_barcode_free(FfiBarcodeImage *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_certificate_load_from_bytes(const uint8_t *cert_bytes,
                                      int32_t cert_len,
                                      const char *password,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Load signing credentials (certificate + private key) from PEM-encoded strings.
 * `cert_pem` and `key_pem` are NUL-terminated UTF-8 strings.
 * Returns an opaque handle on success, NULL on failure.
 */
void *pdf_certificate_load_from_pem(const char *cert_pem, const char *key_pem, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Sign the document loaded at `document_handle` in-place. The signature is
 * appended as a PDF incremental update so that subsequent saves (via
 * `pdf_save` / `pdf_save_to_bytes`) emit the signed file.
 *
 * `certificate_handle` must be a `SigningCredentials` pointer obtained from
 * one of the `pdf_certificate_load_*` functions.
 *
 * Returns 0 on success, -1 on failure (check `*error_code`).
 */
int32_t pdf_document_sign(void *document_handle,
                          const void *certificate_handle,
                          const char *reason,
                          const char *location,
                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Sign raw PDF bytes and return the signed PDF as a newly allocated buffer.
 *
 * The caller is responsible for freeing the returned buffer via `free_bytes`.
 * `*out_len` is set to the number of bytes in the returned buffer.
 *
 * Returns `NULL` on failure (check `*error_code`).
 */
uint8_t *pdf_sign_bytes(const uint8_t *pdf_data,
                        uintptr_t pdf_len,
                        const void *certificate_handle,
                        const char *reason,
                        const char *location,
                        uintptr_t *out_len,
                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_get_signature_count(const void *document_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_document_get_signature(const void *document_handle, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Run the signer-attributes crypto check (RSA-PKCS#1 v1.5 over
 * `signed_attrs`) on the CMS blob carried by a signature handle.
 *
 * Returns:
 * - `1` — Valid: signer held the private key matching the embedded
 *           certificate. Callers still need to verify the
 *           `messageDigest` attribute against their document content
 *           hash for a full detached-signature claim — use
 *           `pdf_signature_verify_detached` which runs both checks.
 * - `0` — Invalid: CMS parsed but the RSA check failed (tampered
 *           attributes or wrong key).
 * - `-1` — Unknown or not supported: PSS / ECDSA / unrecognised
 *           digest OID / missing signed_attrs / structurally
 *           unparseable / feature not compiled.
 */
int32_t pdf_signature_verify(const void *signature_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Verify a PDF signature end-to-end: run the signer-attributes crypto
 * check and the RFC 5652 §11.2 `messageDigest` attribute check against
 * the caller-provided document bytes. `pdf_data` must be the full PDF
 * file — the signature handle's stored `/ByteRange` is used to extract
 * the segments that were actually signed.
 *
 * Returns:
 * - `1` — Valid: both the RSA-PKCS#1 v1.5 check and the messageDigest
 *           check passed. The signer is authentic and the document has
 *           not been tampered with since signing.
 * - `0` — Invalid: either the signer check or the messageDigest check
 *           failed. Callers can't distinguish "wrong signer" from
 *           "document tampered after signing" from this code alone.
 * - `-1` — Unknown or not supported: signer uses PSS / ECDSA / unknown
 *           digest, blob lacks `signed_attrs` / `messageDigest`,
 *           `/ByteRange` is malformed, or the feature is not compiled.
 */
int32_t pdf_signature_verify_detached(const void *signature_handle,
                                      const uint8_t *pdf_data,
                                      uintptr_t pdf_len,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_verify_all_signatures(const void *_document_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_signature_get_signer_name(const FfiSignatureInfo *sig, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int64_t pdf_signature_get_signing_time(const FfiSignatureInfo *sig, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_signature_get_signing_reason(const FfiSignatureInfo *sig, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_signature_get_signing_location(const FfiSignatureInfo *sig, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_signature_get_certificate(const void *sig, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_certificate_get_subject(const void *cert, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_certificate_get_issuer(const void *cert, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_certificate_get_serial(const void *cert, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_certificate_get_validity(const void *cert,
                                  int64_t *not_before,
                                  int64_t *not_after,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_certificate_is_valid(const void *cert, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_signature_free(FfiSignatureInfo *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Sign raw PDF bytes at a PAdES baseline level. `level`: 0=B-B 1=B-T
 * 2=B-LT (3=B-LTA ⇒ `ERR_UNSUPPORTED`). `tsa_url` must be non-null for
 * `level >= 1` (the RFC 3161 timestamp source); requires the
 * `tsa-client` feature, else `ERR_UNSUPPORTED`. The three parallel
 * arrays carry the B-LT revocation material (DER certs/CRLs/OCSPs).
 *
 * Returns a `malloc`'d buffer (free with [`free_bytes`]) / `NULL` on
 * failure (check `*error_code`).
 */
uint8_t *pdf_sign_bytes_pades(const uint8_t *pdf_data,
                              uintptr_t pdf_len,
                              const void *certificate_handle,
                              int32_t level,
                              const char *tsa_url,
                              const char *reason,
                              const char *location,
                              const uint8_t *const *certs,
                              const uintptr_t *cert_lens,
                              uintptr_t n_certs,
                              const uint8_t *const *crls,
                              const uintptr_t *crl_lens,
                              uintptr_t n_crls,
                              const uint8_t *const *ocsps,
                              const uintptr_t *ocsp_lens,
                              uintptr_t n_ocsps,
                              uintptr_t *out_len,
                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Struct-options variant of [`pdf_sign_bytes_pades`] (5 arguments).
 * Functionally identical — it unpacks `options` and delegates to
 * `pdf_sign_bytes_pades`, so behaviour, error codes and the returned
 * buffer are byte-for-byte the same. Additive: the 18-argument
 * function is unchanged for existing C/C++/C#/Node callers.
 *
 * # Safety
 * `options` must be a valid pointer to a `PadesSignOptionsC` whose
 * pointer fields are valid for the duration of the call (same
 * contract as the scalar function).
 */
uint8_t *pdf_sign_bytes_pades_opts(const uint8_t *pdf_data,
                                   uintptr_t pdf_len,
                                   const PadesSignOptionsC *options,
                                   uintptr_t *out_len,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Classify a signature's PAdES level from its CMS attributes alone
 * (B-B vs B-T). B-LT additionally needs the document `/DSS` — read it
 * via [`pdf_document_get_dss`] and inspect its VRI. Returns the frozen
 * `PadesLevel` code, or `-1` on error.
 */
int32_t pdf_signature_get_pades_level(const void *signature_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Read the document `/DSS` into an opaque handle. Returns `NULL` with
 * `*error_code == ERR_SUCCESS` when the document simply has no DSS
 * (not an error); `NULL` with a non-zero code on a real failure. Free
 * the handle with [`pdf_dss_free`].
 */
void *pdf_document_get_dss(const void *document_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Whether the document carries a document-scoped RFC 3161
 * `/DocTimeStamp` archival timestamp (PAdES-B-LTA, ISO 32000-2:2020
 * §12.8.5). This is the document-level reader signal that
 * `pdf_signature_get_pades_level` cannot report — that call is
 * signature-scoped and tops out at B-LT by design. Returns `1` when a
 * document timestamp is present, `0` when not, `-1` on a null handle
 * or when the `signatures` feature is disabled.
 */
int32_t pdf_document_has_timestamp(const void *document_handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_dss_cert_count(const void *_dss);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_dss_crl_count(const void *_dss);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_dss_ocsp_count(const void *_dss);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_dss_vri_count(const void *_dss);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *pdf_dss_get_cert(const void *_dss,
                          int32_t _index,
                          uintptr_t *_out_len,
                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *pdf_dss_get_crl(const void *_dss,
                         int32_t _index,
                         uintptr_t *_out_len,
                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *pdf_dss_get_ocsp(const void *_dss,
                          int32_t _index,
                          uintptr_t *_out_len,
                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free a DSS handle from [`pdf_document_get_dss`].
 */
void pdf_dss_free(void *dss);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_certificate_free(void *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_estimate_render_time(const void *_doc, int32_t _page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_create_renderer(int32_t _dpi,
                          int32_t _format,
                          int32_t _quality,
                          bool _anti_alias,
                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiRenderedImage *pdf_render_page(PdfDocument *doc,
                                  int32_t page_index,
                                  int32_t format,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Render a page with the full RenderOptions surface exposed to C callers.
 *
 * All four background channels are 0.0..=1.0; set `transparent_background`
 * to 1 to drop the fill entirely (matches Rust's
 * `RenderOptions { background: None, .. }`).
 *
 * Mirrors the Python surface added in gap O and the C# RenderOptions class
 * added in gap B. Rust implementation is the single source of truth.
 */
FfiRenderedImage *pdf_render_page_with_options(PdfDocument *doc,
                                               int32_t page_index,
                                               int32_t dpi,
                                               int32_t format,
                                               float bg_r,
                                               float bg_g,
                                               float bg_b,
                                               float bg_a,
                                               int32_t transparent_background,
                                               int32_t render_annotations,
                                               int32_t jpeg_quality,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Render a page with the full RenderOptions surface plus OCG layer filtering.
 *
 * `excluded_layers` is a pointer to an array of `excluded_layers_count` null-
 * terminated UTF-8 C strings. Each string is the `/Name` of an Optional
 * Content Group (OCG) to suppress. Pass a null pointer or zero count to
 * disable filtering (matches `pdf_render_page_with_options` behaviour).
 *
 * The renderer also honours OCMD references that resolve to any of the named
 * OCGs, per ISO 32000-1 §8.11.2.
 */
FfiRenderedImage *pdf_render_page_with_options_ex(PdfDocument *doc,
                                                  int32_t page_index,
                                                  int32_t dpi,
                                                  int32_t format,
                                                  float bg_r,
                                                  float bg_g,
                                                  float bg_b,
                                                  float bg_a,
                                                  int32_t transparent_background,
                                                  int32_t render_annotations,
                                                  int32_t jpeg_quality,
                                                  const char *const *excluded_layers,
                                                  uintptr_t excluded_layers_count,
                                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Render a rectangular region of a page. `crop_*` are in PDF user-space
 * points (origin bottom-left). Format: 0=PNG, 1=JPEG.
 */
FfiRenderedImage *pdf_render_page_region(PdfDocument *doc,
                                         int32_t page_index,
                                         float crop_x,
                                         float crop_y,
                                         float crop_width,
                                         float crop_height,
                                         int32_t format,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiRenderedImage *pdf_render_page_zoom(PdfDocument *doc,
                                       int32_t page_index,
                                       float zoom,
                                       int32_t format,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Render a page to fit inside `w`×`h` pixels, preserving aspect ratio.
 */
FfiRenderedImage *pdf_render_page_fit(PdfDocument *doc,
                                      int32_t page_index,
                                      int32_t w,
                                      int32_t h,
                                      int32_t format,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiRenderedImage *pdf_render_page_thumbnail(PdfDocument *doc,
                                            int32_t page_index,
                                            int32_t _size,
                                            int32_t format,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Render a page and return the raw premultiplied RGBA8888 pixel buffer.
 *
 * The caller retrieves the pixel bytes via `pdf_get_rendered_image_data`
 * the dimensions via `out_width`/`out_height` (set on success). Pixels are
 * row-major, top-left origin; `data_len == *out_width * *out_height * 4`.
 * Free the returned handle with `pdf_rendered_image_free`.
 *
 * Returns null on error; check `error_code`. `out_width` and `out_height`
 * are only written when the return value is non-null.
 */
FfiRenderedImage *pdf_render_page_raw(PdfDocument *doc,
                                      int32_t page_index,
                                      int32_t dpi,
                                      int32_t *out_width,
                                      int32_t *out_height,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_get_rendered_image_width(const FfiRenderedImage *img, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_get_rendered_image_height(const FfiRenderedImage *img, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *pdf_get_rendered_image_data(const FfiRenderedImage *img,
                                     int32_t *data_len,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_save_rendered_image(const FfiRenderedImage *img,
                                const char *file_path,
                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_rendered_image_free(FfiRenderedImage *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_renderer_free(void *_handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_tsa_client_create(const char *url,
                            const char *username,
                            const char *password,
                            int32_t timeout,
                            int32_t hash_algo,
                            bool use_nonce,
                            bool cert_req,
                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_tsa_client_free(void *client);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_tsa_request_timestamp(const void *client,
                                const uint8_t *data,
                                uintptr_t data_len,
                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_tsa_request_timestamp_hash(const void *client,
                                     const uint8_t *hash,
                                     uintptr_t hash_len,
                                     int32_t hash_algo,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Parse a DER-encoded RFC 3161 TimeStampToken (or bare TSTInfo) into
 * an owned `Timestamp` handle. Every `pdf_timestamp_*` accessor below
 * is cheap O(1) once the handle exists.
 */
void *pdf_timestamp_parse(const uint8_t *bytes, uintptr_t len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
const uint8_t *pdf_timestamp_get_token(const void *ts, uintptr_t *out_len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int64_t pdf_timestamp_get_time(const void *ts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_timestamp_get_serial(const void *ts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_timestamp_get_tsa_name(const void *ts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_timestamp_get_policy_oid(const void *ts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_timestamp_get_hash_algorithm(const void *ts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
const uint8_t *pdf_timestamp_get_message_imprint(const void *ts,
                                                 uintptr_t *out_len,
                                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_timestamp_verify(const void *ts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_timestamp_free(void *ts);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_signature_add_timestamp(const void *_sig, const void *_ts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_signature_has_timestamp(const void *_sig, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void *pdf_signature_get_timestamp(const void *_sig, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_add_timestamp(const uint8_t *_pdf_data,
                       uintptr_t _pdf_len,
                       int32_t _sig_index,
                       const char *_tsa_url,
                       uint8_t **_out_data,
                       uintptr_t *_out_len,
                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiUaResults *pdf_validate_pdf_ua(PdfDocument *document, int32_t level, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_pdf_ua_is_accessible(const FfiUaResults *results, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_pdf_ua_error_count(const FfiUaResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_pdf_ua_get_error(const FfiUaResults *results, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_pdf_ua_warning_count(const FfiUaResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_pdf_ua_get_warning(const FfiUaResults *results, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_pdf_ua_get_stats(const FfiUaResults *results,
                          int32_t *out_struct,
                          int32_t *out_images,
                          int32_t *out_tables,
                          int32_t *out_forms,
                          int32_t *out_annotations,
                          int32_t *out_pages,
                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_pdf_ua_results_free(FfiUaResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_form_import_from_file(const void *_document, const char *_filename, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_import_form_data(const void *_document,
                                      const char *_data_path,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_editor_import_fdf_bytes(const void *_document,
                                    const uint8_t *_data,
                                    uintptr_t _data_len,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_editor_import_xfdf_bytes(const void *_document,
                                     const uint8_t *_data,
                                     uintptr_t _data_len,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Export form data from a PdfDocument. format_type: 0=FDF, 1=XFDF
 */
uint8_t *pdf_document_export_form_data_to_bytes(PdfDocument *document,
                                                int32_t format_type,
                                                uintptr_t *out_len,
                                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a PDF document from a byte buffer.
 */
PdfDocument *pdf_document_open_from_bytes(const uint8_t *data, uintptr_t len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a PDF with password.
 */
PdfDocument *pdf_document_open_with_password(const char *path,
                                             const char *password,
                                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Check if the document is encrypted.
 */
bool pdf_document_is_encrypted(const PdfDocument *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Authenticate with password.
 */
bool pdf_document_authenticate(PdfDocument *handle, const char *password, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_document_extract_all_text(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_document_to_html_all(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_document_to_plain_text_all(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiCharList *pdf_document_extract_chars(PdfDocument *handle,
                                        int32_t page_index,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_char_count(const FfiCharList *chars);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint32_t pdf_oxide_char_get_char(const FfiCharList *chars, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_char_get_bbox(const FfiCharList *chars,
                             int32_t index,
                             float *x,
                             float *y,
                             float *w,
                             float *h,
                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_char_get_font_name(const FfiCharList *chars, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_oxide_char_get_font_size(const FfiCharList *chars, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_char_list_free(FfiCharList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiWordList *pdf_document_extract_words(PdfDocument *handle,
                                        int32_t page_index,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_word_count(const FfiWordList *words);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_word_get_text(const FfiWordList *words, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_word_get_bbox(const FfiWordList *words,
                             int32_t index,
                             float *x,
                             float *y,
                             float *w,
                             float *h,
                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_word_get_font_name(const FfiWordList *words, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_oxide_word_get_font_size(const FfiWordList *words, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_word_is_bold(const FfiWordList *words, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_word_list_free(FfiWordList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiTextLineList *pdf_document_extract_text_lines(PdfDocument *handle,
                                                 int32_t page_index,
                                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_line_count(const FfiTextLineList *lines);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_line_get_text(const FfiTextLineList *lines, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_line_get_bbox(const FfiTextLineList *lines,
                             int32_t index,
                             float *x,
                             float *y,
                             float *w,
                             float *h,
                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_line_get_word_count(const FfiTextLineList *lines,
                                      int32_t index,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_line_list_free(FfiTextLineList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiTableList *pdf_document_extract_tables(PdfDocument *handle,
                                          int32_t page_index,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_table_count(const FfiTableList *tables);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_table_get_row_count(const FfiTableList *tables,
                                      int32_t index,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_table_get_col_count(const FfiTableList *tables,
                                      int32_t index,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_table_get_cell_text(const FfiTableList *tables,
                                    int32_t table_index,
                                    int32_t row,
                                    int32_t col,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_table_has_header(const FfiTableList *tables, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_table_list_free(FfiTableList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_document_extract_text_in_rect(PdfDocument *handle,
                                        int32_t page_index,
                                        float x,
                                        float y,
                                        float w,
                                        float h,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiWordList *pdf_document_extract_words_in_rect(PdfDocument *handle,
                                                int32_t page_index,
                                                float x,
                                                float y,
                                                float w,
                                                float h,
                                                int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiTextLineList *pdf_document_extract_lines_in_rect(PdfDocument *handle,
                                                    int32_t page_index,
                                                    float x,
                                                    float y,
                                                    float w,
                                                    float h,
                                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiTableList *pdf_document_extract_tables_in_rect(PdfDocument *handle,
                                                  int32_t page_index,
                                                  float x,
                                                  float y,
                                                  float w,
                                                  float h,
                                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiImageList *pdf_document_extract_images_in_rect(PdfDocument *handle,
                                                  int32_t page_index,
                                                  float x,
                                                  float y,
                                                  float w,
                                                  float h,
                                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiFormFieldList *pdf_document_get_form_fields(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_form_field_count(const FfiFormFieldList *fields);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_form_field_get_name(const FfiFormFieldList *fields,
                                    int32_t index,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_form_field_get_type(const FfiFormFieldList *fields,
                                    int32_t index,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_oxide_form_field_get_value(const FfiFormFieldList *fields,
                                     int32_t index,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_form_field_is_readonly(const FfiFormFieldList *fields,
                                      int32_t index,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_form_field_is_required(const FfiFormFieldList *fields,
                                      int32_t index,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_form_field_list_free(FfiFormFieldList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Check if document has XFA forms.
 */
bool pdf_document_has_xfa(PdfDocument *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_remove_headers(PdfDocument *handle, float threshold, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_remove_footers(PdfDocument *handle, float threshold, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_remove_artifacts(PdfDocument *handle, float threshold, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_erase_header(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_erase_footer(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_document_erase_artifacts(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_delete_page(DocumentEditor *handle,
                                    int32_t page_index,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_move_page(DocumentEditor *handle,
                                  int32_t from,
                                  int32_t to,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_get_page_rotation(DocumentEditor *handle,
                                          int32_t page,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_set_page_rotation(DocumentEditor *handle,
                                          int32_t page,
                                          int32_t degrees,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_erase_region(DocumentEditor *handle,
                                     int32_t page,
                                     float x,
                                     float y,
                                     float w,
                                     float h,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_flatten_annotations(DocumentEditor *handle,
                                            int32_t page,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_flatten_all_annotations(DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_crop_margins(DocumentEditor *handle,
                                     float left,
                                     float right,
                                     float top,
                                     float bottom,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_merge_from(DocumentEditor *handle,
                                   const char *source_path,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *pdf_from_image(const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *pdf_from_image_bytes(const uint8_t *data, int32_t data_len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Merge multiple PDFs. paths is a null-terminated array of C strings.
 */
uint8_t *pdf_merge(const char *const *paths,
                   int32_t path_count,
                   int32_t *data_len,
                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiPdfAResults *pdf_validate_pdf_a_level(PdfDocument *document, int32_t level, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_convert_to_pdf_a(PdfDocument *document, int32_t level, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Return a copy of the document's current source bytes (after any in-place
 * conversion). `out_len` receives the byte count. Free with `free_bytes`.
 */
uint8_t *pdf_document_get_source_bytes(PdfDocument *document,
                                       uintptr_t *out_len,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_pdf_a_is_compliant(const FfiPdfAResults *results, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_pdf_a_error_count(const FfiPdfAResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_pdf_a_get_error(const FfiPdfAResults *results, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_pdf_a_warning_count(const FfiPdfAResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_pdf_a_results_free(FfiPdfAResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiPdfXResults *pdf_validate_pdf_x_level(PdfDocument *document, int32_t level, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_pdf_x_is_compliant(const FfiPdfXResults *results, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_pdf_x_error_count(const FfiPdfXResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *pdf_pdf_x_get_error(const FfiPdfXResults *results, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_pdf_x_results_free(FfiPdfXResults *results);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t document_editor_save_encrypted(DocumentEditor *handle,
                                       const char *path,
                                       const char *user_password,
                                       const char *owner_password,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
FfiPathList *pdf_document_extract_paths(PdfDocument *handle,
                                        int32_t page_index,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_path_count(const FfiPathList *paths);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_path_get_bbox(const FfiPathList *paths,
                             int32_t index,
                             float *x,
                             float *y,
                             float *w,
                             float *h,
                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
float pdf_oxide_path_get_stroke_width(const FfiPathList *paths, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_path_has_stroke(const FfiPathList *paths, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
bool pdf_oxide_path_has_fill(const FfiPathList *paths, int32_t index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t pdf_oxide_path_get_operation_count(const FfiPathList *paths,
                                           int32_t index,
                                           int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void pdf_oxide_path_list_free(FfiPathList *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get page labels as JSON: [{"start":0,"prefix":"","style":"Decimal","first":1}, ...]
 */
char *pdf_document_get_page_labels(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get XMP metadata as JSON
 */
char *pdf_document_get_xmp_metadata(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Get document outline (bookmarks) as JSON
 */
char *pdf_document_get_outline(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Plan a bookmark split. `options_json` is `{}`-tolerant
 * (`title_prefix`, `ignore_case`, `level` [0=all,1=top],
 * `include_front_matter`). Returns a malloc'd JSON array of segment
 * objects (free via the existing string-free). On any failure sets
 * `error_code` (non-zero) and returns null — errors are surfaced, not
 * swallowed (foundation §3.3; this is not pure read-extraction).
 */
char *pdf_document_plan_split_by_bookmarks(PdfDocument *handle,
                                           const char *options_json,
                                           int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Cheap per-page text-vs-OCR classification (no OCR, no rasterisation).
 * Returns a malloc'd JSON `PageClassification`
 * (`{page,kind,confidence,reason,signals}`); free via the string-free.
 * On failure sets `error_code` non-zero and returns null.
 */
char *pdf_document_classify_page(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Cheap whole-document classification (per-page kinds +
 * `pages_needing_ocr` + aggregate summary). Malloc'd JSON
 * `DocumentClassification`; free via the string-free.
 */
char *pdf_document_classify_document(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * One-shot auto text extraction (`AutoExtractor::new()`): per-page,
 * auto-routed text-vs-OCR with graceful native fallback (never the
 * opaque OCR error — #513). Malloc'd UTF-8 string; free via the
 * string-free.
 */
char *pdf_document_extract_text_auto(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Rich per-page extraction. `options_json` is `{}`-tolerant
 * (`AutoExtractOptions`: `mode`, `reconstruct_image_tables`,
 * `emit_placeholders`, `ocr_languages`, `min_text_confidence`,
 * `table_confidence`, `force_ocr_pages`); NULL/empty → defaults.
 * Returns a malloc'd JSON `PageExtraction` (per-region bbox + typed
 * reason — never bare-empty). Free via the string-free.
 */
char *pdf_document_extract_page_auto(PdfDocument *handle,
                                     int32_t page_index,
                                     const char *options_json,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Build-time OCR model provisioning (#519, cross-binding). Downloads
 * the shared detector + the recognition model/dict for each requested
 * language into the model cache dir (`$PDF_OXIDE_MODEL_DIR` / the
 * platform cache) and returns that dir as a malloc'd UTF-8 string
 * (free via `free_string`). `languages_csv` is comma-separated
 * language codes (e.g. `"english,chinese,arabic"`); NULL/empty →
 * English. Unknown codes are skipped. Idempotent. Actual download
 * requires the library built with the `ocr` feature; without it the
 * cache dir is still created (no fetch) — query
 * `pdf_oxide_prefetch_available()`.
 */
char *pdf_oxide_prefetch_models(const char *languages_csv, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Air-gapped OCR model manifest — JSON (detector + every supported
 * language's cache filenames and source URLs). Malloc'd; free via
 * `free_string`. Never errors.
 */
char *pdf_oxide_model_manifest(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Whether this build can actually download models (compiled with the
 * `ocr` feature). `1` = downloads work; `0` =
 * `pdf_oxide_prefetch_models` only creates the cache dir (no fetch).
 */
int32_t pdf_oxide_prefetch_available(void);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set a form field value on a DocumentEditor. Value is a UTF-8 string.
 */
int32_t document_editor_set_form_field_value(DocumentEditor *handle,
                                             const char *name,
                                             const char *value,
                                             int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Flatten all forms in the document (bake form values into page content).
 */
int32_t document_editor_flatten_forms(DocumentEditor *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Flatten forms on a specific page.
 */
int32_t document_editor_flatten_forms_on_page(DocumentEditor *handle,
                                              int32_t page_index,
                                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Return the number of warnings collected during the last form-flattening save.
 *
 * Each warning names a widget field that had no `/AP` appearance stream.
 * Returns -1 if `handle` is null.
 */
int32_t document_editor_flatten_warnings_count(const DocumentEditor *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Return the `index`-th flatten warning as a C string owned by the caller.
 *
 * The returned string must be freed with `free_string`. Returns null if
 * `handle` is null or `index` is out of range.
 */
char *document_editor_flatten_warning(const DocumentEditor *handle,
                                      int32_t index,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
PdfDocument *PdfDocumentOpen(const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void PdfDocumentFree(PdfDocument *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t PdfDocumentGetPageCount(PdfDocument *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *PdfDocumentExtractText(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *PdfDocumentToMarkdown(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *PdfDocumentToHtml(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *PdfDocumentToPlainText(PdfDocument *handle, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *PdfFromMarkdown(const char *markdown, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *PdfFromHtml(const char *html, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
Pdf *PdfFromText(const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t PdfSave(Pdf *handle, const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
uint8_t *PdfSaveToBytes(Pdf *handle, int32_t *data_len, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void PdfFree(Pdf *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
DocumentEditor *DocumentEditorOpen(const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void DocumentEditorFree(DocumentEditor *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t DocumentEditorSave(DocumentEditor *handle, const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t DocumentEditorSetTitle(DocumentEditor *handle, const char *value, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
int32_t DocumentEditorSetAuthor(DocumentEditor *handle, const char *value, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void FreeString(char *ptr);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
void FreeBytes(uint8_t *ptr);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
char *AllocString(const char *s);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Serializes an entire font list to JSON. Returns a UTF-8 C string owned by
 * the caller (free with `free_string`). The schema is
 * `[{"name": "...", "type": "...", "encoding": "...", "isEmbedded": bool,
 * "isSubset": bool, "size": number}, ...]`.
 */
char *pdf_oxide_fonts_to_json(const FfiFontList *fonts, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Serializes an entire annotation list to JSON. Returns a UTF-8 C string
 * owned by the caller (free with `free_string`). The schema matches the Go
 * `Annotation` struct fields.
 */
char *pdf_oxide_annotations_to_json(const FfiAnnotationList *annotations, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Serializes an entire element list (text spans) to JSON. Returns a UTF-8 C
 * string owned by the caller (free with `free_string`).
 */
char *pdf_oxide_elements_to_json(const FfiElementList *elements, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Serializes an entire search-results list to JSON. Returns a UTF-8 C string
 * owned by the caller (free with `free_string`).
 */
char *pdf_oxide_search_results_to_json(const FfiSearchResults *results, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Create an OCR engine from model/dictionary file paths.
 * Returns an opaque handle (`Box<OcrEngine>`) that must be freed with
 * `pdf_ocr_engine_free`. On failure returns null and sets `error_code`.
 */
void *pdf_ocr_engine_create(const char *det_model_path,
                            const char *rec_model_path,
                            const char *dict_path,
                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free an OCR engine handle created by `pdf_ocr_engine_create`.
 */
void pdf_ocr_engine_free(void *engine);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Check whether a page needs OCR (i.e. is scanned/hybrid).
 * Returns false and sets `error_code` to `_ERR_UNSUPPORTED` when the `ocr`
 * feature is disabled.
 */
bool pdf_ocr_page_needs_ocr(PdfDocument *doc, int32_t page_index, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Extract text from a page using OCR. `engine` may be null (will use native
 * text extraction only). The returned C string must be freed with `free_string`.
 */
char *pdf_ocr_extract_text(PdfDocument *doc,
                           int32_t page_index,
                           const void *engine,
                           int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Load a TTF / OTF font from a file path. Returns an opaque handle or
 * NULL on error.
 */
EmbeddedFont *pdf_embedded_font_from_file(const char *path, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Load a font from a byte buffer. `name` may be NULL to use the
 * PostScript name from the font face.
 */
EmbeddedFont *pdf_embedded_font_from_bytes(const uint8_t *data,
                                           uintptr_t len,
                                           const char *name,
                                           int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free an `EmbeddedFont` handle. No-op on NULL. Do not call after
 * a successful `pdf_document_builder_register_embedded_font` —
 * the builder has taken ownership.
 */
void pdf_embedded_font_free(EmbeddedFont *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Create a new `DocumentBuilder`. Never fails but keeps the
 * error_code signature for uniformity.
 */
FfiDocumentBuilder *pdf_document_builder_create(int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Free a `DocumentBuilder` handle without building. Safe to call on
 * an already-consumed handle — it'll just drop the (empty) wrapper.
 */
void pdf_document_builder_free(FfiDocumentBuilder *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the document title. Returns 0 on success, -1 on error.
 */
int32_t pdf_document_builder_set_title(FfiDocumentBuilder *handle,
                                       const char *title,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the document author. Returns 0 on success, -1 on error.
 */
int32_t pdf_document_builder_set_author(FfiDocumentBuilder *handle,
                                        const char *author,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the document subject.
 */
int32_t pdf_document_builder_set_subject(FfiDocumentBuilder *handle,
                                         const char *subject,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the document keywords (comma-separated).
 */
int32_t pdf_document_builder_set_keywords(FfiDocumentBuilder *handle,
                                          const char *keywords,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the creator application name.
 */
int32_t pdf_document_builder_set_creator(FfiDocumentBuilder *handle,
                                         const char *creator,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Run JavaScript when the document is opened (`/OpenAction`).
 */
int32_t pdf_document_builder_on_open(FfiDocumentBuilder *handle,
                                     const char *script,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Enable PDF/UA-1 tagged PDF mode. Returns 0 on success, -1 on error.
 *
 * When enabled, `pdf_document_builder_build` emits `/MarkInfo`,
 * `/StructTreeRoot`, `/Lang`, and `/ViewerPreferences` in the catalog.
 * Opt-in — no effect unless called. Bundle F-1/F-2.
 */
int32_t pdf_document_builder_tagged_pdf_ua1(FfiDocumentBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the document's natural language tag (e.g. "en-US"). Returns 0 on success.
 *
 * Emitted as `/Lang` in the catalog when `pdf_document_builder_tagged_pdf_ua1`
 * has been called. Bundle F-2.
 */
int32_t pdf_document_builder_language(FfiDocumentBuilder *handle,
                                      const char *lang,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add a role-map entry: custom structure type → standard PDF structure type.
 * Returns 0 on success, -1 on error.
 *
 * Emitted in `/RoleMap` inside the StructTreeRoot when
 * `pdf_document_builder_tagged_pdf_ua1` is set. Multiple calls accumulate
 * entries. Bundle F-4.
 */
int32_t pdf_document_builder_role_map(FfiDocumentBuilder *handle,
                                      const char *custom,
                                      const char *standard,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Register a TTF / OTF font. **Consumes** the `font` handle — on
 * success, callers must not call `pdf_embedded_font_free` on it.
 * On error the font handle is NOT consumed and remains valid.
 */
int32_t pdf_document_builder_register_embedded_font(FfiDocumentBuilder *handle,
                                                    const char *name,
                                                    EmbeddedFont *font,
                                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Start an A4 page. Only one page at a time may be open per builder —
 * a second call while a page handle is outstanding returns NULL with
 * `ERR_INVALID_ARG`.
 */
FfiPageBuilder *pdf_document_builder_a4_page(FfiDocumentBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Start a US Letter page.
 */
FfiPageBuilder *pdf_document_builder_letter_page(FfiDocumentBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Start a page with custom dimensions in PDF points (72 pt = 1 inch).
 */
FfiPageBuilder *pdf_document_builder_page(FfiDocumentBuilder *handle,
                                          float width,
                                          float height,
                                          int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the font + size for subsequent text on this page.
 */
int32_t pdf_page_builder_font(FfiPageBuilder *handle,
                              const char *name,
                              float size,
                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Move the cursor to absolute coordinates (in PDF points, from lower-left).
 */
int32_t pdf_page_builder_at(FfiPageBuilder *handle, float x, float y, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Emit a line of text at the current cursor position, then advance
 * the cursor down by one line-height.
 */
int32_t pdf_page_builder_text(FfiPageBuilder *handle, const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Emit a heading with the given level (1–6) and text.
 */
int32_t pdf_page_builder_heading(FfiPageBuilder *handle,
                                 uint8_t level,
                                 const char *text,
                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Emit a paragraph with automatic line wrapping.
 */
int32_t pdf_page_builder_paragraph(FfiPageBuilder *handle, const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Advance the cursor down by `points`.
 */
int32_t pdf_page_builder_space(FfiPageBuilder *handle, float points, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Draw a horizontal rule across the page.
 */
int32_t pdf_page_builder_horizontal_rule(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Attach a URL link to the previously-emitted text element.
 */
int32_t pdf_page_builder_link_url(FfiPageBuilder *handle, const char *url, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Link the previous text to an internal page index (zero-based).
 */
int32_t pdf_page_builder_link_page(FfiPageBuilder *handle, uintptr_t page, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Link the previous text to a named destination.
 */
int32_t pdf_page_builder_link_named(FfiPageBuilder *handle,
                                    const char *destination,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Link the previous text to a JavaScript action.
 */
int32_t pdf_page_builder_link_javascript(FfiPageBuilder *handle,
                                         const char *script,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Run JavaScript when this page is opened (`/AA /O`).
 */
int32_t pdf_page_builder_on_open(FfiPageBuilder *handle, const char *script, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Run JavaScript when this page is closed (`/AA /C`).
 */
int32_t pdf_page_builder_on_close(FfiPageBuilder *handle, const char *script, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set a keystroke JS action (`/AA /K`) on the most-recently-added form field.
 */
int32_t pdf_page_builder_field_keystroke(FfiPageBuilder *handle,
                                         const char *script,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set a format JS action (`/AA /F`) on the most-recently-added form field.
 */
int32_t pdf_page_builder_field_format(FfiPageBuilder *handle,
                                      const char *script,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set a validate JS action (`/AA /V`) on the most-recently-added form field.
 */
int32_t pdf_page_builder_field_validate(FfiPageBuilder *handle,
                                        const char *script,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set a calculate JS action (`/AA /C`) on the most-recently-added form field.
 */
int32_t pdf_page_builder_field_calculate(FfiPageBuilder *handle,
                                         const char *script,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Highlight the previous text with an RGB colour (channels in 0.0–1.0).
 */
int32_t pdf_page_builder_highlight(FfiPageBuilder *handle,
                                   float r,
                                   float g,
                                   float b,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Underline the previous text.
 */
int32_t pdf_page_builder_underline(FfiPageBuilder *handle,
                                   float r,
                                   float g,
                                   float b,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Strikeout the previous text.
 */
int32_t pdf_page_builder_strikeout(FfiPageBuilder *handle,
                                   float r,
                                   float g,
                                   float b,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Squiggly-underline the previous text.
 */
int32_t pdf_page_builder_squiggly(FfiPageBuilder *handle,
                                  float r,
                                  float g,
                                  float b,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Attach a sticky-note annotation to the previous text.
 */
int32_t pdf_page_builder_sticky_note(FfiPageBuilder *handle, const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Place a free-standing sticky note at an absolute position on the page.
 */
int32_t pdf_page_builder_sticky_note_at(FfiPageBuilder *handle,
                                        float x,
                                        float y,
                                        const char *text,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Apply a text watermark to the entire page.
 */
int32_t pdf_page_builder_watermark(FfiPageBuilder *handle, const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Apply the standard "CONFIDENTIAL" diagonal watermark.
 */
int32_t pdf_page_builder_watermark_confidential(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Apply the standard "DRAFT" diagonal watermark.
 */
int32_t pdf_page_builder_watermark_draft(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Attach a standard stamp annotation at the current cursor position
 * (default 150×50 pt box). `type_name` matches the PDF spec's stamp
 * names (Approved, NotApproved, Draft, Confidential, Final,
 * Experimental, Expired, ForPublicRelease, NotForPublicRelease, AsIs,
 * Sold, Departmental, ForComment, TopSecret) — anything else becomes
 * a custom stamp with that text.
 */
int32_t pdf_page_builder_stamp(FfiPageBuilder *handle, const char *type_name, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Place a free-flowing text annotation inside the given rectangle.
 */
int32_t pdf_page_builder_freetext(FfiPageBuilder *handle,
                                  float x,
                                  float y,
                                  float w,
                                  float h,
                                  const char *text,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add a single-line text form field. `default_value` may be NULL for
 * a blank field; the initial value otherwise.
 */
int32_t pdf_page_builder_text_field(FfiPageBuilder *handle,
                                    const char *name,
                                    float x,
                                    float y,
                                    float w,
                                    float h,
                                    const char *default_value,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add a checkbox form field. `checked` is non-zero for initially-ticked.
 */
int32_t pdf_page_builder_checkbox(FfiPageBuilder *handle,
                                  const char *name,
                                  float x,
                                  float y,
                                  float w,
                                  float h,
                                  int32_t checked,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add a dropdown combo-box with a fixed list of string options.
 * `options` is an array of C-strings of length `options_count`.
 * `selected` may be NULL for no initial selection.
 */
int32_t pdf_page_builder_combo_box(FfiPageBuilder *handle,
                                   const char *name,
                                   float x,
                                   float y,
                                   float w,
                                   float h,
                                   const char *const *options,
                                   uintptr_t options_count,
                                   const char *selected,
                                   int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add a radio-button group. `values` / `xs` / `ys` / `ws` / `hs` are
 * parallel arrays of length `count` describing each button's export
 * value and rect. `selected` may be NULL.
 */
int32_t pdf_page_builder_radio_group(FfiPageBuilder *handle,
                                     const char *name,
                                     const char *const *values,
                                     const float *xs,
                                     const float *ys,
                                     const float *ws,
                                     const float *hs,
                                     uintptr_t count,
                                     const char *selected,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add a clickable push button with a visible caption.
 */
int32_t pdf_page_builder_push_button(FfiPageBuilder *handle,
                                     const char *name,
                                     float x,
                                     float y,
                                     float w,
                                     float h,
                                     const char *caption,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add an unsigned signature placeholder field to the page.
 */
int32_t pdf_page_builder_signature_field(FfiPageBuilder *handle,
                                         const char *name,
                                         float x,
                                         float y,
                                         float w,
                                         float h,
                                         int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Add a footnote reference mark at the current cursor position and record the
 * note body for page-end placement.
 *
 * `ref_mark` — superscript label emitted inline (e.g. "¹" or `[1]`).
 * `note_text` — body text placed near the page bottom with a separator line.
 *
 * # Safety
 * `handle`, `ref_mark`, and `note_text` must be valid non-null pointers.
 */
int32_t pdf_page_builder_footnote(FfiPageBuilder *handle,
                                  const char *ref_mark,
                                  const char *note_text,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Lay out `text` as wrapped lines distributed across `column_count` balanced
 * columns at the current cursor position, with `gap_pt` points between columns.
 *
 * # Safety
 * `handle` and `text` must be valid non-null pointers.
 */
int32_t pdf_page_builder_columns(FfiPageBuilder *handle,
                                 uint32_t column_count,
                                 float gap_pt,
                                 const char *text,
                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Emit `text` inline at the cursor — advances cursor_x, not cursor_y.
 * Call `pdf_page_builder_newline` to advance to the next line.
 *
 * # Safety
 * `handle` and `text` must be valid non-null pointers.
 */
int32_t pdf_page_builder_inline(FfiPageBuilder *handle, const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Inline bold run.
 *
 * # Safety
 * `handle` and `text` must be valid non-null pointers.
 */
int32_t pdf_page_builder_inline_bold(FfiPageBuilder *handle, const char *text, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Inline italic run.
 *
 * # Safety
 * `handle` and `text` must be valid non-null pointers.
 */
int32_t pdf_page_builder_inline_italic(FfiPageBuilder *handle,
                                       const char *text,
                                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Inline colored run (RGB 0.0–1.0).
 *
 * # Safety
 * `handle` and `text` must be valid non-null pointers.
 */
int32_t pdf_page_builder_inline_color(FfiPageBuilder *handle,
                                      float r,
                                      float g,
                                      float b,
                                      const char *text,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Advance cursor_y by one line-height and reset cursor_x to 72 pt.
 *
 * # Safety
 * `handle` must be a valid non-null pointer.
 */
int32_t pdf_page_builder_newline(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Place a 1-D barcode image on the page.
 * `barcode_type`: 0=Code128 1=Code39 2=EAN13 3=EAN8 4=UPCA 5=ITF 6=Code93 7=Codabar.
 */
int32_t pdf_page_builder_barcode_1d(FfiPageBuilder *handle,
                                    int32_t barcode_type,
                                    const char *data,
                                    float x,
                                    float y,
                                    float w,
                                    float h,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Place a QR-code image on the page (square: `size × size` points).
 */
int32_t pdf_page_builder_barcode_qr(FfiPageBuilder *handle,
                                    const char *data,
                                    float x,
                                    float y,
                                    float size,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Embed an image at `(x, y, w, h)` with an accessibility alt text.
 * `bytes` must point to raw JPEG/PNG/WebP image data of `len` bytes.
 * Embed an image at `(x, y, w, h)` in PDF points (no accessibility wrapper).
 * `bytes` must point to raw JPEG/PNG image data of `len` bytes.
 * Returns 0 on success, −1 on error.
 */
int32_t pdf_page_builder_image(FfiPageBuilder *handle,
                               const uint8_t *bytes,
                               uintptr_t len,
                               float x,
                               float y,
                               float w,
                               float h,
                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * `alt_text` is a NUL-terminated UTF-8 string.
 * Returns 0 on success, −1 on error.
 */
int32_t pdf_page_builder_image_with_alt(FfiPageBuilder *handle,
                                        const uint8_t *bytes,
                                        uintptr_t len,
                                        float x,
                                        float y,
                                        float w,
                                        float h,
                                        const char *alt_text,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Embed a decorative image at `(x, y, w, h)` as an /Artifact (no alt text).
 * `bytes` must point to raw JPEG/PNG/WebP image data of `len` bytes.
 * Returns 0 on success, −1 on error.
 */
int32_t pdf_page_builder_image_artifact(FfiPageBuilder *handle,
                                        const uint8_t *bytes,
                                        uintptr_t len,
                                        float x,
                                        float y,
                                        float w,
                                        float h,
                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Draw a stroked rectangle outline (1pt black).
 */
int32_t pdf_page_builder_rect(FfiPageBuilder *handle,
                              float x,
                              float y,
                              float w,
                              float h,
                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Draw a filled rectangle in RGB colour (channels 0–1).
 */
int32_t pdf_page_builder_filled_rect(FfiPageBuilder *handle,
                                     float x,
                                     float y,
                                     float w,
                                     float h,
                                     float r,
                                     float g,
                                     float b,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Draw a line from `(x1, y1)` to `(x2, y2)` with 1pt black stroke.
 */
int32_t pdf_page_builder_line(FfiPageBuilder *handle,
                              float x1,
                              float y1,
                              float x2,
                              float y2,
                              int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Buffer a `stroke_rect(x, y, w, h, LineStyle)` call. `width` is the
 * stroke width in points; `r`/`g`/`b` are 0..1 colour channels.
 */
int32_t pdf_page_builder_stroke_rect(FfiPageBuilder *handle,
                                     float x,
                                     float y,
                                     float w,
                                     float h,
                                     float width,
                                     float r,
                                     float g,
                                     float b,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Buffer a `stroke_line(p1, p2, LineStyle)` call.
 */
int32_t pdf_page_builder_stroke_line(FfiPageBuilder *handle,
                                     float x1,
                                     float y1,
                                     float x2,
                                     float y2,
                                     float width,
                                     float r,
                                     float g,
                                     float b,
                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Buffer a `stroke_rect` with a dash pattern.
 *
 * `dash_array` is a pointer to `n_dash` alternating on/off lengths in points.
 * `phase` is the starting offset into the pattern. Pass `n_dash == 0` for solid.
 *
 * # Safety
 * `handle` must satisfy the Handle Ownership Contract. If `n_dash > 0`,
 * `dash_array` must point to `n_dash` valid `f32` values.
 */
int32_t pdf_page_builder_stroke_rect_dashed(FfiPageBuilder *handle,
                                            float x,
                                            float y,
                                            float w,
                                            float h,
                                            float width,
                                            float r,
                                            float g,
                                            float b,
                                            const float *dash_array,
                                            uintptr_t n_dash,
                                            float phase,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Buffer a `stroke_line` with a dash pattern.
 *
 * # Safety
 * `handle` must satisfy the Handle Ownership Contract. If `n_dash > 0`,
 * `dash_array` must point to `n_dash` valid `f32` values.
 */
int32_t pdf_page_builder_stroke_line_dashed(FfiPageBuilder *handle,
                                            float x1,
                                            float y1,
                                            float x2,
                                            float y2,
                                            float width,
                                            float r,
                                            float g,
                                            float b,
                                            const float *dash_array,
                                            uintptr_t n_dash,
                                            float phase,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Buffer a `text_in_rect(rect, text, align)` call. `align` encodes
 * 0=Left, 1=Center, 2=Right — anything else is treated as Left.
 */
int32_t pdf_page_builder_text_in_rect(FfiPageBuilder *handle,
                                      float x,
                                      float y,
                                      float w,
                                      float h,
                                      const char *text,
                                      int32_t align,
                                      int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Buffer a `new_page_same_size` transition. Subsequent ops buffered on
 * this same page handle land on the new page.
 */
int32_t pdf_page_builder_new_page_same_size(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Buffer a buffered `table(Table)` call — the whole row matrix is
 * stored in memory until `pdf_page_builder_done` replays it against the
 * real FluentPageBuilder.
 *
 * Cell array is row-major: `cell_strings[row * n_columns + col]`. Each
 * pointer must be a valid null-terminated UTF-8 C string. `widths`
 * `aligns` are both length `n_columns`; `aligns` encodes 0/1/2 (see
 * `pdf_page_builder_text_in_rect`). `has_header != 0` promotes the
 * first row to a header (bold + default background).
 *
 * The streaming variant (row-at-a-time, O(cols) memory) is tracked
 * separately — see issue #393 step 6.5.
 */
int32_t pdf_page_builder_table(FfiPageBuilder *handle,
                               uintptr_t n_columns,
                               const float *widths,
                               const int32_t *aligns,
                               uintptr_t n_rows,
                               const char *const *cell_strings,
                               int32_t has_header,
                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Open a streaming table on this page. Columns are supplied as three
 * parallel arrays — `headers` is a length-`n_columns` array of
 * null-terminated C strings; `widths` and `aligns` are length-
 * `n_columns` numeric arrays (aligns: 0=Left, 1=Center, 2=Right).
 *
 * Subsequent calls to `pdf_page_builder_streaming_table_push_row` feed
 * rows into the open table. Call `pdf_page_builder_streaming_table_finish`
 * to close it; the table is also auto-closed on `_done`.
 *
 * In v0.3.39 the FFI layer buffers rows until `_done` replays them
 * against the live Rust `StreamingTable` — true O(cols) memory lives
 * in the Rust core; the FFI replay holds `Vec<Vec<String>>` temporarily.
 * Row-by-row FFI streaming is tracked under #400 v0.3.40.
 */
int32_t pdf_page_builder_streaming_table_begin(FfiPageBuilder *handle,
                                               uintptr_t n_columns,
                                               const char *const *headers,
                                               const float *widths,
                                               const int32_t *aligns,
                                               int32_t repeat_header,
                                               int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Begin a streaming table with a column-width mode.
 *
 * `mode`: 0 = Fixed (default), 1 = Sample(sample_rows, min_w, max_w),
 *         2 = AutoAll (rejected — see `pdf_page_builder_streaming_table_begin`).
 * `max_rowspan`: 0 or 1 disables rowspan; ≥2 enables cells to span that many rows.
 */
int32_t pdf_page_builder_streaming_table_begin_v2(FfiPageBuilder *handle,
                                                  uintptr_t n_columns,
                                                  const char *const *headers,
                                                  const float *widths,
                                                  const int32_t *aligns,
                                                  int32_t repeat_header,
                                                  int32_t mode,
                                                  uintptr_t sample_rows,
                                                  float min_col_width_pt,
                                                  float max_col_width_pt,
                                                  uintptr_t max_rowspan,
                                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Set the batch size for the currently-open streaming table. When
 * `push_row` accumulates this many pending rows, a batch boundary is
 * recorded automatically (auto-flush). Call after
 * `pdf_page_builder_streaming_table_begin[_v2]` and before the first
 * `push_row`. A value of 0 defaults to 256.
 */
int32_t pdf_page_builder_streaming_table_set_batch_size(FfiPageBuilder *handle,
                                                        uintptr_t batch_size,
                                                        int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Return the number of rows pushed since the last batch boundary
 * (or since the table was opened). Returns 0 if no table is open.
 */
uintptr_t pdf_page_builder_streaming_table_pending_row_count(FfiPageBuilder *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Return the number of complete batches recorded so far.
 * Returns 0 if no table is open.
 */
uintptr_t pdf_page_builder_streaming_table_batch_count(FfiPageBuilder *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Explicitly mark a batch boundary: increment the batch counter
 * reset the pending-row counter. A no-op if there are no pending rows.
 */
int32_t pdf_page_builder_streaming_table_flush(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Push one row into the currently-open streaming table. `cells` is
 * a length-`n_cells` array of null-terminated UTF-8 C strings; its
 * length must equal the column count supplied to `_begin`.
 * All cells are treated as rowspan=1. For rowspan > 1 use
 * `pdf_page_builder_streaming_table_push_row_v2`.
 */
int32_t pdf_page_builder_streaming_table_push_row(FfiPageBuilder *handle,
                                                  uintptr_t n_cells,
                                                  const char *const *cells,
                                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Push one row with per-cell rowspan values. `cells` is a length-`n_cells`
 * array of null-terminated UTF-8 C strings. `rowspans` is a length-`n_cells`
 * array of `size_t` values (1 = normal cell, ≥2 = span). Pass NULL for
 * `rowspans` to treat all cells as rowspan=1 (same as `_push_row`).
 */
int32_t pdf_page_builder_streaming_table_push_row_v2(FfiPageBuilder *handle,
                                                     uintptr_t n_cells,
                                                     const char *const *cells,
                                                     const uintptr_t *rowspans,
                                                     int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Close the currently-open streaming table. Records a final batch
 * boundary if any rows are pending, then resets all batch state.
 * Returns -1 + ERR_INVALID_ARG if no table is open.
 */
int32_t pdf_page_builder_streaming_table_finish(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Commit this page's buffered operations to its parent builder
 * **consume** the page handle. After a successful call the handle is
 * invalid; do not call `_free`.
 */
int32_t pdf_page_builder_done(FfiPageBuilder *handle, int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Drop an uncommitted page handle (error-recovery path). Does NOT
 * apply the buffered operations. If the parent builder is still
 * alive, also clears the open-page slot so the next `_page` call
 * succeeds.
 */
void pdf_page_builder_free(FfiPageBuilder *handle);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Build the PDF and return the bytes. Consumes the builder *state*
 * — the handle wrapper is still allocated and MUST be released with
 * `pdf_document_builder_free`. Returns NULL on error. Output buffer
 * must be freed with `free_bytes`.
 */
uint8_t *pdf_document_builder_build(FfiDocumentBuilder *handle,
                                    uintptr_t *out_len,
                                    int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Build and save the PDF to `path`. Consumes the builder state;
 * caller must still call `pdf_document_builder_free`.
 */
int32_t pdf_document_builder_save(FfiDocumentBuilder *handle,
                                  const char *path,
                                  int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Build and save with AES-256 encryption. Consumes the builder
 * state; caller must still call `pdf_document_builder_free`.
 */
int32_t pdf_document_builder_save_encrypted(FfiDocumentBuilder *handle,
                                            const char *path,
                                            const char *user_password,
                                            const char *owner_password,
                                            int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Build encrypted bytes. Consumes the builder state; caller must
 * still call `pdf_document_builder_free`. Output buffer must be
 * freed with `free_bytes`.
 */
uint8_t *pdf_document_builder_to_bytes_encrypted(FfiDocumentBuilder *handle,
                                                 const char *user_password,
                                                 const char *owner_password,
                                                 uintptr_t *out_len,
                                                 int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Build a PDF from HTML + CSS + a single embedded font. Returns a
 * `Pdf` handle (usable with `pdf_save` / `pdf_save_to_bytes`) or NULL
 * on error.
 */
Pdf *pdf_from_html_css(const char *html,
                       const char *css,
                       const uint8_t *font_bytes,
                       uintptr_t font_len,
                       int32_t *error_code);
#endif

#if !defined(PDF_OXIDE_TARGET_WASM32)
/**
 * Build a PDF from HTML+CSS with a multi-font cascade. `families`,
 * `font_bytes`, and `font_lens` are parallel arrays of length `count`.
 * Caller-owned; the FFI copies the bytes.
 */
Pdf *pdf_from_html_css_with_fonts(const char *html,
                                  const char *css,
                                  const char *const *families,
                                  const uint8_t *const *font_bytes,
                                  const uintptr_t *font_lens,
                                  uintptr_t count,
                                  int32_t *error_code);
#endif

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus

#endif  /* PDF_OXIDE_H */
