Bluebeam JavaScript Header Fields
Two-Line Input Limits & Page-to-Page Syncing

This document explains why Bluebeam header fields behave the way they do, and how we control that behavior using JavaScript.


1. Why Header Fields Are “Per Page”

In Bluebeam (and Acrobat), a form field can exist in two fundamentally different ways:

This script intentionally uses the second approach:

ProjectName.0
ProjectName.1
ProjectName.2

Why?

Important:
Because these are different fields, they do NOT automatically stay in sync. Any syncing must be done intentionally with JavaScript.

2. Limiting a Field to Two Lines (Important Gotcha)

The Problem

Bluebeam does not have a built-in “max lines” setting. If a field is multiline, the user can normally:

The Solution (Two Layers of Protection)

We enforce a two-line limit using:

  1. Keystroke action – blocks typing or pasting beyond two lines
  2. Validate action – trims excess lines if something slips through

Why Two Layers?

Keystroke catches normal typing.
Validate catches copy-paste, undo/redo, or edge cases.

The Code

// Prevent typing or pasting more than 2 lines
function PB_limitLinesKeystroke(maxLines) {
  var cur = String(event.value || "").replace(/\r\n/g, "\n");
  var chg = String(event.change || "").replace(/\r\n/g, "\n");

  var before = cur.substring(0, event.selStart);
  var after  = cur.substring(event.selEnd);
  var proposed = before + chg + after;

  if (proposed.split("\n").length > maxLines) {
    event.rc = false;
  }
}

// Final cleanup safeguard
function PB_enforceMaxLinesValidate(maxLines) {
  var v = String(event.value || "").replace(/\r\n/g, "\n");
  var lines = v.split("\n");
  if (lines.length > maxLines) {
    event.value = lines.slice(0, maxLines).join("\n");
  }
}

How It’s Applied

f.multiline = true;
f.doNotScroll = true;
f.setAction("Keystroke", "PB_limitLinesKeystroke(2);");
f.setAction("Validate",  "PB_enforceMaxLinesValidate(2);");

Result:


3. Syncing Page 1 to All Other Pages

The Goal

When the user edits:

ProjectName.0

We want:

ProjectName.1
ProjectName.2
ProjectName.3

to automatically match it.

Why We Use Page 0 as the “Master”

The Sync Function

function PB_syncHeaderField(baseName) {
  var master = this.getField(baseName + ".0");
  if (!master) return;

  for (var p = 1; p < this.numPages; p++) {
    var f = this.getField(baseName + "." + p);
    if (f) f.value = master.value;
  }
}

When Sync Happens

We attach it to the Validate event on page 0:

this.getField("ProjectName.0")
  .setAction("Validate", "PB_syncHeaderField('ProjectName');");

That means:


4. Why We Make Other Pages Read-Only

If every page stayed editable:

Best Practice

function PB_setMirrorsReadonly(baseName) {
  for (var p = 1; p < this.numPages; p++) {
    var f = this.getField(baseName + "." + p);
    if (f) f.readonly = true;
  }
}

5. Why This Design Is Reliable

This approach mirrors how professional calculation packets, drawing headers, and spec covers are typically automated.

6. Common Questions

“Why not just use one field name everywhere?”

Because Bluebeam will treat them as one logical object. Page reordering and resizing can produce unpredictable results.

“Why not trust char limits alone?”

Character limits do not control line breaks. Two short lines can exceed one long line visually.

“Why Validate instead of Keystroke only?”

Paste, undo, and scripted changes bypass keystroke. Validate is the safety net.


7. Summary

This setup is deliberate, robust, and designed for real-world calculation packets.