CCS PDF Editor · Docs

CCS PDF Editor — Architecture

Field Value
Version 1.5.42
Owner CCS Information Technology
Approved by CCS Information Technology
Last updated 2026-08-28
Review frequency Annual (or after significant architectural change)
Next review 2027-08-05

High-level design of the CCS PDF Editor as an offline-first, client-side application. For ownership and residual risk see SYSTEM-PROFILE.md and GOVERNANCE.md. For deploy overview see README.md.


1. Purpose of the system

CCS PDF Editor is a static web application (optional Windows WebView2 shell) that:

  1. Renders PDF pages (PDF.js canvas + text layer + interactive AnnotationLayer for widgets).
  2. Lets staff annotate with DOM overlays in Editor workspace.
  3. Lets staff fill form fields in Editor with Pointer (native widgets and/or designer fill surfaces).
  4. Lets staff design fillable AcroForm fields in Form workspace; supported native widgets are auto-imported as designer overlays (no prompt).
  5. Optionally runs local OCR (Tesseract.js + English data under lib/; file:// uses base64 embeds).
  6. Exports either:
  7. Opens many password-protected PDFs via PDF.js onPassword UI.
  8. Prints via the browser/print pipeline (full-opacity annotations; form designer chrome omitted).

It is not multi-tenant SaaS, not a document management system, and not a certified electronic signature platform.


2. Context diagram

                    ┌─────────────────────────────────────┐
                    │  Optional: intranet reverse proxy    │
                    │  TLS · optional SSO / IP allowlist   │
                    └──────────────────┬──────────────────┘
                                       │ HTTPS (static only)
                                       ▼
                    ┌─────────────────────────────────────┐
                    │  Static web host                     │
                    │  index.html, js/, lib/, assets/      │
                    │  NO document database / upload API   │
                    └──────────────────┬──────────────────┘
                                       │ download assets once
                                       ▼
     Local PDF / .ccspdf ──────►┌──────────────────────┐──────► Saved PDF / .ccspdf
     (user disk / share)        │  Browser tab memory  │        (user disk / share)
                                │  PDF.js · overlays   │
                                │  pdf-lib · Tesseract │
                                └──────────────────────┘
                                       │
                                       ▼
                                Printer / PDF printer
                                (OS / org print path)

Optional alternate runtimes:
  · Windows WebView2 shell (deploy_windows / build-windows.ps1)
  · Browser offline zip pack (build-release.ps1) — file:// capable
  · Tauri-style shell if adopted later (same static UI)

3. Application layering

┌──────────────────────────────────────────────────────────┐
│  Browser / WebView2 DOM                                   │
│  index.html · style.css · fonts.css · modals              │
└────────────────────────────┬─────────────────────────────┘
                             │
┌────────────────────────────▼─────────────────────────────┐
│  js/ui/*          Toolbars, modals, hotkeys, header density│
│  js/interaction/* Mouse, keyboard, drag, file import       │
│  js/overlays/*    Text, shapes, images, tables, snip,      │
│                   form-fields (design + Editor fill UI)    │
│  js/features/*    OCR, print, signature + form attestation │
│  js/engine/*      PDF render, AnnotationLayer, native-forms│
│  js/core/*        State, history, import/export (io.js)    │
│                   host-bridge (WebView2 open-with)         │
└────────────────────────────┬─────────────────────────────┘
                             │
┌────────────────────────────▼─────────────────────────────┐
│  lib/*  PDF.js · pdf-lib · qpdf.wasm · html2canvas ·      │
│         Tesseract (+ offline embeds for file://) · fonts  │
└──────────────────────────────────────────────────────────┘
Layer Responsibility
UI Workspace switch, tools, dialogs, responsive header
Interaction Pointer tools, multi-select, zoom/scroll; workspace isolation
Overlays Annotation objects + form designer + Editor fill surfaces
Engine PDF.js canvases, text layer, interactive form widgets, auto-import
Core I/O Load PDF/project; save project; flatten + AcroForm + optional QPDF encrypt
lib/ Third-party engines (see NOTICE)

There is no required JS build pipeline for the static web UI. Scripts load with defer from index.html. Windows EXE is produced separately via scripts/build-windows.ps1.


4. Core runtime flows

4.1 Open PDF

  1. User selects file(s), drops onto the viewer, or (Windows shell) uses Open with / argv path.
  2. Bytes enter memory (ArrayBuffer).
  3. If encrypted, PDF.js onPassword drives the password modal; user password unlocks the document.
  4. PDF.js renders each page to canvas (annotationMode DISABLE so widgets are not double-painted).
  5. Text layer + AnnotationLayer (renderForms: true) for interactive native widgets.
  6. Source bytes retained in window.sourcePdfBuffers[fingerprint] for later export.
  7. If file is .ccspdf, trailer schema reapplied as overlays.
  8. Otherwise, supported native widgets are auto-imported as Form designer overlays (silent); matching native AnnotationLayer sections are hidden so Editor fill uses the designer fill surface without doubles.

4.2 Edit / fill

4.3 Workspaces (Editor vs Form)

APP.workspaceMode is "editor" or "form". Body classes workspace-editor / workspace-form (and tool-select) control hit-testing:

Mode Annotations Form widgets Native AnnotationLayer
Editor + Pointer Full, interactive Fill surfaces active Interactive (unless hidden after import)
Editor + other tool Interactive Fill PE off PE off (don’t steal annotate clicks)
Form Dimmed, PE none Design chrome + handles Dimmed, PE none

Switching into Form always selects Pointer. Save PDF and Print temporarily apply full-opacity annotation styling so Form-mode dimming is not included in output.

4.4 Save Project (.ccspdf)

  1. Acquire save destination first (native picker / modal) while user gesture is valid.
  2. For each page: copy original PDF page via pdf-lib; serialize overlay + form-field JSON.
  3. Gzip + hash edit state; append after PDF bytes with markers.
  4. Write blob to destination.

4.5 Save PDF (annotations flattened + fillable fields)

  1. Acquire save destination first (critical for Chromium File System Access API).
  2. If the UI is in Form mode, temporarily restore full-opacity annotations for capture.
  3. For each page: copy the source page; if decorative overlays exist, hide form designer widgets and the base canvas, rasterize overlays, and embed the PNG. If Scan OCR produced text-layer spans, write them as invisible searchable PDF text (rendering mode 3) in MediaBox space.
  4. After all pages exist: create AcroForm widgets (text, checkbox, radio group, dropdown; signature field where supported).
  5. Optionally encrypt the finished bytes with a user password via QPDF (WASM) (content-preserving; AcroForm fields remain fillable).
  6. Write the final PDF.

4.6 OCR

  1. Tesseract worker initialized with local workerPath, corePath, langPath under ./lib/.
  2. On file://, worker/core/lang assets load via base64 embeds + IndexedDB language seed (CORS-safe).
  3. Canvas pixels recognized in-browser; text layer spans injected for selection/stamp tools.
  4. Save PDF embeds those spans as hidden page text so other readers can search/select/copy. Save Project stores span geometry in schema and restores it on reopen.
  5. No remote OCR endpoint in this release.

4.7 Print

Temporarily uses full-opacity annotation styling, clones page canvases and annotation overlays (not form designer chrome), then opens the browser print dialog.


5. Data flow & trust boundaries

Boundary Trust assumption
Static host → browser Assets are authentic CCS release (HTTPS + host controls)
User disk → browser User chose a file they are allowed to open
Browser → user disk User chooses save location; OS permissions apply
Browser → network Should not upload document content; only loads same-origin assets (SW is network-only pass-through)

5.1 In-memory data

While a document is open, memory may hold: full PDF bytes, high-resolution canvases, overlay HTML (including text that may be PHI), form-field designer state, OCR results, and signature/image bitmaps.

5.2 Persistence

Store Content Controlled by
User filesystem Saved PDF / .ccspdf User + OS ACLs + org DLP
Browser download folder Fallback saves User profile
IndexedDB ccs-pdf-editor-session Open-tab list + optional PDF byte cache Session restore / Dismiss
IndexedDB ccs-pdf-editor-prefs Typed signature name + chosen ink PNG until Clear saved signature User (Tools menu / signature dialog)
Cache Storage (if SW improved) App assets only (intended) Service worker design
Server disk Static app files only CCS IT

6. Offline-first model

Asset Offline strategy
Application JS/CSS/HTML Deployed on host; intended for local cache / PWA
PDF.js worker / cmaps Vendored under lib/
OCR engine + eng.traineddata Vendored under lib/; embeds for file://
QPDF WASM Vendored qpdf.js + qpdf.wasm (+ embed for file:// encrypt)
Fonts Self-hosted assets/*.woff2 via fonts.css
User PDFs Always local to the user

Service worker (sw.js): network-only pass-through (PWA installability); no Cache API precache or fallback. Offline intent = no CDN hard-dependency + vendored lib/ / assets on the origin; script/CSS freshness via config.js ?v=.

Deploy modes:

Mode Notes
HTTPS intranet (primary) Preferred production; PWA manifest / SW work
Browser zip pack scripts/build-release.ps1 → app/ + launchers; file:// supported with embeds
Windows WebView2 scripts/build-windows.ps1 → CcsPdfEditor.exe + app/ + tools\

7. Security design notes

Topic Approach
AuthN/Z Outside the app (network edge)
XSS Prefer textContent / sanitizer for restored HTML (sanitizeHTML); keep third-party libs updated
Supply chain Vendor libs in-repo; no runtime npm install on host
Save gesture Destination acquired before long export work (File System Access activation)
Secrets None in client application code; no third-party API keys required
CSP Optional host header; validate wasm requirements for Tesseract/PDF.js before enabling a restrictive policy

8. Deployment topologies

8.1 Internal OWA (primary)

8.2 Windows WebView2 shell (supported optional)

8.3 Browser offline pack (supported optional)

8.4 What not to do


9. File format: .ccspdf / embedded project state

CCS project files are valid PDFs that also carry an editable schema + optional audit.

9.0 Primary: Catalog dictionary /CCSProject (schema 4.0+)

Stored on the document catalog (private key ignored by most readers):

Key Meaning
/Type /CCSProject
/Ver (4.0)
/Fmt /GZipHex — JSON → gzip → hex in /Data
/Hash Integrity string (same salt recipe as legacy trailer)
/Data <hex…> of the gzip payload

Implemented in js/core/project-state.js. Load tries legacy trailer first (if present), then catalog.

Save Project (.ccspdf) and Export PDF (.pdf) both embed /CCSProject (no trailer by default).

Trailer dual-write is opt-in only (dualWriteTrailer: true).

Resilience note: Other editors may strip unknown catalog keys on “Save as”. Hex-in-catalog is the standard private-data pattern; full legal non-repudiation still needs signed metadata / external e-sign when required.

9.1 Legacy trailer (still written + read)

  1. Valid PDF body (with or without /CCSProject).
  2. Trailing ASCII marker: \n---CCS-SAVE-STATE---.
  3. Integrity hash + delimiter + gzip-compressed JSON schema of overlays and optional rolling audit.
Field Purpose
version Schema version string (3.1 trailer-only, 4.0 embedded)
pdfName Suggested file name
pages[] Per-page overlay / form-field layout
audit[] Optional rolling change log (see §9.2)
storage Optional: catalog-CCSProject / catalog+trailer

Ordinary PDF readers open the PDF portion; edit restore requires this application (or a compatible tool that understands trailer and/or /CCSProject).

9.2 Rolling project audit (insight log)

High-signal changes while a document is open are recorded in memory (js/core/project-audit.js) and written into the trailer on Save Project.

Cap Value
Rolling entry count 200 (oldest dropped)
Compressed audit size safety ~128 KB gzip of audit[] (oldest dropped until under)
Schema field audit[] (schema version 3.1+)

Entry shape (approx.): { id, t (ISO-8601), action, detail? }

Typical action codes: FIELD_CREATED, FIELD_FILLED, FIELD_SIGNED, FIELD_IMPORTED, TEXT_ADDED, IMAGE_ADDED, SHAPE_ADDED, OVERLAY_DELETED, OVERLAY_TRANSFORM, PAGE_CHANGE, SAVE_PROJECT, plus coarse EDIT fallbacks from history.

Guarantees / non-goals

9.3 Layered seal + form-signature attestation

Project schema may include a .seal object built so hashes do not loop on themselves (js/core/project-state.js, js/core/project-crypto.js):

Field Meaning
originHash SHA-256 of bytes when the document was first opened this session
contentHash SHA-256 of PDF bytes before /CCSProject is attached
schemaHash SHA-256 of schema JSON without .seal
Optional ECDSA Device-local WebCrypto signature over seal material (not a TSA)

Form signature fields (Editor fill): fixed attestation wording, signed name, ISO time, basic browser/device meta, and digests are stored on the field and included in schema → schemaHash. UI shows a compact caption with mid-length hashes; a body-level hover popover exposes full hex with Copy (js/features/signature.js).

This is device-local attestation, not certified legal e-sign / identity proofing.

9.4 Incident extraction (IT)

To extract project state / audit without opening the editor (supports catalog /CCSProject and legacy trailer):

Context How
Windows install package Drag .ccspdf or Export PDF onto tools\Extract Project Audit.bat
Windows install package powershell -File tools\scripts\extract-ccspdf-audit.ps1 -Path "…\file.ccspdf" -FullSchema -ExtractPdf
Source / offline pack powershell -File scripts\extract-ccspdf-audit.ps1 -Path "…\file.pdf" -FullSchema -ExtractPdf
Source / offline pack Drag onto scripts\Extract-Ccspdf-Audit.bat

Prefer trailer when both exist (same as app open path); use -PreferCatalog to force catalog.

Outputs (folder *_extract next to the source file):

File Content
*_summary.txt Storage source, integrity, seal hashes, form signatures, chronological audit
_audit.json / _audit.csv Machine-readable log
*_seal.json Layered seal when present
*_signatures.json Form signature attestations when present
*_schema.json Full schema JSON (-FullSchema)
*_body.pdf Trailer stripped, or source copy for catalog-only (-ExtractPdf)

Verifies the integrity hash using the same algorithm as the app (Uint8Array comma-join + salt CCS-SAVE-HASH-CHECK-S4lT!!). This is decode/gunzip, not QPDF password removal. Exit code 0 = integrity OK; 2 = hash mismatch (artifacts still written for investigation).

Training lab: USER-TRAINING.md Track D · SOP: WINDOWS-INSTALL-SOP.md §8.

(§9.4 continues the extract workflow formerly numbered §9.3 in older docs.)


10. Related documentation

Document Purpose
SYSTEM-PROFILE.md Owners, host fields
GOVERNANCE.md Process, residual risk
DEVELOPMENT-STANDARDS.md How to change code safely
HOW-TO.md Staff landing how-to (docs/index.html)
USER-GUIDE.md Operator how-to
NOTICE Third-party stack

Rendered for the browser from repository markdown. Edit the .md sources, then re-run powershell -File docs/marketing/render-docs.ps1 from the repo root.