PdfScriptRunner

class PdfScriptRunner(document: PdfDocument, val formState: PdfFormState = PdfFormState(document), val policy: PdfScriptPolicy = PdfScriptPolicy(), onAlert: (PdfScriptAlert) -> Int = { 1 }, onConsole: (String) -> Unit = {}, onRequest: (PdfScriptRequest) -> Unit = {}, onResponse: (PdfScriptPrompt) -> String? = { null }, clock: () -> Long? = null, engine: KiteScriptEngine? = null) : PdfScriptHandler, AutoCloseable(source)

Runs the JavaScript a PDF carries: its document scripts, the actions of its pages and buttons, and the four scripts a form field runs while it is filled.

Scripts see the objects Acrobat defines and Chrome's PDF engine implements: the document's own members as globals, getField, event, app, util, color, console, and the AF helper library every form tool calls. A script that works in Chrome's viewer is meant to work here.

val state = PdfFormState(doc)
PdfScriptRunner(doc, state, onAlert = { alert -> showDialog(alert.message); 1 }).use { runner ->
runner.runDocumentOpen() // document scripts, then the open action
runner.setFieldValue("price", "12") // keystroke, validate, calculate, format
state.value("total") // what the form's own script worked out
}

Nothing here touches the file. Values land in formState, which a viewer draws and the editor saves. One runner belongs to one document.

A runner can be called from any thread. A KiteJS engine belongs to the thread that opened it, so the runner opens its engine on a thread of its own and runs every call there, one at a time and in order. close stops that thread. The callbacks below run on it too. On JavaScript and WebAssembly the engine loads before its first use: call prepare once and wait for it, as KiteDocView does. There, a long script that a suspend event method runs pauses now and then so the page can draw, and the next call waits for it (#489).

Scripts are untrusted input, so policy decides whether they run at all and how long they may take, and anything that reaches outside the document arrives at onRequest for the host to allow or refuse.

Constructors

Link copied to clipboard
constructor(document: PdfDocument, formState: PdfFormState = PdfFormState(document), policy: PdfScriptPolicy = PdfScriptPolicy(), onAlert: (PdfScriptAlert) -> Int = { 1 }, onConsole: (String) -> Unit = {}, onRequest: (PdfScriptRequest) -> Unit = {}, onResponse: (PdfScriptPrompt) -> String? = { null }, clock: () -> Long? = null, engine: KiteScriptEngine? = null)

Types

Link copied to clipboard

What a keystroke script decided: whether the change is allowed, and what the field would hold if it is.

Properties

Link copied to clipboard

The page the reader is on, which this.pageNum reports to scripts.

Link copied to clipboard

How many failures failures no longer holds, because newer ones took their place (#960).

Link copied to clipboard

The scripts that failed since the runner opened, newest last: the last MAX_FAILURES of them, each message cut to MAX_FAILURE_MESSAGE characters and with no cause, so a timer that throws a large message every frame cannot fill the heap (#960). droppedFailures counts the older ones.

Link copied to clipboard

The name a script sees as this.documentFileName.

Link copied to clipboard
open override val formState: PdfFormState

Where field values live while the reader has the file open.

Link copied to clipboard
open override val hasTimers: Boolean

True when a script is waiting on a timer, so a viewer knows to keep pumping.

Link copied to clipboard

What the document's scripts are allowed to do.

Link copied to clipboard
open override val supportsMultipleChoices: Boolean

Functions

Link copied to clipboard
open suspend override fun blur(fieldName: String)

Runs a widget's blur script, which a viewer fires when the field loses the caret.

open suspend override fun blur(fieldName: String, widgetIndex: Int)

Runs the blur script of one widget of the field.

Link copied to clipboard
open suspend override fun choiceKeystroke(fieldName: String, selection: PdfChoiceSelection): PdfChoiceSelection?

Runs only the selection-change event; deferred selection remains outside the live state.

open suspend override fun choiceKeystroke(fieldName: String, selection: PdfChoiceSelection, previous: PdfChoiceSelection): PdfChoiceSelection?
Link copied to clipboard
open override fun close()
Link copied to clipboard
open suspend override fun commit(fieldName: String, value: String): Boolean
Link copied to clipboard
open suspend override fun commitChoice(fieldName: String, selection: PdfChoiceSelection): Boolean

One choice change follows the Acrobat Field event sequence without flattening its values.

Link copied to clipboard
open suspend override fun documentOpened()
Link copied to clipboard
open suspend override fun focus(fieldName: String)

Runs a widget's focus script.

open suspend override fun focus(fieldName: String, widgetIndex: Int)

Runs the focus script of one widget of the field.

Link copied to clipboard
fun formattedValue(fieldName: String): String

The text a field shows, after its format script has had it. The stored value does not change: a formatted total still calculates as a number.

Link copied to clipboard
open suspend override fun keystroke(fieldName: String, change: String, selectionStart: Int, selectionEnd: Int): String?

The viewer's keystroke: the value the field should show, or null when a script refused it.

fun keystroke(fieldName: String, change: String, selectionStart: Int = editingValue(fieldName).length, selectionEnd: Int = selectionStart, commit: Boolean = false): PdfScriptRunner.KeystrokeResult

Runs a field's keystroke script for one edit, without committing it (ISO 32000-1 §12.6.3). A viewer calls this for each character the reader types, so a script can refuse it.

Link copied to clipboard
open suspend override fun mouseDown(fieldName: String)

Runs a widget's mouse down script, which is how an on-screen button reports a press.

open suspend override fun mouseDown(fieldName: String, widgetIndex: Int)

Runs the mouse down script of one widget of the field, such as one button of a radio group.

Link copied to clipboard
open suspend override fun mouseUp(fieldName: String)

Runs a widget's mouse up script, which is how an on-screen button reports a release.

open suspend override fun mouseUp(fieldName: String, widgetIndex: Int)

Runs the mouse up script of one widget of the field.

Link copied to clipboard
open override fun onTimersChanged(listener: () -> Unit): () -> Unit

Reports each timer a script sets or clears, from the script thread (#368).

Link copied to clipboard
open suspend override fun pageClosed(pageIndex: Int)
Link copied to clipboard
open suspend override fun pageOpened(pageIndex: Int)
Link copied to clipboard
open suspend override fun prepare()

Loads the engine, which on JavaScript and WebAssembly has to happen before the first script.

Link copied to clipboard
open suspend override fun pumpTimers(nowMillis: Long): Long?

Runs the timers a script set with app.setInterval or app.setTimeOut that are due, and returns how long to wait before the next one, or null when none is waiting.

Link copied to clipboard

Runs one JavaScript action, such as a link's or a button's.

Link copied to clipboard
open suspend override fun runAction(action: PdfAction.JavaScript)
Link copied to clipboard

Runs every calculate script, in the order of the form's /CO array (ISO 32000-1 §12.7.2). A form with no order runs them in the order its fields appear.

Link copied to clipboard
Link copied to clipboard

Runs every document-level script in the order of their names (ISO 32000-1 §7.7.4). A script that fails is recorded and the ones after it still run.

Link copied to clipboard

Runs the page's close script, its /AA /C entry.

Link copied to clipboard

Runs the page's open script, its /AA /O entry (ISO 32000-1 §12.6.3, Table 195).

Link copied to clipboard
open suspend override fun runWidgetAction(fieldName: String, action: PdfAction): Boolean

Performs one action of a widget's /A chain. A script runs as the field's mouse up event, which is where Acrobat keeps a button's mouse up script, and a form reset is followed by a calculation round, as MuPDF does after one.

Link copied to clipboard
fun setFieldValue(fieldName: String, value: String): Boolean

Commits a value to a field the way a viewer does when the reader leaves it: the keystroke script sees the whole value, then validate, then the value is stored, then every calculate script runs in the form's own order, then the format scripts decide what is shown (ISO 32000-1 §12.6.3 for the triggers and §12.7.2 for the calculation order).