Form Calculation Dependency Graph
HotPDF builds a dependency graph over the calculation fields of a loaded AcroForm so a value change recalculates only the calculations that transitively read it, in an order where every recomputed value is already visible to the calculations that consume it
Graph construction
THotPDF.BuildLoadedFormCalculationGraph returns a snapshot owned by the caller. Every field becomes a node; each /CO-registered Calculate script is statically analyzed with HPDFExtractSafeJavaScriptFieldReferences, which reads the field names a safe-subset script touches: the quoted arguments of AFSimple_Calculate("OP", ["a","b"]) and the quoted names of getField("x") accesses, with string literals skipped so textual mentions are not treated as references
THPDFFormCalcGraph.ResolveLinks resolves those names into source → dependent edges and orders the targets with a topological sort. Fields on a dependency cycle keep /CO document order instead, matching the one-pass semantics of a full recalculation; a changed field never recalculates its own script, so cycles cannot ping-pong and a user-written value always wins. Source names that match no field are reported through UnresolvedFieldNameCount and produce no edges, mirroring the runtime resolver
Incremental recalculation
RecalculateLoadedFormFieldsIncremental evaluates only the affected closure of the given changed fields. Writes through SetFormFieldValue and SetLoadedFormFieldValueByName accumulate a dirty set; the parameterless overload recalculates from that set, so an FDF or XFDF import followed by one incremental pass refreshes every dependent calculation without touching unrelated ones. Both overloads and the full RecalculateLoadedFormFields pass consume the dirty set afterwards
PDF.LoadFromFile('Order.pdf');
PDF.SetLoadedFormFieldValueByName('qty', '3');
PDF.SetLoadedFormFieldValueByName('price', '19.5');
// Recomputes subtotal and total only, chained in one pass
PDF.RecalculateLoadedFormFieldsIncremental(['qty', 'price']);
Graph inspection
The graph exposes per-field calculation state (GetNode), the full evaluation sequence (AllTargets), the affected closure (AffectedTargets), the number of targets on cycles (CyclicTargetCount), and unresolved source names, so tooling can preview exactly which calculations an edit will trigger