Skip to content

Add form field read-only control and signature interception bridge#3

Draft
mutumbakato wants to merge 4 commits into
masterfrom
feature/form-readonly-signature-interception
Draft

Add form field read-only control and signature interception bridge#3
mutumbakato wants to merge 4 commits into
masterfrom
feature/form-readonly-signature-interception

Conversation

@mutumbakato

Copy link
Copy Markdown

Summary

Exposes two native PDF form capabilities through NutrientView, on both the Legacy (Paper) and New (Fabric + TurboModule) architectures, following the BRIDGING.md recipe:

  • setFormFieldReadOnly(fullyQualifiedName, readOnly, persist?) — locks/unlocks a single form field. iOS maps persist: true to PSPDFFormField.isReadOnly (persisted PDF flag) and persist: false to isEditable (session-only). Android updates AnnotationFlags.LOCKEDCONTENTS on the field's widget annotations, preserving existing flags (READONLY is ignored for widgets, and Android's FormField has no read-only setter).
  • interceptSignatureFields prop + onSignatureFieldTapped event — consumes taps on signature form fields (iOS didTapOnAnnotation: delegate, Android FormManager.OnFormElementClickedListener) and emits {fullyQualifiedName, pageIndex} to JS instead of presenting the native signature UI.
  • dismissSignaturePad() — dismisses the native signature creation UI when presented (type-checked against PSPDFSignatureCreationViewController/PSPDFSignatureViewController on iOS; located via ElectronicSignatureFragment.FRAGMENT_TAG on Android), resolving false otherwise. Idempotent.

Also included:

  • A Signature Interception Catalog example used as the runtime test harness.
  • An implementation guide for customers customizing an SDK fork.

Verification

  • npm test (32/32), npm run lint, npm run build-and-generate-types — all pass.
  • Catalog Android assembleDebug (compiles legacy + newarch source sets + codegen) and iOS xcodebuild — both succeed.
  • Runtime (Maestro-driven, New Architecture) on a Pixel 9 Pro emulator and iPhone 17 Pro simulator:
Test Android iOS
Intercept ON: event fires with correct field/page, native UI suppressed
Intercept OFF: default "Add Signature" UI opens unchanged
dismissSignaturePad() with UI open → true, UI closed
dismissSignaturePad() with nothing open → false
Lock blocks editor on the target field only; unlock restores

Known caveat surfaced during testing: form interaction (and therefore interception) requires a writable document — Android file:///android_asset/ documents open read-only. Documented in the guide.

Open items before undrafting

  • Save-and-reopen persistence assertions; multi-widget fields; rotation during signature UI; same runtime matrix on Paper builds and physical devices.
  • Decide whether persist: false should reject on Android instead of behaving persistently.

🤖 Generated with Claude Code

mutumbakato and others added 4 commits July 17, 2026 15:39
Exposes two native form capabilities through NutrientView on both the
Legacy (Paper) and New (Fabric + TurboModule) architectures:

- setFormFieldReadOnly(fullyQualifiedName, readOnly, persist): locks or
  unlocks a single form field. iOS maps persist=true to
  PSPDFFormField.isReadOnly (persisted PDF flag) and persist=false to
  isEditable (session-only). Android updates the LOCKEDCONTENTS flag on
  the field's widget annotations, preserving existing flags.
- dismissSignaturePad(): dismisses the native signature creation UI when
  presented, resolving false otherwise. Type-checks the presented
  controller/fragment so unrelated UI is never dismissed.
- interceptSignatureFields prop + onSignatureFieldTapped event: consumes
  taps on signature form fields (iOS PSPDFViewControllerDelegate tap
  handling, Android FormManager.OnFormElementClickedListener) and emits
  the field's fully qualified name and page index to JS instead of
  presenting the native signature UI.

Verified with Catalog builds on both platforms (gradlew assembleDebug,
xcodebuild), npm test, npm run lint, and regenerated types.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a SignatureInterception example screen used to verify the new
bridge at runtime on the Android emulator and iOS simulator via
Maestro: signature tap interception (on/off), setFormFieldReadOnly
lock/unlock, and dismissSignaturePad on an open and closed signature UI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Standalone guide for customizing an SDK fork with the new APIs: public
API semantics and platform differences, per-layer implementation
walkthrough for both architectures, build/test instructions, runtime
verification results, and packaging options.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
dismissSignaturePad only targeted the signature creation UI, so it
worked on the first tap of a signature field but failed on later tries
once a saved signature existed (or under the legacy signature flow),
when the SDK presents the saved-signature picker instead.

Android now scans candidate fragment managers for instances of both
ElectronicSignatureFragment and SignaturePickerFragment (instanceof
instead of tag lookup, since the picker's tag is private) and dismisses
via the matching static helper. iOS now checks the entire presented
navigation stack against the creation controller, the legacy signature
controller, and PSPDFSignatureSelectorViewController.

Verified on the Android emulator: after saving a signature, tapping the
field opens the "Signatures" picker and dismissSignaturePad now closes
it (previously returned false). Creation-UI cycles re-verified on both
platforms; a signed field's annotation context menu is still correctly
left alone (resolves false).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mutumbakato

Copy link
Copy Markdown
Author

Fix: repeated dismissSignaturePad() failures (69ad436)

Reported behavior: dismiss worked on the first try, then failed on subsequent tries.

Root cause: dismissSignaturePad() only recognized the signature creation UI. Once a signature has been saved to the signature store (or under the legacy signature flow), tapping a signature field presents the saved-signature picker instead — SignaturePickerFragment on Android, PSPDFSignatureSelectorViewController on iOS — which the type checks didn't match, so the call resolved false and left the UI open.

Fix:

  • Android: scan candidate fragment managers for instances of both ElectronicSignatureFragment and SignaturePickerFragment (instanceof-based, since the picker's FRAGMENT_TAG is private) and dismiss via the matching static helper.
  • iOS: check the entire presented navigation stack against PSPDFSignatureCreationViewController, legacy PSPDFSignatureViewController, and PSPDFSignatureSelectorViewController.

Verified (Maestro, emulator/simulator):

  • Android: saved a signature → field tap now opens the "Signatures" picker → dismissSignaturePad() dismisses it and resolves true (previously false/no-op). Creation-UI cycles re-verified on both platforms.
  • A signed field opens the annotation context menu (no signature UI); dismiss correctly resolves false and leaves the menu alone.

Guide updated with the expanded targeting and a caveat about the picker/signed-field states.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant