@printwithsynergy/lens-pdf 0.3.0-beta.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +344 -0
  3. package/dist/browser/codexOverlay.d.ts +109 -0
  4. package/dist/browser/codexOverlay.d.ts.map +1 -0
  5. package/dist/browser/codexOverlay.js +256 -0
  6. package/dist/browser/codexOverlay.js.map +1 -0
  7. package/dist/browser/constants.d.ts +13 -0
  8. package/dist/browser/constants.d.ts.map +1 -0
  9. package/dist/browser/constants.js +13 -0
  10. package/dist/browser/constants.js.map +1 -0
  11. package/dist/browser/index.d.ts +211 -0
  12. package/dist/browser/index.d.ts.map +1 -0
  13. package/dist/browser/index.js +1190 -0
  14. package/dist/browser/index.js.map +1 -0
  15. package/dist/browser/pantone-gold.d.ts +59 -0
  16. package/dist/browser/pantone-gold.d.ts.map +1 -0
  17. package/dist/browser/pantone-gold.js +237 -0
  18. package/dist/browser/pantone-gold.js.map +1 -0
  19. package/dist/components/AnnotationCanvas.d.ts +27 -0
  20. package/dist/components/AnnotationCanvas.d.ts.map +1 -0
  21. package/dist/components/AnnotationCanvas.js +401 -0
  22. package/dist/components/AnnotationCanvas.js.map +1 -0
  23. package/dist/components/AnnotationNotesPanel.d.ts +15 -0
  24. package/dist/components/AnnotationNotesPanel.d.ts.map +1 -0
  25. package/dist/components/AnnotationNotesPanel.js +235 -0
  26. package/dist/components/AnnotationNotesPanel.js.map +1 -0
  27. package/dist/components/AnnotationThread.d.ts +18 -0
  28. package/dist/components/AnnotationThread.d.ts.map +1 -0
  29. package/dist/components/AnnotationThread.js +163 -0
  30. package/dist/components/AnnotationThread.js.map +1 -0
  31. package/dist/components/AnnotationToolbar.d.ts +39 -0
  32. package/dist/components/AnnotationToolbar.d.ts.map +1 -0
  33. package/dist/components/AnnotationToolbar.js +258 -0
  34. package/dist/components/AnnotationToolbar.js.map +1 -0
  35. package/dist/components/BoxOverlay.d.ts +20 -0
  36. package/dist/components/BoxOverlay.d.ts.map +1 -0
  37. package/dist/components/BoxOverlay.js +107 -0
  38. package/dist/components/BoxOverlay.js.map +1 -0
  39. package/dist/components/ColorPickerTool.d.ts +11 -0
  40. package/dist/components/ColorPickerTool.d.ts.map +1 -0
  41. package/dist/components/ColorPickerTool.js +220 -0
  42. package/dist/components/ColorPickerTool.js.map +1 -0
  43. package/dist/components/DensitometerTool.d.ts +25 -0
  44. package/dist/components/DensitometerTool.d.ts.map +1 -0
  45. package/dist/components/DensitometerTool.js +246 -0
  46. package/dist/components/DensitometerTool.js.map +1 -0
  47. package/dist/components/DielineInfoPanel.d.ts +27 -0
  48. package/dist/components/DielineInfoPanel.d.ts.map +1 -0
  49. package/dist/components/DielineInfoPanel.js +23 -0
  50. package/dist/components/DielineInfoPanel.js.map +1 -0
  51. package/dist/components/DielineOverlay.d.ts +10 -0
  52. package/dist/components/DielineOverlay.d.ts.map +1 -0
  53. package/dist/components/DielineOverlay.js +57 -0
  54. package/dist/components/DielineOverlay.js.map +1 -0
  55. package/dist/components/FindingsSidebar.d.ts +50 -0
  56. package/dist/components/FindingsSidebar.d.ts.map +1 -0
  57. package/dist/components/FindingsSidebar.js +78 -0
  58. package/dist/components/FindingsSidebar.js.map +1 -0
  59. package/dist/components/LayerCanvas.d.ts +30 -0
  60. package/dist/components/LayerCanvas.d.ts.map +1 -0
  61. package/dist/components/LayerCanvas.js +84 -0
  62. package/dist/components/LayerCanvas.js.map +1 -0
  63. package/dist/components/LayerPanel.d.ts +9 -0
  64. package/dist/components/LayerPanel.d.ts.map +1 -0
  65. package/dist/components/LayerPanel.js +144 -0
  66. package/dist/components/LayerPanel.js.map +1 -0
  67. package/dist/components/LensPDF.d.ts +61 -0
  68. package/dist/components/LensPDF.d.ts.map +1 -0
  69. package/dist/components/LensPDF.js +49 -0
  70. package/dist/components/LensPDF.js.map +1 -0
  71. package/dist/components/LensPDFDemo.d.ts +160 -0
  72. package/dist/components/LensPDFDemo.d.ts.map +1 -0
  73. package/dist/components/LensPDFDemo.js +1060 -0
  74. package/dist/components/LensPDFDemo.js.map +1 -0
  75. package/dist/components/LensPDFDemo.styles.d.ts +38 -0
  76. package/dist/components/LensPDFDemo.styles.d.ts.map +1 -0
  77. package/dist/components/LensPDFDemo.styles.js +282 -0
  78. package/dist/components/LensPDFDemo.styles.js.map +1 -0
  79. package/dist/components/LensPDFViewer.d.ts +79 -0
  80. package/dist/components/LensPDFViewer.d.ts.map +1 -0
  81. package/dist/components/LensPDFViewer.js +254 -0
  82. package/dist/components/LensPDFViewer.js.map +1 -0
  83. package/dist/components/MeasureTool.d.ts +16 -0
  84. package/dist/components/MeasureTool.d.ts.map +1 -0
  85. package/dist/components/MeasureTool.js +137 -0
  86. package/dist/components/MeasureTool.js.map +1 -0
  87. package/dist/components/MobileBottomSheet.d.ts +12 -0
  88. package/dist/components/MobileBottomSheet.d.ts.map +1 -0
  89. package/dist/components/MobileBottomSheet.js +113 -0
  90. package/dist/components/MobileBottomSheet.js.map +1 -0
  91. package/dist/components/MobileDrawer.d.ts +31 -0
  92. package/dist/components/MobileDrawer.d.ts.map +1 -0
  93. package/dist/components/MobileDrawer.js +67 -0
  94. package/dist/components/MobileDrawer.js.map +1 -0
  95. package/dist/components/PageCanvas.d.ts +33 -0
  96. package/dist/components/PageCanvas.d.ts.map +1 -0
  97. package/dist/components/PageCanvas.js +385 -0
  98. package/dist/components/PageCanvas.js.map +1 -0
  99. package/dist/components/PageNavigator.d.ts +18 -0
  100. package/dist/components/PageNavigator.d.ts.map +1 -0
  101. package/dist/components/PageNavigator.js +44 -0
  102. package/dist/components/PageNavigator.js.map +1 -0
  103. package/dist/components/SeparationCanvas.d.ts +12 -0
  104. package/dist/components/SeparationCanvas.d.ts.map +1 -0
  105. package/dist/components/SeparationCanvas.js +174 -0
  106. package/dist/components/SeparationCanvas.js.map +1 -0
  107. package/dist/components/TACHeatmapOverlay.d.ts +17 -0
  108. package/dist/components/TACHeatmapOverlay.d.ts.map +1 -0
  109. package/dist/components/TACHeatmapOverlay.js +119 -0
  110. package/dist/components/TACHeatmapOverlay.js.map +1 -0
  111. package/dist/components/ZoomControls.d.ts +11 -0
  112. package/dist/components/ZoomControls.d.ts.map +1 -0
  113. package/dist/components/ZoomControls.js +26 -0
  114. package/dist/components/ZoomControls.js.map +1 -0
  115. package/dist/components/defaultShellPlugins.d.ts +3 -0
  116. package/dist/components/defaultShellPlugins.d.ts.map +1 -0
  117. package/dist/components/defaultShellPlugins.js +273 -0
  118. package/dist/components/defaultShellPlugins.js.map +1 -0
  119. package/dist/components/index.d.ts +32 -0
  120. package/dist/components/index.d.ts.map +1 -0
  121. package/dist/components/index.js +32 -0
  122. package/dist/components/index.js.map +1 -0
  123. package/dist/components/presets.d.ts +8 -0
  124. package/dist/components/presets.d.ts.map +1 -0
  125. package/dist/components/presets.js +14 -0
  126. package/dist/components/presets.js.map +1 -0
  127. package/dist/components/shellPlugins.d.ts +105 -0
  128. package/dist/components/shellPlugins.d.ts.map +1 -0
  129. package/dist/components/shellPlugins.js +52 -0
  130. package/dist/components/shellPlugins.js.map +1 -0
  131. package/dist/components/useIsMobile.d.ts +16 -0
  132. package/dist/components/useIsMobile.d.ts.map +1 -0
  133. package/dist/components/useIsMobile.js +30 -0
  134. package/dist/components/useIsMobile.js.map +1 -0
  135. package/dist/fallback-pdfjs/index.d.ts +60 -0
  136. package/dist/fallback-pdfjs/index.d.ts.map +1 -0
  137. package/dist/fallback-pdfjs/index.js +163 -0
  138. package/dist/fallback-pdfjs/index.js.map +1 -0
  139. package/dist/host/LensPDFProvider.d.ts +36 -0
  140. package/dist/host/LensPDFProvider.d.ts.map +1 -0
  141. package/dist/host/LensPDFProvider.js +12 -0
  142. package/dist/host/LensPDFProvider.js.map +1 -0
  143. package/dist/host/index.d.ts +167 -0
  144. package/dist/host/index.d.ts.map +1 -0
  145. package/dist/host/index.js +173 -0
  146. package/dist/host/index.js.map +1 -0
  147. package/dist/host/pdfFallback.d.ts +50 -0
  148. package/dist/host/pdfFallback.d.ts.map +1 -0
  149. package/dist/host/pdfFallback.js +171 -0
  150. package/dist/host/pdfFallback.js.map +1 -0
  151. package/dist/host/pdfValidation.d.ts +45 -0
  152. package/dist/host/pdfValidation.d.ts.map +1 -0
  153. package/dist/host/pdfValidation.js +78 -0
  154. package/dist/host/pdfValidation.js.map +1 -0
  155. package/dist/host/shareLink.d.ts +80 -0
  156. package/dist/host/shareLink.d.ts.map +1 -0
  157. package/dist/host/shareLink.js +114 -0
  158. package/dist/host/shareLink.js.map +1 -0
  159. package/dist/host/useLensPDF.d.ts +73 -0
  160. package/dist/host/useLensPDF.d.ts.map +1 -0
  161. package/dist/host/useLensPDF.js +213 -0
  162. package/dist/host/useLensPDF.js.map +1 -0
  163. package/dist/index.d.ts +68 -0
  164. package/dist/index.d.ts.map +1 -0
  165. package/dist/index.js +62 -0
  166. package/dist/index.js.map +1 -0
  167. package/dist/plugin/context.d.ts +70 -0
  168. package/dist/plugin/context.d.ts.map +1 -0
  169. package/dist/plugin/context.js +16 -0
  170. package/dist/plugin/context.js.map +1 -0
  171. package/dist/plugin/findings-location.d.ts +53 -0
  172. package/dist/plugin/findings-location.d.ts.map +1 -0
  173. package/dist/plugin/findings-location.js +72 -0
  174. package/dist/plugin/findings-location.js.map +1 -0
  175. package/dist/plugin/index.d.ts +19 -0
  176. package/dist/plugin/index.d.ts.map +1 -0
  177. package/dist/plugin/index.js +16 -0
  178. package/dist/plugin/index.js.map +1 -0
  179. package/dist/plugin/registry.d.ts +61 -0
  180. package/dist/plugin/registry.d.ts.map +1 -0
  181. package/dist/plugin/registry.js +102 -0
  182. package/dist/plugin/registry.js.map +1 -0
  183. package/dist/plugin/services.d.ts +380 -0
  184. package/dist/plugin/services.d.ts.map +1 -0
  185. package/dist/plugin/services.js +104 -0
  186. package/dist/plugin/services.js.map +1 -0
  187. package/dist/plugin/types.d.ts +198 -0
  188. package/dist/plugin/types.d.ts.map +1 -0
  189. package/dist/plugin/types.js +24 -0
  190. package/dist/plugin/types.js.map +1 -0
  191. package/dist/types/index.d.ts +191 -0
  192. package/dist/types/index.d.ts.map +1 -0
  193. package/dist/types/index.js +95 -0
  194. package/dist/types/index.js.map +1 -0
  195. package/dist/units/index.d.ts +64 -0
  196. package/dist/units/index.d.ts.map +1 -0
  197. package/dist/units/index.js +98 -0
  198. package/dist/units/index.js.map +1 -0
  199. package/docs/architecture.md +90 -0
  200. package/docs/components.md +569 -0
  201. package/docs/contributing.md +78 -0
  202. package/docs/fallback.md +174 -0
  203. package/docs/lens-pdf-viewer.md +128 -0
  204. package/docs/licensing.md +78 -0
  205. package/docs/measurement-units.md +87 -0
  206. package/docs/plugins.md +256 -0
  207. package/docs/security.md +69 -0
  208. package/docs/server.md +212 -0
  209. package/docs/services.md +210 -0
  210. package/docs/share-links.md +111 -0
  211. package/docs/theming.md +164 -0
  212. package/docs/validation.md +83 -0
  213. package/package.json +139 -0
@@ -0,0 +1,174 @@
1
+ ---
2
+ title: "Fallback & capability detection"
3
+ description: "Tools self-hide when their backing service is unwired. Hosts can opt into graceful degradation with the in-browser pdf.js fallback adapter for page rendering, layers, and color sampling."
4
+ group: "Reference"
5
+ order: 4
6
+ ---
7
+
8
+ # Fallback behaviour & capability detection
9
+
10
+ LensPDF's components no longer all render the same regardless of what the
11
+ host wired. Instead each component picks one of three modes per render:
12
+
13
+ | Mode | When | Render |
14
+ | ---------- | ------------------------------------------------------------------------------------- | --------------------------------------- |
15
+ | `wired` | Host supplied a real implementation for the backing service. | Normal — use the service. |
16
+ | `fallback` | Service is unwired **and** `host.pdfFallback` is set. | Use the in-browser fallback adapter. |
17
+ | `hidden` | Service is unwired **and** no `pdfFallback` is set (or no fallback exists for it). | `return null` — the component disappears. |
18
+
19
+ The distinction matters: a host that wires a real service and gets back an
20
+ empty list still renders an empty state ("This PDF has no optional content
21
+ layers"), because the host explicitly opted in. A host that didn't wire the
22
+ service at all gets nothing — no panel, no menu item, no inert button.
23
+
24
+ ## Capability detection
25
+
26
+ The default services exported from `@printwithsynergy/lens-pdf/host` are
27
+ tagged with a non-enumerable symbol marker. `isUnwired(service)` returns
28
+ `true` for any of those defaults and `false` for anything a host substitutes
29
+ in. Components use this to choose their render mode.
30
+
31
+ The full set of unwired defaults is available as a single object:
32
+
33
+ ```ts
34
+ import { defaultUnwiredServices } from "@printwithsynergy/lens-pdf/host";
35
+ ```
36
+
37
+ Spread it and override only the services your host provides — no need
38
+ to manually `markUnwired` each field:
39
+
40
+ ```ts
41
+ import { isUnwired, useViewerServices } from "@printwithsynergy/lens-pdf/host";
42
+
43
+ const { layers } = useViewerServices();
44
+ if (isUnwired(layers)) {
45
+ // Host didn't wire the LayerService.
46
+ }
47
+ ```
48
+
49
+ You generally don't need `isUnwired` directly — `useFallbackMode(service)`
50
+ takes the service object and returns the three-state mode for you.
51
+
52
+ ## In-browser fallback (pdf.js)
53
+
54
+ Hosts that want graceful degradation for the base tools can ship a raw PDF
55
+ URL plus the bundled pdf.js adapter:
56
+
57
+ ```ts
58
+ import { createPdfJsFallback, ViewerHostContext } from "@printwithsynergy/lens-pdf/host";
59
+
60
+ const fallback = createPdfJsFallback({
61
+ pdfUrl: "/proofs/abc-signed.pdf",
62
+ // Optional — only needed if your bundler hasn't already configured
63
+ // pdf.js's worker.
64
+ workerSrc: "/pdfjs/pdf.worker.min.mjs",
65
+ });
66
+
67
+ <ViewerHostContext.Provider
68
+ value={{
69
+ apiBase: "",
70
+ jobApiBase: "",
71
+ readOnly: true,
72
+ pdfUrl: "/proofs/abc-signed.pdf",
73
+ pdfFallback: fallback,
74
+ debug: import.meta.env.DEV,
75
+ }}
76
+ >
77
+ {children}
78
+ </ViewerHostContext.Provider>
79
+ ```
80
+
81
+ Add `pdfjs-dist` to your app's dependencies (it's an *optional* peer dep of
82
+ `@printwithsynergy/lens-pdf` and loaded lazily via `import("pdfjs-dist")`,
83
+ so consumers that don't use the fallback pay no bundle cost).
84
+
85
+ ### What the fallback can do
86
+
87
+ | Tool / panel | Wired service | Falls back? |
88
+ | ------------------ | ------------------- | ------------------------------- |
89
+ | `PageCanvas` | `pageImages` | ✅ Renders pages with pdf.js. |
90
+ | `PageNavigator` | `pageImages`* | ✅ via `getPageCount()`. |
91
+ | `MeasureTool` | (none — needs dims) | ✅ via `getPageDimensions()`. |
92
+ | `LayerPanel` | `layers` | ✅ via `listLayers()`. |
93
+ | `ColorPickerTool` | `colorSample` | ✅ RGB sample only (no TAC). |
94
+ | `SeparationCanvas` | `separations` | ❌ pdf.js can't split inks. |
95
+ | `DensitometerTool` | `densitometer` | ❌ pdf.js can't split inks. |
96
+ | `TACHeatmapOverlay`| `tacHeatmap` | ❌ Needs server-side rendering. |
97
+ | `AnnotationCanvas` | `annotations` | ❌ Annotations need persistence. |
98
+ | `AnnotationThread` | `annotations` | ❌ Same. |
99
+ | `MobileDrawer` | `reports` (links) | ❌ Report URLs are host-built. |
100
+
101
+ \* `PageNavigator` reads page count from the host's page list, but
102
+ `getPageCount()` is exposed on the adapter so hosts that build their own
103
+ glue can bootstrap the page list from the PDF directly.
104
+
105
+ The three ❌ rows need real ink-channel separations, which only a
106
+ server-side renderer (Ghostscript, MuPDF with separation rendering, etc.)
107
+ can produce. pdf.js renders to RGB; there's no path to reconstruct CMYK
108
+ from the resulting raster. Those components stay hidden when their
109
+ dedicated services are unwired, fallback or no fallback.
110
+
111
+ For the preflight-grade tools, deploy the optional reference server
112
+ under [`server/`](https://github.com/Printwithsynergy/lens-pdf/tree/main/server)
113
+ or wire `services.separations` / `services.densitometer` /
114
+ `services.tacHeatmap` to your own backend. See
115
+ [server.md](./server.md) for the contract and a wiring example.
116
+
117
+ ### Debug logging
118
+
119
+ Set `debug: true` on the host context (typically `import.meta.env.DEV` or
120
+ `process.env.NODE_ENV !== "production"`) and every self-hide gets a
121
+ one-shot `console.info`:
122
+
123
+ ```
124
+ [lens-pdf] DensitometerTool hidden — host did not wire `services.densitometer`.
125
+ Provide an implementation, or set `pdfFallback` on the host context to use the
126
+ in-browser PDF fallback.
127
+ ```
128
+
129
+ The log is deduped per-component-name so re-renders don't spam the console.
130
+ With `debug` off (the default) hidden components are silent.
131
+
132
+ ## Security
133
+
134
+ LensPDF is a pure renderer. It does not authenticate, sign, or rate-limit
135
+ any of the URLs it consumes. Specifically:
136
+
137
+ - The `pdfUrl` you put on the host context is fetched verbatim by the
138
+ user's browser. If a downstream user shouldn't be able to read that PDF,
139
+ the host must enforce that with signed/expiring/scoped URLs **before**
140
+ handing the URL to the viewer.
141
+ - Service URL builders (`getPageImageUrl`, `getChannelImageUrl`, etc.) are
142
+ the same — whatever URL the host returns is what the browser fetches.
143
+ - The pdf.js fallback adapter parses the PDF entirely client-side. If the
144
+ PDF blob contains data the user shouldn't see (other pages, hidden
145
+ layers, embedded files), they will be able to extract it via DevTools.
146
+ Strip / redact upstream if that matters.
147
+ - The viewer never stores credentials. If your services need auth, do it
148
+ at the URL level (signed query strings, cookies the browser already
149
+ carries, etc.).
150
+ - `readOnly: true` hides write-only UI but is **not** a security boundary —
151
+ it's a UX convenience. Enforce write-side authz on the server.
152
+
153
+ ## The service contract (for non-JS hosts)
154
+
155
+ If you're wiring LensPDF from a PHP, Laravel, Perl, Rails, or any other
156
+ backend, your job is just to expose HTTP endpoints that match the shape
157
+ each `ViewerService` URL builder calls. There is no SDK to install — the
158
+ viewer is decoupled by design. The minimal contract is:
159
+
160
+ | Service | Endpoint shape (your choice) | Returns |
161
+ | ----------------- | --------------------------------------------------------------- | ------------------------ |
162
+ | `pageImages` | `GET /pdf/{job}/page/{n}.png?dpi=N` | PNG bytes |
163
+ | `layers` | `GET /pdf/{job}/layers` + `GET /pdf/{job}/layer/{i}.png?dpi=N` | JSON list + PNGs |
164
+ | `separations` | `GET /pdf/{job}/channel/{name}.png?dpi=N` | PNG bytes (greyscale) |
165
+ | `tacHeatmap` | `GET /pdf/{job}/tac.png?dpi=N&limit=L` + `…/tac.json?...` | PNG + JSON runs |
166
+ | `colorSample` | `GET /pdf/{job}/color?page=N&x=X&y=Y` | `ColorSample` JSON |
167
+ | `densitometer` | `GET /pdf/{job}/density?page=N&x=X&y=Y&limit=L` | `DensitometerSample` JSON |
168
+ | `annotations` | CRUD on `/pdf/{job}/annotations[/id]` | `AnnotationEntry` JSON |
169
+ | `reports` | `GET /pdf/{job}/report.html` + `GET /pdf/{job}/report.pdf` | Static URLs |
170
+
171
+ Pick whatever URL scheme fits your framework's routing. The viewer's
172
+ synchronous URL builders just need to produce the right string — they
173
+ don't care what's on the other end as long as it returns the documented
174
+ content-type.
@@ -0,0 +1,128 @@
1
+ ---
2
+ title: "LensPDFViewer (one-line viewer)"
3
+ description: "Default high-level composition. One JSX line gets you a working multi-page PDF viewer with auto-discovered pages, layers, zoom, color picker, and measure tool."
4
+ group: "Getting started"
5
+ order: 3
6
+ ---
7
+
8
+ # LensPDFViewer
9
+
10
+ The default composition. Drop it into any React 19 project and you have
11
+ a working PDF viewer — no host-context wiring, no per-page mounting, no
12
+ worrying about pdf.js workers.
13
+
14
+ ```tsx
15
+ import { LensPDFViewer } from "@printwithsynergy/lens-pdf";
16
+
17
+ export function MyViewer() {
18
+ return <LensPDFViewer pdfUrl="https://example.com/file.pdf" />;
19
+ }
20
+ ```
21
+
22
+ ## What it does
23
+
24
+ - Builds a pdf.js fallback adapter from `pdfUrl` and configures the
25
+ worker URL.
26
+ - Wraps `ViewerHostContext` and (optionally) `ViewerServicesContext`.
27
+ - Auto-discovers page count, page dimensions, and OCG layers from the
28
+ PDF.
29
+ - Renders every page in a scrollable, lazy-loaded virtual list (or
30
+ one at a time with `mode="single"`).
31
+ - Seeds initial layer state from the PDF's defaults (default-on
32
+ layers enabled, default-off off).
33
+ - Ships a responsive default toolbar with zoom, layers, color picker,
34
+ and measure tool.
35
+ - Reflows to a bottom-drawer layout under 768 px wide.
36
+
37
+ ## Props
38
+
39
+ | Prop | Type | Default | Notes |
40
+ | --- | --- | --- | --- |
41
+ | `pdfUrl` | `string` | required | PDF URL fetched by the user's browser. **Sign / scope upstream** — see [Security](#security). |
42
+ | `workerSrc` | `string` | `defaultPdfWorkerSrc` (unpkg, pinned to bundled pdfjs-dist version) | Override to self-host the pdf.js worker. |
43
+ | `services` | `ViewerServices` | _(unset)_ | Pass when your host has a backend with separations / densitometer / TAC / annotations / reports. Components for unwired services hide silently. |
44
+ | `tokens` | `ThemeTokens` | `defaultThemeTokens` | Brand palette. |
45
+ | `className` | `string` | `""` | Class hook on the outer shell. |
46
+ | `mode` | `"scroll"` \| `"single"` | `"scroll"` | Page rendering mode. |
47
+ | `tools` | `ReadonlyArray<"zoom"\|"layers"\|"color-picker"\|"measure">` | all four | Toolbar contents. Pass `[]` for no toolbar. |
48
+ | `initialZoom` | `number` | `100` | Starting zoom percent. |
49
+ | `brand` | `string` | _(none)_ | Optional brand label rendered in the top-left of the toolbar. |
50
+ | `header` | `(state: LensPDFViewerState) => ReactNode` | _(built-in toolbar)_ | Replace the default toolbar. Receives viewer state. |
51
+ | `sidebar` | `(state: LensPDFViewerState) => ReactNode` | _(none)_ | Inject a sidebar next to the page list. |
52
+ | `footer` | `ReactNode` | _(none)_ | Static footer content rendered below the viewer stage. |
53
+
54
+ ## What gets hidden when no services are wired
55
+
56
+ The composition relies on the host-agnostic capability-detection
57
+ contract from [fallback.md](./fallback.md). With only `pdfUrl` set:
58
+
59
+ | Component | Visible | Notes |
60
+ | --- | --- | --- |
61
+ | Page rendering, navigation, zoom | ✅ | pdf.js fallback. |
62
+ | Layer panel | ✅ if PDF has OCGs | pdf.js fallback. |
63
+ | Color picker | ✅ | RGB only (no TAC). |
64
+ | Measure tool | ✅ | Page dimensions from PDF. |
65
+ | Separations | ❌ hidden | Needs a backend; pdf.js renders RGB only. |
66
+ | Densitometer | ❌ hidden | Same. |
67
+ | TAC heatmap | ❌ hidden | Same. |
68
+ | Annotations | ❌ hidden | Needs persistence. |
69
+ | Report links | ❌ hidden | Needs a backend. |
70
+
71
+ Pass `services` to wire the backend-dependent ones. The reference
72
+ server in [`server/`](https://github.com/Printwithsynergy/lens-pdf/tree/main/server)
73
+ is a turnkey option — see [server.md](./server.md).
74
+
75
+ ## Slot props
76
+
77
+ Slot props let you replace individual regions without reimplementing the
78
+ entire viewer. Each slot accepts a static `ReactNode` or a render
79
+ function that receives the current `LensPDFViewerState`:
80
+
81
+ ```tsx
82
+ import { LensPDFViewer } from "@printwithsynergy/lens-pdf/components";
83
+
84
+ <LensPDFViewer
85
+ pdfUrl={url}
86
+ header={(state) => (
87
+ <div>
88
+ <span>Page {state.currentPage}</span>
89
+ <button onClick={() => state.setZoom(state.zoom + 25)}>Zoom in</button>
90
+ </div>
91
+ )}
92
+ footer={<p>Powered by LensPDF</p>}
93
+ />
94
+ ```
95
+
96
+ `LensPDFViewerState` exposes: `zoom`, `setZoom`, `currentPage`,
97
+ `setCurrentPage`, `pageCount`, `activeTool`, `setActiveTool`,
98
+ `enabledLayers`, `toggleLayer`, `setAllLayers`, `hasLayers`,
99
+ `layersOpen`, `setLayersOpen`.
100
+
101
+ ## Custom layouts
102
+
103
+ `<LensPDFViewer>` is purely additive. The lower-level surface stays
104
+ exactly as before — for bespoke layouts compose `PageCanvas`,
105
+ `LayerPanel`, `MeasureTool`, `ColorPickerTool`, etc. with your own
106
+ context providers. See [components.md](./components.md) for the per-
107
+ component reference.
108
+
109
+ ## Other integration tiers
110
+
111
+ - **Less boilerplate** — [`<LensPDFDemo>`](./components.md#drop-in-demo)
112
+ bakes in upload, URL paste, drag-drop, and validation. ~5 lines.
113
+ - **More control** — [`useLensPDF()`](./share-links.md) +
114
+ `<LensPDFProvider>` manage state while you build the layout.
115
+ - **Full custom** — wire `ViewerHostContext` + `ViewerServicesContext`
116
+ yourself. See [architecture.md](./architecture.md).
117
+
118
+ ## Security
119
+
120
+ The `pdfUrl` you pass is fetched verbatim by the user's browser. If a
121
+ user shouldn't be able to read that PDF, the host must enforce that
122
+ upstream — sign the URL, scope it, expire it. The viewer is a pure
123
+ renderer and doesn't authenticate anything.
124
+
125
+ The pdf.js worker is loaded from unpkg by default. For deployments
126
+ that can't reach unpkg (intranet, air-gapped CI, strict CSPs), set
127
+ `workerSrc` to a self-hosted URL or import the worker into your app's
128
+ build and pass its bundler-resolved URL.
@@ -0,0 +1,78 @@
1
+ ---
2
+ title: "Licensing"
3
+ description: "AGPL-3.0-or-later licensing terms for LensPDF — what hosts can do, what they must do, and how to request alternative terms for proprietary use."
4
+ group: "Project"
5
+ order: 11
6
+ ---
7
+
8
+ # Licensing
9
+
10
+ LensPDF is published under **AGPL-3.0-or-later** — the GNU Affero
11
+ General Public License, version 3 or any later version. The full
12
+ licence text lives in [`LICENSE`](https://github.com/Printwithsynergy/lens-pdf/blob/main/LICENSE)
13
+ at the root of the repository, and `package.json` declares the same
14
+ SPDX identifier so npm tooling picks it up automatically.
15
+
16
+ ## What you can do
17
+
18
+ - Use LensPDF in any application, commercial or non-commercial.
19
+ - Modify the source — fork it, vendor it, patch it for your build.
20
+ - Redistribute it, modified or unmodified, on any platform.
21
+ - Combine it with other AGPL-compatible code.
22
+
23
+ ## What you must do
24
+
25
+ If you distribute LensPDF (binary or source) — or **make it available
26
+ over a network** — you have to:
27
+
28
+ - Make the **complete corresponding source code** of your modified
29
+ version available, under AGPL-3.0-or-later, to every recipient and
30
+ every user interacting with it remotely.
31
+ - Preserve the copyright and licence notices from this repository.
32
+ - State the changes you made, with dates.
33
+ - License any larger work that links against LensPDF under
34
+ AGPL-3.0-or-later.
35
+
36
+ The "**make available over a network**" clause (§13 of AGPL-3) is the
37
+ key difference from GPLv3: a SaaS that ships LensPDF — even if no one
38
+ ever downloads a binary — has to offer the source to its users. Hosting
39
+ a hosted PDF viewer that imports `@printwithsynergy/lens-pdf` triggers
40
+ this.
41
+
42
+ ## Third-party code
43
+
44
+ LensPDF re-exports and bundles open-source dependencies:
45
+
46
+ - **pdf.js** (Apache-2.0) — fallback rendering adapter.
47
+ - **fabric.js** (MIT) — annotation canvas (optional peer).
48
+ - **React** (MIT) — peer.
49
+
50
+ Their licences are compatible with AGPL-3.0-or-later. When you ship
51
+ LensPDF you also ship those dependencies, so check their notices in
52
+ `node_modules` and reproduce them if your distribution channel
53
+ requires it.
54
+
55
+ ## Alternative / commercial licensing
56
+
57
+ The AGPL-3.0 reciprocity requirement is incompatible with some
58
+ proprietary or closed-source products. If you want to embed LensPDF
59
+ in a product you can't or don't want to release under AGPL-3.0,
60
+ contact **licensing@printwithsynergy.com** to discuss commercial
61
+ terms.
62
+
63
+ ## Why AGPL?
64
+
65
+ LensPDF is the rendering core for the printwithsynergy OSS PDF
66
+ tooling family. Releasing it under AGPL keeps the core honest:
67
+ improvements anyone makes — even hidden behind a SaaS — flow back to
68
+ the community. Hosts that want a non-reciprocal arrangement can buy
69
+ into the commercial track above; everyone else gets the same code on
70
+ the same terms.
71
+
72
+ ## Contributor licensing
73
+
74
+ Contributions are accepted under the same AGPL-3.0-or-later terms by
75
+ default. By submitting a PR you agree your changes are licensed that
76
+ way, and that you have the right to submit them. See
77
+ [Contributing](/docs/contributing) for the boundary rules and PR
78
+ style.
@@ -0,0 +1,87 @@
1
+ ---
2
+ title: "Measurement units"
3
+ description: "Built-in millimetre, inch, point, pica, and agate definitions, plus the MeasurementUnit protocol for adding custom units to the MeasureTool."
4
+ group: "Reference"
5
+ order: 7
6
+ ---
7
+
8
+ # Measurement units
9
+
10
+ `MeasureTool` accepts a `units` prop. The five built-ins cover most print
11
+ workflows; pass any subset, or write your own conforming to the
12
+ `MeasurementUnit` Protocol.
13
+
14
+ ## Built-ins
15
+
16
+ ```ts
17
+ import {
18
+ mmUnit,
19
+ inchUnit,
20
+ pointUnit,
21
+ picaUnit,
22
+ agateUnit,
23
+ defaultMeasurementUnits, // [mm, in, pt]
24
+ allMeasurementUnits, // [mm, in, pt, pica, agate]
25
+ } from "@printwithsynergy/lens-pdf/units";
26
+ ```
27
+
28
+ | Unit | id | label | Conversion (from PDF points) |
29
+ | --- | --- | --- | --- |
30
+ | Millimetre | `mm` | `mm` | `pt × 25.4 / 72` |
31
+ | Inch | `in` | `in` | `pt / 72` |
32
+ | Point | `pt` | `pt` | identity (PDF native) |
33
+ | Pica | `pica` | `pc` | `pt / 12` |
34
+ | Agate | `agate` | `ag` | `pt / 5.5` |
35
+
36
+ `MeasureTool` defaults to `defaultMeasurementUnits` (mm, in, pt). Pass
37
+ `allMeasurementUnits` to add pica + agate, or supply a custom subset.
38
+
39
+ ```tsx
40
+ import { MeasureTool } from "@printwithsynergy/lens-pdf/components";
41
+ import { allMeasurementUnits } from "@printwithsynergy/lens-pdf/units";
42
+
43
+ <MeasureTool
44
+ pageWidthPts={612}
45
+ pageHeightPts={792}
46
+ canvasWidth={800}
47
+ canvasHeight={1036}
48
+ units={allMeasurementUnits}
49
+ />;
50
+ ```
51
+
52
+ ## Custom units
53
+
54
+ The Protocol is small — anchor your conversions to PDF points (1 pt =
55
+ 1/72 inch) and you're done.
56
+
57
+ ```ts
58
+ import type { MeasurementUnit } from "@printwithsynergy/lens-pdf/plugin";
59
+
60
+ export const cmUnit: MeasurementUnit = {
61
+ id: "cm",
62
+ label: "cm",
63
+ fromPoints: (pts) => (pts * 25.4) / 72 / 10,
64
+ toPoints: (cm) => (cm * 10 * 72) / 25.4,
65
+ };
66
+
67
+ export const emUnit: MeasurementUnit = {
68
+ id: "em",
69
+ label: "em",
70
+ // 1em = 12pt by typographic convention; adjust if you have a real
71
+ // type-size in scope.
72
+ fromPoints: (pts) => pts / 12,
73
+ toPoints: (em) => em * 12,
74
+ };
75
+ ```
76
+
77
+ Pass them to `MeasureTool` directly, or merge with the built-ins:
78
+
79
+ ```tsx
80
+ <MeasureTool
81
+ units={[mmUnit, cmUnit, inchUnit, pointUnit]}
82
+ /* … */
83
+ />;
84
+ ```
85
+
86
+ The `id` is used for keying / preference storage; keep it stable across
87
+ versions if you persist user choice.