xfa-layout-engine 1.0.0-beta.5

Box-model and pagination layout engine for XFA forms. Experimental — part of the PDFluent XFA stack, under active development.
Documentation
//! Named emit helpers for the five M1.6 hot phases.
//!
//! Each helper builds a fully-populated [`super::TraceEvent`] for a
//! specific phase and forwards it to [`super::emit`]. Callers who want
//! finer control can construct events directly with
//! [`super::TraceEvent::new`] and the builders.
//!
//! ## Why this module exists
//!
//! Engine code that wants to emit a trace event should call one of these
//! helpers rather than re-typing the `Phase`/`Reason` literal tags. That
//! keeps the call sites short, makes refactors localised, and gives
//! reviewers a single place to inspect the shape of trace events for
//! each phase.
//!
//! All helpers are zero-cost when no sink is installed (one thread-local
//! read + an `Option::is_some` branch).
//!
//! ## Wiring status (M1 v1)
//!
//! These helpers are public API and exercised by the integration test
//! in `tests/trace_sites.rs`. Production wiring of these calls into the
//! XFA layout/data-binding code paths is staged behind a follow-up and
//! is intentionally not part of M1.6 because:
//!
//! - The five hot phases live across `xfa-layout-engine::layout`,
//!   `xfa-layout-engine::form`, and `pdf-xfa::dynamic`. Each call site
//!   needs case-specific input/decision text that v1 does not yet
//!   curate.
//! - Wiring during M1 risks subtle regressions in layout/page-count
//!   gates, which would make the determinism gate noisy precisely as
//!   we are trying to land it.
//!
//! Therefore M1.6 ships:
//! - The taxonomy (M1.5).
//! - These five typed emit helpers (this module) — exercised end-to-end.
//! - Documentation in `M1_RESULT.md` marking the production wiring as
//!   PARTIAL with a clear hand-off pointer.

use super::{emit, Phase, Reason, TraceEvent};

/// Emit a `bind` phase event.
///
/// `som` is the SOM path of the template subform being bound.
/// `decision` is a short summary like `"3 instances created"`.
pub fn bind(som: &str, reason: Reason, decision: impl Into<String>) {
    emit(
        TraceEvent::new(Phase::Bind, reason)
            .with_som(som.to_string())
            .with_decision(decision)
            .with_source("xfa_layout_engine::form::bind"),
    );
}

/// Emit an `occur` phase event.
///
/// `count` is the number of instances after applying `occur.min/max`.
pub fn occur(som: &str, reason: Reason, count: i64) {
    emit(
        TraceEvent::new(Phase::Occur, reason)
            .with_som(som.to_string())
            .with_input(format!("count={count}"))
            .with_source("xfa_layout_engine::form::occur"),
    );
}

/// Emit a `presence` phase event.
pub fn presence(som: &str, reason: Reason, decision: impl Into<String>) {
    emit(
        TraceEvent::new(Phase::Presence, reason)
            .with_som(som.to_string())
            .with_decision(decision)
            .with_source("xfa_layout_engine::form::presence"),
    );
}

/// Emit a `paginate` phase event.
///
/// `available_h` is the available content-area height in points;
/// `needed_h` is the height the container would need.
pub fn paginate(som: &str, reason: Reason, available_h: f64, needed_h: f64) {
    emit(
        TraceEvent::new(Phase::Paginate, reason)
            .with_som(som.to_string())
            .with_input(format!(
                "available_h={available_h:.4} needed_h={needed_h:.4}"
            ))
            .with_source("xfa_layout_engine::layout::paginate"),
    );
}

/// Emit a `suppress` phase event.
///
/// `page_index` is the zero-based page index being considered for suppression.
pub fn suppress(reason: Reason, page_index: u32, decision: impl Into<String>) {
    emit(
        TraceEvent::new(Phase::Suppress, reason)
            .with_input(format!("page_index={page_index}"))
            .with_decision(decision)
            .with_source("xfa_layout_engine::layout::suppress"),
    );
}

/// Emit a `fallback` phase event.
///
/// `decision` should be a short reason summary (e.g. the underlying
/// `XfaError` rendered with `{:?}`, or `"timeout"`). Used by
/// `pdf-xfa::flatten` to signal that the XFA pipeline could not
/// produce output and the caller has been served a non-XFA
/// passthrough.
pub fn fallback(reason: Reason, decision: impl Into<String>) {
    emit(
        TraceEvent::new(Phase::Fallback, reason)
            .with_decision(decision)
            .with_source("pdf_xfa::flatten::fallback"),
    );
}