Skip to content

Architecture overview

InkLayer Core separates PDF and annotation behavior from product UI. React, Vue, or Vanilla JavaScript renders the toolbar, sidebar, dialogs, and application layout; Core provides the same document behavior and data model in every integration.

Start with the public surface

Most applications only need the following public surfaces:

SurfacePurpose
createInkLayer()Creates one instance and connects its Viewer, Annotation engine, Page Flow, and installed Capabilities
core.viewerLoads PDFs and handles pages, zoom, navigation, search, selection, outlines, and thumbnails
core.annotationsSelects tools, creates and edits annotations, and exposes the annotation Repository
core.annotationTypesRegisters and inspects built-in or custom annotation types
core.capabilitiesExposes services installed for this instance
Secondary import/export entriesImport native PDF annotations or generate PDF/Excel output without adding those heavier dependencies to the main Viewer path

The internal PDF.js and Konva objects are implementation details. Applications work through Core's public APIs and receive detached, serializable data instead of renderer nodes or mutable internal collections.

How data moves through Core

  1. The application gives core.load() a URL or PDF bytes. The Viewer uses PDF.js to open the document.
  2. Page Flow creates page shells and mounts the PDF canvas, text layer, and annotation layer only where needed.
  3. Annotation operations update core.annotations.repository. The Konva renderer projects that canonical data onto the page; the canvas is not the source of truth.
  4. Importers convert native PDF annotations into the same annotation model. Exporters read the source PDF bytes and current annotations, then return new PDF or Excel bytes.
  5. The application decides whether generated output is downloaded, uploaded, or printed.

This separation is why annotation data can be saved without serializing Canvas nodes, and why React, Vue, and Vanilla integrations can share the same behavior.

Dependency direction

Core keeps its internal dependencies pointing in one direction:

LayerResponsibility and allowed dependencies
Domain and RepositorySerializable annotation data and storage operations; no DOM, PDF.js, or Konva dependency
GeometryCoordinate, transform, and color calculations; independent of rendering frameworks
ViewerPDF.js loading, page rendering, text layers, search, outlines, thumbnails, and document lifecycle
Annotation and Konva rendererAnnotation interaction and visual projection; depends on the domain, geometry, Repository, and Konva
Import and exportConverts between external files and the canonical model; published as separate package entries
Browser platform and CapabilitiesConnects environment-specific actions and application-provided services to one instance

npm run check:dependencies checks local TypeScript imports for cycles, forbidden layer edges, and accidental framework dependencies.

Instance ownership and cleanup

Each Core instance owns its document loading tasks, rendered pages, text layers, annotation layers, event listeners, and temporary browser resources. Replacing a document releases resources belonging to the previous document. Calling core.destroy() releases everything owned by that instance and is safe to call more than once.

An annotation Repository is the main ownership exception: it can be owned by Core or borrowed from the application. A borrowed Repository remains available after the Core instance is destroyed. See Save and restore annotations for the persistence model.

Where the framework boundary sits

The boundary is based on responsibility, not visibility. A search feature, for example, is split across both sides: Core extracts text, finds matches, and navigates to a result; the application renders the search field and result list.

Continue with Core boundary for a feature-by-feature responsibility table and the rules used to decide where new behavior belongs.

Released under the MIT License.