Граф зависимостей вычислений формы

HotPDF строит граф зависимостей по полям вычислений загруженного AcroForm, поэтому изменение значения пересчитывает только те вычисления, которые читают его транзитивно, и в таком порядке, при котором каждое пересчитанное значение уже видно вычислениям-потребителям

Построение графа

THotPDF.BuildLoadedFormCalculationGraph возвращает snapshot, которым владеет вызывающий. Каждое поле становится узлом; каждый Calculate-скрипт, зарегистрированный в /CO, статически анализируется через HPDFExtractSafeJavaScriptFieldReferences, которая вытаскивает имена полей, к которым обращается скрипт из безопасного подмножества: аргументы в кавычках у AFSimple_Calculate("OP", ["a","b"]) и имена в кавычках в обращениях getField("x"), причём строковые литералы пропускаются, чтобы упоминания в тексте не считались ссылками

THPDFFormCalcGraph.ResolveLinks превращает эти имена в рёбра source → dependent и упорядочивает цели топологической сортировкой. Поля в цикле зависимостей вместо этого сохраняют порядок /CO в документе — как при однопроходной семантике полного пересчёта; изменившееся поле никогда не пересчитывает собственный скрипт, поэтому циклы не уходят в пинг-понг, а введённое пользователем значение всегда выигрывает. Имена источников, не совпавшие ни с одним полем, учитываются в UnresolvedFieldNameCount и рёбер не порождают — так же ведёт себя runtime-резолвер

Инкрементальный пересчёт

RecalculateLoadedFormFieldsIncremental вычисляет только затронутое замыкание переданных изменившихся полей. Записи через SetFormFieldValue и SetLoadedFormFieldValueByName накапливают dirty-набор; перегрузка без параметров пересчитывает по нему, так что импорт FDF или XFDF с одним инкрементальным проходом после обновляет все зависимые вычисления, не трогая посторонние. Обе перегрузки и полный проход RecalculateLoadedFormFields после этого очищают dirty-набор

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']);

Осмотр графа

Граф раскрывает состояние вычислений по каждому полю (GetNode), полную последовательность вычисления (AllTargets), затронутое замыкание (AffectedTargets), число целей в циклах (CyclicTargetCount) и неразрешённые имена источников, так что инструментарий может заранее увидеть, какие именно вычисления вызовет правка