@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.
- package/LICENSE +661 -0
- package/README.md +344 -0
- package/dist/browser/codexOverlay.d.ts +109 -0
- package/dist/browser/codexOverlay.d.ts.map +1 -0
- package/dist/browser/codexOverlay.js +256 -0
- package/dist/browser/codexOverlay.js.map +1 -0
- package/dist/browser/constants.d.ts +13 -0
- package/dist/browser/constants.d.ts.map +1 -0
- package/dist/browser/constants.js +13 -0
- package/dist/browser/constants.js.map +1 -0
- package/dist/browser/index.d.ts +211 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +1190 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/pantone-gold.d.ts +59 -0
- package/dist/browser/pantone-gold.d.ts.map +1 -0
- package/dist/browser/pantone-gold.js +237 -0
- package/dist/browser/pantone-gold.js.map +1 -0
- package/dist/components/AnnotationCanvas.d.ts +27 -0
- package/dist/components/AnnotationCanvas.d.ts.map +1 -0
- package/dist/components/AnnotationCanvas.js +401 -0
- package/dist/components/AnnotationCanvas.js.map +1 -0
- package/dist/components/AnnotationNotesPanel.d.ts +15 -0
- package/dist/components/AnnotationNotesPanel.d.ts.map +1 -0
- package/dist/components/AnnotationNotesPanel.js +235 -0
- package/dist/components/AnnotationNotesPanel.js.map +1 -0
- package/dist/components/AnnotationThread.d.ts +18 -0
- package/dist/components/AnnotationThread.d.ts.map +1 -0
- package/dist/components/AnnotationThread.js +163 -0
- package/dist/components/AnnotationThread.js.map +1 -0
- package/dist/components/AnnotationToolbar.d.ts +39 -0
- package/dist/components/AnnotationToolbar.d.ts.map +1 -0
- package/dist/components/AnnotationToolbar.js +258 -0
- package/dist/components/AnnotationToolbar.js.map +1 -0
- package/dist/components/BoxOverlay.d.ts +20 -0
- package/dist/components/BoxOverlay.d.ts.map +1 -0
- package/dist/components/BoxOverlay.js +107 -0
- package/dist/components/BoxOverlay.js.map +1 -0
- package/dist/components/ColorPickerTool.d.ts +11 -0
- package/dist/components/ColorPickerTool.d.ts.map +1 -0
- package/dist/components/ColorPickerTool.js +220 -0
- package/dist/components/ColorPickerTool.js.map +1 -0
- package/dist/components/DensitometerTool.d.ts +25 -0
- package/dist/components/DensitometerTool.d.ts.map +1 -0
- package/dist/components/DensitometerTool.js +246 -0
- package/dist/components/DensitometerTool.js.map +1 -0
- package/dist/components/DielineInfoPanel.d.ts +27 -0
- package/dist/components/DielineInfoPanel.d.ts.map +1 -0
- package/dist/components/DielineInfoPanel.js +23 -0
- package/dist/components/DielineInfoPanel.js.map +1 -0
- package/dist/components/DielineOverlay.d.ts +10 -0
- package/dist/components/DielineOverlay.d.ts.map +1 -0
- package/dist/components/DielineOverlay.js +57 -0
- package/dist/components/DielineOverlay.js.map +1 -0
- package/dist/components/FindingsSidebar.d.ts +50 -0
- package/dist/components/FindingsSidebar.d.ts.map +1 -0
- package/dist/components/FindingsSidebar.js +78 -0
- package/dist/components/FindingsSidebar.js.map +1 -0
- package/dist/components/LayerCanvas.d.ts +30 -0
- package/dist/components/LayerCanvas.d.ts.map +1 -0
- package/dist/components/LayerCanvas.js +84 -0
- package/dist/components/LayerCanvas.js.map +1 -0
- package/dist/components/LayerPanel.d.ts +9 -0
- package/dist/components/LayerPanel.d.ts.map +1 -0
- package/dist/components/LayerPanel.js +144 -0
- package/dist/components/LayerPanel.js.map +1 -0
- package/dist/components/LensPDF.d.ts +61 -0
- package/dist/components/LensPDF.d.ts.map +1 -0
- package/dist/components/LensPDF.js +49 -0
- package/dist/components/LensPDF.js.map +1 -0
- package/dist/components/LensPDFDemo.d.ts +160 -0
- package/dist/components/LensPDFDemo.d.ts.map +1 -0
- package/dist/components/LensPDFDemo.js +1060 -0
- package/dist/components/LensPDFDemo.js.map +1 -0
- package/dist/components/LensPDFDemo.styles.d.ts +38 -0
- package/dist/components/LensPDFDemo.styles.d.ts.map +1 -0
- package/dist/components/LensPDFDemo.styles.js +282 -0
- package/dist/components/LensPDFDemo.styles.js.map +1 -0
- package/dist/components/LensPDFViewer.d.ts +79 -0
- package/dist/components/LensPDFViewer.d.ts.map +1 -0
- package/dist/components/LensPDFViewer.js +254 -0
- package/dist/components/LensPDFViewer.js.map +1 -0
- package/dist/components/MeasureTool.d.ts +16 -0
- package/dist/components/MeasureTool.d.ts.map +1 -0
- package/dist/components/MeasureTool.js +137 -0
- package/dist/components/MeasureTool.js.map +1 -0
- package/dist/components/MobileBottomSheet.d.ts +12 -0
- package/dist/components/MobileBottomSheet.d.ts.map +1 -0
- package/dist/components/MobileBottomSheet.js +113 -0
- package/dist/components/MobileBottomSheet.js.map +1 -0
- package/dist/components/MobileDrawer.d.ts +31 -0
- package/dist/components/MobileDrawer.d.ts.map +1 -0
- package/dist/components/MobileDrawer.js +67 -0
- package/dist/components/MobileDrawer.js.map +1 -0
- package/dist/components/PageCanvas.d.ts +33 -0
- package/dist/components/PageCanvas.d.ts.map +1 -0
- package/dist/components/PageCanvas.js +385 -0
- package/dist/components/PageCanvas.js.map +1 -0
- package/dist/components/PageNavigator.d.ts +18 -0
- package/dist/components/PageNavigator.d.ts.map +1 -0
- package/dist/components/PageNavigator.js +44 -0
- package/dist/components/PageNavigator.js.map +1 -0
- package/dist/components/SeparationCanvas.d.ts +12 -0
- package/dist/components/SeparationCanvas.d.ts.map +1 -0
- package/dist/components/SeparationCanvas.js +174 -0
- package/dist/components/SeparationCanvas.js.map +1 -0
- package/dist/components/TACHeatmapOverlay.d.ts +17 -0
- package/dist/components/TACHeatmapOverlay.d.ts.map +1 -0
- package/dist/components/TACHeatmapOverlay.js +119 -0
- package/dist/components/TACHeatmapOverlay.js.map +1 -0
- package/dist/components/ZoomControls.d.ts +11 -0
- package/dist/components/ZoomControls.d.ts.map +1 -0
- package/dist/components/ZoomControls.js +26 -0
- package/dist/components/ZoomControls.js.map +1 -0
- package/dist/components/defaultShellPlugins.d.ts +3 -0
- package/dist/components/defaultShellPlugins.d.ts.map +1 -0
- package/dist/components/defaultShellPlugins.js +273 -0
- package/dist/components/defaultShellPlugins.js.map +1 -0
- package/dist/components/index.d.ts +32 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +32 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/presets.d.ts +8 -0
- package/dist/components/presets.d.ts.map +1 -0
- package/dist/components/presets.js +14 -0
- package/dist/components/presets.js.map +1 -0
- package/dist/components/shellPlugins.d.ts +105 -0
- package/dist/components/shellPlugins.d.ts.map +1 -0
- package/dist/components/shellPlugins.js +52 -0
- package/dist/components/shellPlugins.js.map +1 -0
- package/dist/components/useIsMobile.d.ts +16 -0
- package/dist/components/useIsMobile.d.ts.map +1 -0
- package/dist/components/useIsMobile.js +30 -0
- package/dist/components/useIsMobile.js.map +1 -0
- package/dist/fallback-pdfjs/index.d.ts +60 -0
- package/dist/fallback-pdfjs/index.d.ts.map +1 -0
- package/dist/fallback-pdfjs/index.js +163 -0
- package/dist/fallback-pdfjs/index.js.map +1 -0
- package/dist/host/LensPDFProvider.d.ts +36 -0
- package/dist/host/LensPDFProvider.d.ts.map +1 -0
- package/dist/host/LensPDFProvider.js +12 -0
- package/dist/host/LensPDFProvider.js.map +1 -0
- package/dist/host/index.d.ts +167 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +173 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/pdfFallback.d.ts +50 -0
- package/dist/host/pdfFallback.d.ts.map +1 -0
- package/dist/host/pdfFallback.js +171 -0
- package/dist/host/pdfFallback.js.map +1 -0
- package/dist/host/pdfValidation.d.ts +45 -0
- package/dist/host/pdfValidation.d.ts.map +1 -0
- package/dist/host/pdfValidation.js +78 -0
- package/dist/host/pdfValidation.js.map +1 -0
- package/dist/host/shareLink.d.ts +80 -0
- package/dist/host/shareLink.d.ts.map +1 -0
- package/dist/host/shareLink.js +114 -0
- package/dist/host/shareLink.js.map +1 -0
- package/dist/host/useLensPDF.d.ts +73 -0
- package/dist/host/useLensPDF.d.ts.map +1 -0
- package/dist/host/useLensPDF.js +213 -0
- package/dist/host/useLensPDF.js.map +1 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/context.d.ts +70 -0
- package/dist/plugin/context.d.ts.map +1 -0
- package/dist/plugin/context.js +16 -0
- package/dist/plugin/context.js.map +1 -0
- package/dist/plugin/findings-location.d.ts +53 -0
- package/dist/plugin/findings-location.d.ts.map +1 -0
- package/dist/plugin/findings-location.js +72 -0
- package/dist/plugin/findings-location.js.map +1 -0
- package/dist/plugin/index.d.ts +19 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +16 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/registry.d.ts +61 -0
- package/dist/plugin/registry.d.ts.map +1 -0
- package/dist/plugin/registry.js +102 -0
- package/dist/plugin/registry.js.map +1 -0
- package/dist/plugin/services.d.ts +380 -0
- package/dist/plugin/services.d.ts.map +1 -0
- package/dist/plugin/services.js +104 -0
- package/dist/plugin/services.js.map +1 -0
- package/dist/plugin/types.d.ts +198 -0
- package/dist/plugin/types.d.ts.map +1 -0
- package/dist/plugin/types.js +24 -0
- package/dist/plugin/types.js.map +1 -0
- package/dist/types/index.d.ts +191 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +95 -0
- package/dist/types/index.js.map +1 -0
- package/dist/units/index.d.ts +64 -0
- package/dist/units/index.d.ts.map +1 -0
- package/dist/units/index.js +98 -0
- package/dist/units/index.js.map +1 -0
- package/docs/architecture.md +90 -0
- package/docs/components.md +569 -0
- package/docs/contributing.md +78 -0
- package/docs/fallback.md +174 -0
- package/docs/lens-pdf-viewer.md +128 -0
- package/docs/licensing.md +78 -0
- package/docs/measurement-units.md +87 -0
- package/docs/plugins.md +256 -0
- package/docs/security.md +69 -0
- package/docs/server.md +212 -0
- package/docs/services.md +210 -0
- package/docs/share-links.md +111 -0
- package/docs/theming.md +164 -0
- package/docs/validation.md +83 -0
- package/package.json +139 -0
package/docs/fallback.md
ADDED
|
@@ -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.
|