@file-viewer/core 2.1.28 → 2.1.30

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.
@@ -217,6 +217,8 @@ export interface FileRenderExportAdapter {
217
217
  exportHtml?: boolean;
218
218
  includeDocumentStyles?: boolean;
219
219
  beforeSnapshot?: () => Promise<void> | void;
220
+ /** Live page surfaces used by the interactive print-mask designer. */
221
+ getPrintMaskPages?: () => readonly HTMLElement[];
220
222
  printStyle?: string | ((options: FileRenderExportOptions) => Promise<string> | string);
221
223
  toHtml?: (options: FileRenderExportOptions) => Promise<string> | string;
222
224
  }
@@ -806,6 +808,8 @@ export interface FileViewerPrintMaskRegion {
806
808
  top: number;
807
809
  width: number;
808
810
  height: number;
811
+ /** Zero-based rendered page index. Omitted regions retain legacy whole-document coordinates. */
812
+ pageIndex?: number;
809
813
  }
810
814
  export interface FileViewerPrintMaskOptions {
811
815
  /** Solid black cover blocks, matching common OFD/business redaction UX. */
@@ -1,5 +1,6 @@
1
1
  import type { FileViewerPrintMaskOptions, FileViewerPrintMaskRegion } from '../contracts/types';
2
2
  export declare const normalizeFileViewerPrintMaskRegion: (region: Partial<FileViewerPrintMaskRegion> | null | undefined) => FileViewerPrintMaskRegion | null;
3
3
  export declare const normalizeFileViewerPrintMaskOptions: (mask?: FileViewerPrintMaskOptions | null) => FileViewerPrintMaskOptions | null;
4
- export declare const buildFileViewerPrintMaskOverlayHtml: (mask?: FileViewerPrintMaskOptions | null) => string;
5
- export declare const FILE_VIEWER_PRINT_MASK_STYLE = "\n .viewer-export-print-mask{position:absolute;inset:0;z-index:15;pointer-events:none;print-color-adjust:exact;-webkit-print-color-adjust:exact;}\n .viewer-export-print-mask-block{position:absolute;box-sizing:border-box;print-color-adjust:exact;-webkit-print-color-adjust:exact;}\n";
4
+ export declare const buildFileViewerPrintMaskOverlayHtml: (mask?: FileViewerPrintMaskOptions | null, pageScoped?: boolean) => string;
5
+ export declare const FILE_VIEWER_PRINT_MASK_STYLE = "\n .viewer-export-print-mask{position:absolute;inset:0;z-index:15;pointer-events:none;print-color-adjust:exact;-webkit-print-color-adjust:exact;}\n .viewer-export-print-mask--page{z-index:2147483647;}\n .viewer-export-print-mask-block{position:absolute;box-sizing:border-box;print-color-adjust:exact;-webkit-print-color-adjust:exact;}\n [data-viewer-print-page-index]{position:relative!important;isolation:isolate;}\n";
6
+ export declare const applyFileViewerPagePrintMasksToHtml: (contentHtml: string, mask?: FileViewerPrintMaskOptions | null) => string;
@@ -3,6 +3,11 @@ export const normalizeFileViewerPrintMaskRegion = (region) => {
3
3
  if (!region) {
4
4
  return null;
5
5
  }
6
+ const pageIndex = region.pageIndex;
7
+ if (pageIndex !== undefined &&
8
+ (!Number.isInteger(pageIndex) || pageIndex < 0)) {
9
+ return null;
10
+ }
6
11
  const left = clampPercent(Number(region.left) || 0);
7
12
  const top = clampPercent(Number(region.top) || 0);
8
13
  const width = clampPercent(Number(region.width) || 0);
@@ -15,6 +20,7 @@ export const normalizeFileViewerPrintMaskRegion = (region) => {
15
20
  top,
16
21
  width: Math.min(width, 100 - left),
17
22
  height: Math.min(height, 100 - top),
23
+ ...(pageIndex === undefined ? {} : { pageIndex }),
18
24
  };
19
25
  };
20
26
  export const normalizeFileViewerPrintMaskOptions = (mask) => {
@@ -32,7 +38,7 @@ export const normalizeFileViewerPrintMaskOptions = (mask) => {
32
38
  color: mask.color || '#000000',
33
39
  };
34
40
  };
35
- export const buildFileViewerPrintMaskOverlayHtml = (mask) => {
41
+ export const buildFileViewerPrintMaskOverlayHtml = (mask, pageScoped = false) => {
36
42
  var _a;
37
43
  const normalized = normalizeFileViewerPrintMaskOptions(mask);
38
44
  if (!((_a = normalized === null || normalized === void 0 ? void 0 : normalized.regions) === null || _a === void 0 ? void 0 : _a.length)) {
@@ -40,9 +46,43 @@ export const buildFileViewerPrintMaskOverlayHtml = (mask) => {
40
46
  }
41
47
  const color = normalized.color || '#000000';
42
48
  const blocks = normalized.regions.map(region => (`<div class="viewer-export-print-mask-block" style="left:${region.left}%;top:${region.top}%;width:${region.width}%;height:${region.height}%;background:${color};"></div>`)).join('');
43
- return `<div class="viewer-export-print-mask" aria-hidden="true">${blocks}</div>`;
49
+ const className = pageScoped
50
+ ? 'viewer-export-print-mask viewer-export-print-mask--page'
51
+ : 'viewer-export-print-mask';
52
+ return `<div class="${className}" aria-hidden="true">${blocks}</div>`;
44
53
  };
45
54
  export const FILE_VIEWER_PRINT_MASK_STYLE = `
46
55
  .viewer-export-print-mask{position:absolute;inset:0;z-index:15;pointer-events:none;print-color-adjust:exact;-webkit-print-color-adjust:exact;}
56
+ .viewer-export-print-mask--page{z-index:2147483647;}
47
57
  .viewer-export-print-mask-block{position:absolute;box-sizing:border-box;print-color-adjust:exact;-webkit-print-color-adjust:exact;}
58
+ [data-viewer-print-page-index]{position:relative!important;isolation:isolate;}
48
59
  `;
60
+ export const applyFileViewerPagePrintMasksToHtml = (contentHtml, mask) => {
61
+ var _a;
62
+ const normalized = normalizeFileViewerPrintMaskOptions(mask);
63
+ if (!normalized) {
64
+ return contentHtml;
65
+ }
66
+ const regionsByPage = new Map();
67
+ (_a = normalized.regions) === null || _a === void 0 ? void 0 : _a.forEach(region => {
68
+ if (region.pageIndex === undefined) {
69
+ return;
70
+ }
71
+ const pageRegions = regionsByPage.get(region.pageIndex) || [];
72
+ pageRegions.push(region);
73
+ regionsByPage.set(region.pageIndex, pageRegions);
74
+ });
75
+ if (!regionsByPage.size) {
76
+ return contentHtml;
77
+ }
78
+ return contentHtml.replace(/(<[A-Za-z][^>]*\sdata-viewer-print-page-index=(['"])(\d+)\2[^>]*>)/g, (match, openingTag, _quote, rawPageIndex) => {
79
+ const pageRegions = regionsByPage.get(Number(rawPageIndex));
80
+ if (!(pageRegions === null || pageRegions === void 0 ? void 0 : pageRegions.length)) {
81
+ return match;
82
+ }
83
+ return `${openingTag}${buildFileViewerPrintMaskOverlayHtml({
84
+ regions: pageRegions,
85
+ color: normalized.color,
86
+ }, true)}`;
87
+ });
88
+ };
@@ -2,6 +2,7 @@ import type { FileViewerPrintMaskOptions, FileViewerPrintMaskRegion } from '../c
2
2
  import { type FileViewerI18nInput } from '../i18n/messages';
3
3
  export interface OpenFileViewerPrintMaskDesignerOptions {
4
4
  root: HTMLElement;
5
+ pages?: readonly HTMLElement[];
5
6
  i18n?: FileViewerI18nInput;
6
7
  color?: string;
7
8
  initialRegions?: FileViewerPrintMaskRegion[];
@@ -10,7 +11,7 @@ export interface FileViewerPrintMaskDesignerResult {
10
11
  mask: FileViewerPrintMaskOptions;
11
12
  }
12
13
  /**
13
- * Opens an interactive print-mask designer over the rendered content.
14
- * Loaded asynchronously so the main viewer bundle stays lean.
14
+ * Opens a page-aware print-mask designer. Browsing remains the default mode;
15
+ * drawing is armed for the currently visible page only and disarms after one block.
15
16
  */
16
17
  export declare const openFileViewerPrintMaskDesigner: (options: OpenFileViewerPrintMaskDesignerOptions) => Promise<FileViewerPrintMaskDesignerResult | null>;
@@ -1,14 +1,15 @@
1
1
  import { createFileViewerTranslator } from '../i18n/messages.js';
2
2
  import { normalizeFileViewerPrintMaskRegion } from './printMask.js';
3
3
  const DESIGNER_STYLE = `
4
- .fv-print-mask-layer{position:absolute;inset:0;z-index:40;pointer-events:none;}
5
- .fv-print-mask-canvas{position:absolute;inset:0;pointer-events:auto;cursor:crosshair;touch-action:none;}
4
+ .fv-print-mask-layer{position:absolute;inset:0;z-index:2147483000;pointer-events:none;}
5
+ .fv-print-mask-canvas{position:absolute;inset:0;z-index:2147483000;pointer-events:none;cursor:default;}
6
+ .fv-print-mask-canvas.is-armed{pointer-events:auto;cursor:crosshair;touch-action:none;}
6
7
  .fv-print-mask-block{position:absolute;background:#000;box-sizing:border-box;pointer-events:auto;}
7
8
  .fv-print-mask-block-remove{position:absolute;right:-8px;top:-8px;width:18px;height:18px;border:0;border-radius:999px;background:#111;color:#fff;font:700 12px/18px system-ui,sans-serif;cursor:pointer;padding:0;}
8
- .fv-print-mask-toolbar{position:absolute;left:50%;bottom:16px;transform:translateX(-50%);z-index:41;display:inline-flex;align-items:center;gap:6px;padding:6px 8px;border:1px solid rgba(20,35,53,.12);border-radius:999px;background:rgba(255,255,255,.94);box-shadow:0 12px 28px rgba(15,23,42,.16);pointer-events:auto;max-width:calc(100% - 24px);flex-wrap:wrap;justify-content:center;}
9
+ .fv-print-mask-toolbar{position:absolute;left:50%;bottom:16px;transform:translateX(-50%);z-index:2147483001;display:inline-flex;align-items:center;gap:6px;padding:6px 8px;border:1px solid rgba(20,35,53,.12);border-radius:999px;background:rgba(255,255,255,.94);box-shadow:0 12px 28px rgba(15,23,42,.16);pointer-events:auto;max-width:calc(100% - 24px);flex-wrap:wrap;justify-content:center;}
9
10
  .fv-print-mask-toolbar span{font:600 12px/1.2 system-ui,sans-serif;color:#40546a;white-space:nowrap;}
10
11
  .fv-print-mask-toolbar button{min-width:42px;height:30px;padding:0 10px;border:0;border-radius:999px;background:transparent;color:#40546a;font:800 12px/1 system-ui,sans-serif;cursor:pointer;}
11
- .fv-print-mask-toolbar button:hover{background:rgba(33,163,102,.1);color:#16774c;}
12
+ .fv-print-mask-toolbar button:hover,.fv-print-mask-toolbar button.is-active{background:rgba(33,163,102,.1);color:#16774c;}
12
13
  .fv-print-mask-toolbar button.primary{background:#16774c;color:#fff;}
13
14
  .fv-print-mask-toolbar button.primary:hover{background:#0f5f3c;}
14
15
  `;
@@ -21,7 +22,7 @@ const ensureDesignerStyle = (documentRef) => {
21
22
  style.textContent = DESIGNER_STYLE;
22
23
  documentRef.head.appendChild(style);
23
24
  };
24
- const toPercentRegion = (start, end, bounds) => {
25
+ const toPercentRegion = (start, end, bounds, pageIndex) => {
25
26
  if (!bounds.width || !bounds.height) {
26
27
  return null;
27
28
  }
@@ -37,11 +38,18 @@ const toPercentRegion = (start, end, bounds) => {
37
38
  top: (topPx / bounds.height) * 100,
38
39
  width: (widthPx / bounds.width) * 100,
39
40
  height: (heightPx / bounds.height) * 100,
41
+ pageIndex,
40
42
  });
41
43
  };
44
+ const visibleArea = (element, viewport) => {
45
+ const rect = element.getBoundingClientRect();
46
+ const width = Math.max(0, Math.min(rect.right, viewport.right) - Math.max(rect.left, viewport.left));
47
+ const height = Math.max(0, Math.min(rect.bottom, viewport.bottom) - Math.max(rect.top, viewport.top));
48
+ return width * height;
49
+ };
42
50
  /**
43
- * Opens an interactive print-mask designer over the rendered content.
44
- * Loaded asynchronously so the main viewer bundle stays lean.
51
+ * Opens a page-aware print-mask designer. Browsing remains the default mode;
52
+ * drawing is armed for the currently visible page only and disarms after one block.
45
53
  */
46
54
  export const openFileViewerPrintMaskDesigner = (options) => {
47
55
  const root = options.root;
@@ -51,25 +59,59 @@ export const openFileViewerPrintMaskDesigner = (options) => {
51
59
  const regions = [...(options.initialRegions || [])]
52
60
  .map(region => normalizeFileViewerPrintMaskRegion(region))
53
61
  .filter((region) => !!region);
62
+ const providedPages = Array.from(new Set(options.pages || []))
63
+ .filter(page => page === root || root.contains(page));
64
+ const pageScoped = providedPages.length > 0;
65
+ const pageElements = pageScoped ? providedPages : [root];
54
66
  ensureDesignerStyle(documentRef);
55
- const previousPosition = root.style.position;
56
- if (!previousPosition || previousPosition === 'static') {
57
- root.style.position = 'relative';
58
- }
67
+ const ensureOverlayContainingBlock = (element) => {
68
+ var _a, _b, _c;
69
+ const previousPosition = element.style.position;
70
+ const computedPosition = ((_c = (_b = (_a = documentRef.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle) === null || _b === void 0 ? void 0 : _b.call(_a, element)) === null || _c === void 0 ? void 0 : _c.position) ||
71
+ previousPosition ||
72
+ 'static';
73
+ if (computedPosition === 'static') {
74
+ element.style.position = 'relative';
75
+ }
76
+ return previousPosition;
77
+ };
78
+ // The toolbar layer is positioned against the viewer root even when page
79
+ // canvases live on child page elements.
80
+ const previousRootPosition = ensureOverlayContainingBlock(root);
59
81
  return new Promise(resolve => {
60
82
  let settled = false;
61
83
  let drawing = null;
62
84
  const layer = documentRef.createElement('div');
63
85
  layer.className = 'fv-print-mask-layer';
64
86
  layer.setAttribute('data-viewer-print-mask-designer', 'true');
65
- const canvas = documentRef.createElement('div');
66
- canvas.className = 'fv-print-mask-canvas';
87
+ const createSurface = (element, pageIndex) => {
88
+ const previousPosition = ensureOverlayContainingBlock(element);
89
+ const canvas = documentRef.createElement('div');
90
+ canvas.className = 'fv-print-mask-canvas';
91
+ element.appendChild(canvas);
92
+ return {
93
+ element,
94
+ pageIndex,
95
+ canvas,
96
+ previousPosition,
97
+ };
98
+ };
99
+ const surfaces = pageElements.map((element, index) => (createSurface(element, pageScoped ? index : undefined)));
100
+ const legacySurface = pageScoped &&
101
+ !surfaces.some(surface => surface.element === root) &&
102
+ regions.some(region => region.pageIndex === undefined)
103
+ ? createSurface(root)
104
+ : null;
105
+ const allSurfaces = legacySurface ? [...surfaces, legacySurface] : surfaces;
67
106
  const toolbar = documentRef.createElement('div');
68
107
  toolbar.className = 'fv-print-mask-toolbar';
69
108
  toolbar.setAttribute('role', 'toolbar');
70
109
  toolbar.setAttribute('aria-label', t('toolbar.printMaskTitle'));
71
110
  const hint = documentRef.createElement('span');
72
111
  hint.textContent = t('toolbar.printMaskHint');
112
+ const addButton = documentRef.createElement('button');
113
+ addButton.type = 'button';
114
+ addButton.textContent = t('toolbar.printMaskAdd');
73
115
  const clearButton = documentRef.createElement('button');
74
116
  clearButton.type = 'button';
75
117
  clearButton.textContent = t('toolbar.printMaskClear');
@@ -80,26 +122,49 @@ export const openFileViewerPrintMaskDesigner = (options) => {
80
122
  confirmButton.type = 'button';
81
123
  confirmButton.className = 'primary';
82
124
  confirmButton.textContent = t('toolbar.printMaskConfirm');
83
- toolbar.append(hint, clearButton, cancelButton, confirmButton);
84
- layer.append(canvas, toolbar);
125
+ toolbar.append(hint, addButton, clearButton, cancelButton, confirmButton);
126
+ layer.append(toolbar);
85
127
  root.appendChild(layer);
128
+ const disarm = () => {
129
+ surfaces.forEach(surface => surface.canvas.classList.remove('is-armed'));
130
+ addButton.classList.remove('is-active');
131
+ };
86
132
  const finish = (result) => {
87
133
  if (settled) {
88
134
  return;
89
135
  }
90
136
  settled = true;
91
137
  layer.remove();
92
- if (!previousPosition) {
138
+ allSurfaces.forEach(surface => {
139
+ surface.canvas.remove();
140
+ if (!surface.previousPosition) {
141
+ surface.element.style.removeProperty('position');
142
+ }
143
+ else {
144
+ surface.element.style.position = surface.previousPosition;
145
+ }
146
+ });
147
+ if (!previousRootPosition) {
93
148
  root.style.removeProperty('position');
94
149
  }
95
150
  else {
96
- root.style.position = previousPosition;
151
+ root.style.position = previousRootPosition;
97
152
  }
98
153
  resolve(result);
99
154
  };
155
+ const surfaceForRegion = (region) => {
156
+ if (region.pageIndex === undefined) {
157
+ return legacySurface || surfaces[0];
158
+ }
159
+ return surfaces.find(surface => surface.pageIndex === region.pageIndex) || null;
160
+ };
100
161
  const renderRegions = () => {
101
- canvas.replaceChildren();
162
+ allSurfaces.forEach(surface => surface.canvas.replaceChildren());
102
163
  regions.forEach((region, index) => {
164
+ const surface = surfaceForRegion(region);
165
+ if (!surface) {
166
+ return;
167
+ }
103
168
  const block = documentRef.createElement('div');
104
169
  block.className = 'fv-print-mask-block';
105
170
  block.style.left = `${region.left}%`;
@@ -120,66 +185,86 @@ export const openFileViewerPrintMaskDesigner = (options) => {
120
185
  renderRegions();
121
186
  });
122
187
  block.appendChild(remove);
123
- canvas.appendChild(block);
188
+ surface.canvas.appendChild(block);
124
189
  });
125
190
  };
126
- const pointFromEvent = (event) => {
191
+ const pointFromEvent = (event, canvas) => {
127
192
  const bounds = canvas.getBoundingClientRect();
128
193
  return {
129
194
  x: Math.max(0, Math.min(bounds.width, event.clientX - bounds.left)),
130
195
  y: Math.max(0, Math.min(bounds.height, event.clientY - bounds.top)),
131
196
  };
132
197
  };
133
- canvas.addEventListener('pointerdown', event => {
134
- if (event.button !== 0) {
135
- return;
136
- }
137
- event.preventDefault();
138
- const start = pointFromEvent(event);
139
- const preview = documentRef.createElement('div');
140
- preview.className = 'fv-print-mask-block';
141
- preview.style.background = color;
142
- preview.style.opacity = '0.72';
143
- canvas.appendChild(preview);
144
- drawing = { start, preview };
145
- canvas.setPointerCapture(event.pointerId);
198
+ surfaces.forEach(surface => {
199
+ surface.canvas.addEventListener('pointerdown', event => {
200
+ if (event.button !== 0 || !surface.canvas.classList.contains('is-armed')) {
201
+ return;
202
+ }
203
+ event.preventDefault();
204
+ const start = pointFromEvent(event, surface.canvas);
205
+ const preview = documentRef.createElement('div');
206
+ preview.className = 'fv-print-mask-block';
207
+ preview.style.background = color;
208
+ preview.style.opacity = '0.72';
209
+ surface.canvas.appendChild(preview);
210
+ drawing = { surface, start, preview, pointerId: event.pointerId };
211
+ surface.canvas.setPointerCapture(event.pointerId);
212
+ });
213
+ surface.canvas.addEventListener('pointermove', event => {
214
+ if (!drawing || drawing.surface !== surface) {
215
+ return;
216
+ }
217
+ const end = pointFromEvent(event, surface.canvas);
218
+ const left = Math.min(drawing.start.x, end.x);
219
+ const top = Math.min(drawing.start.y, end.y);
220
+ drawing.preview.style.left = `${left}px`;
221
+ drawing.preview.style.top = `${top}px`;
222
+ drawing.preview.style.width = `${Math.abs(end.x - drawing.start.x)}px`;
223
+ drawing.preview.style.height = `${Math.abs(end.y - drawing.start.y)}px`;
224
+ });
225
+ surface.canvas.addEventListener('pointerup', event => {
226
+ if (!drawing || drawing.surface !== surface) {
227
+ return;
228
+ }
229
+ const end = pointFromEvent(event, surface.canvas);
230
+ const region = toPercentRegion(drawing.start, end, surface.canvas.getBoundingClientRect(), surface.pageIndex);
231
+ drawing.preview.remove();
232
+ try {
233
+ surface.canvas.releasePointerCapture(drawing.pointerId);
234
+ }
235
+ catch {
236
+ // Pointer capture may already be released by the browser.
237
+ }
238
+ drawing = null;
239
+ disarm();
240
+ if (region) {
241
+ regions.push(region);
242
+ }
243
+ renderRegions();
244
+ });
245
+ surface.canvas.addEventListener('pointercancel', () => {
246
+ if (!drawing || drawing.surface !== surface) {
247
+ return;
248
+ }
249
+ drawing.preview.remove();
250
+ drawing = null;
251
+ disarm();
252
+ renderRegions();
253
+ });
146
254
  });
147
- canvas.addEventListener('pointermove', event => {
148
- if (!drawing) {
149
- return;
150
- }
151
- const end = pointFromEvent(event);
152
- const left = Math.min(drawing.start.x, end.x);
153
- const top = Math.min(drawing.start.y, end.y);
154
- drawing.preview.style.left = `${left}px`;
155
- drawing.preview.style.top = `${top}px`;
156
- drawing.preview.style.width = `${Math.abs(end.x - drawing.start.x)}px`;
157
- drawing.preview.style.height = `${Math.abs(end.y - drawing.start.y)}px`;
255
+ addButton.addEventListener('click', () => {
256
+ var _a;
257
+ disarm();
258
+ const viewport = root.getBoundingClientRect();
259
+ const activeSurface = ((_a = surfaces
260
+ .map(surface => ({ surface, area: visibleArea(surface.element, viewport) }))
261
+ .sort((left, right) => right.area - left.area)[0]) === null || _a === void 0 ? void 0 : _a.surface) || surfaces[0];
262
+ activeSurface === null || activeSurface === void 0 ? void 0 : activeSurface.canvas.classList.add('is-armed');
263
+ addButton.classList.toggle('is-active', Boolean(activeSurface));
158
264
  });
159
- const endDrawing = (event) => {
160
- if (!drawing) {
161
- return;
162
- }
163
- const end = pointFromEvent(event);
164
- const bounds = canvas.getBoundingClientRect();
165
- const region = toPercentRegion(drawing.start, end, bounds);
166
- drawing.preview.remove();
167
- drawing = null;
168
- try {
169
- canvas.releasePointerCapture(event.pointerId);
170
- }
171
- catch {
172
- // ignore
173
- }
174
- if (region) {
175
- regions.push(region);
176
- renderRegions();
177
- }
178
- };
179
- canvas.addEventListener('pointerup', endDrawing);
180
- canvas.addEventListener('pointercancel', endDrawing);
181
265
  clearButton.addEventListener('click', () => {
182
266
  regions.splice(0, regions.length);
267
+ disarm();
183
268
  renderRegions();
184
269
  });
185
270
  cancelButton.addEventListener('click', () => finish(null));
package/dist/index.d.ts CHANGED
@@ -67,7 +67,7 @@ export { DEFAULT_FILE_VIEWER_STATE_THEME, DEFAULT_FILE_VIEWER_UNSUPPORTED_DESCRI
67
67
  export type { FileViewerErrorMessageFormatter, FileViewerRendererInstallHint, } from './viewer/state';
68
68
  export { buildFileViewerWatermarkBackgroundImage, buildFileViewerWatermarkInlineStyle, buildFileViewerWatermarkStyle, buildFileViewerWatermarkSvg, normalizeFileViewerWatermark, resolveFileViewerWatermarkPresentationState, } from './features/watermark';
69
69
  export type { FileViewerWatermarkPresentationState, FileViewerWatermarkStyle, } from './features/watermark';
70
- export { buildFileViewerPrintMaskOverlayHtml, normalizeFileViewerPrintMaskOptions, normalizeFileViewerPrintMaskRegion, FILE_VIEWER_PRINT_MASK_STYLE, } from './features/printMask';
70
+ export { buildFileViewerPrintMaskOverlayHtml, applyFileViewerPagePrintMasksToHtml, normalizeFileViewerPrintMaskOptions, normalizeFileViewerPrintMaskRegion, FILE_VIEWER_PRINT_MASK_STYLE, } from './features/printMask';
71
71
  export { openFileViewerPrintMaskDesignerAsync, } from './features/printMaskLoader';
72
72
  export type { FileViewerPrintMaskDesignerResult, OpenFileViewerPrintMaskDesignerOptions, } from './features/printMaskDesigner';
73
73
  export { cancelFileViewerPreviewRequest, DEFAULT_FILE_VIEWER_STREAMING_PDF_FILENAME, DEFAULT_FILE_VIEWER_PREVIEW_LOAD_ERROR_LOGGER, DEFAULT_PDF_RANGE_CHUNK_SIZE, FILE_VIEWER_PREVIEW_LOAD_ERROR_PREFIXES, FILE_VIEWER_REMOTE_MISSING_DATA_ERROR_MESSAGE, applyFileViewerEmptyPreviewState, applyFileViewerPreviewFilenameState, applyFileViewerPreviewSourceUrlState, applyFileViewerReadPreviewState, applyFileViewerRenderReadinessState, applyFileViewerPreviewRequestResetState, commitFileViewerEmptyPreviewResetState, commitFileViewerLoadStartState, commitFileViewerPreviewRequestStartState, commitFileViewerRenderCompleteState, commitFileViewerRemoteDownloadState, createFileViewerEmptyPreviewState, createFileViewerLoadStartState, createFileViewerPreviewStateTarget, createFileViewerSourceLoadingActionHandlers, createFileViewerReadPreviewState, createFileViewerPreviewRequestResetState, createFileViewerRenderCompleteState, createFileViewerRequestController, createFileViewerRequestScope, createFileViewerStreamingPdfPlaceholderFile, finalizeFileViewerPreviewLoadState, hasFileViewerPreviewSource, isFileViewerAbortError, isSameOriginUrl, normalizeFileViewerSourceUrl, normalizePdfStreamingMode, resolveFileViewerFileRefSourcePlan, resolveFileViewerLoadStartMessage, resolveFileViewerMissingRemoteDataErrorMessage, resolveFileViewerPreviewLoadErrorMessage, resolveFileViewerPreviewRequestReason, resolveFileViewerRemoteSourcePlan, resolveFileViewerPageHref, reportFileViewerMissingRemoteData, reportFileViewerPreviewLoadError, runFileViewerLocalFilePreview, runFileViewerPreviewComponentUnmount, runFileViewerPreviewRequest, runFileViewerPreviewSourceChange, runFileViewerRemoteFilePreview, runFileViewerReadAndRenderFile, runFileViewerStreamingPdfPreview, shouldStreamPdfUrl, } from './source/loading';
package/dist/index.js CHANGED
@@ -173,7 +173,7 @@ export { appendFileViewerStyle, buildFileRenderContextFromLoadContext, applyFile
173
173
  export { DEFAULT_FILE_VIEWER_SOURCE_FILENAME, decodeFilename, getExtension, normalizeFileExtension, normalizeFilename, resolveFileViewerSourceFilename, normalizeSource, readFileViewerBuffer, readFileViewerDataUrl, readFileViewerText, wrapFileViewerFileRef, } from './source/index.js';
174
174
  export { DEFAULT_FILE_VIEWER_STATE_THEME, DEFAULT_FILE_VIEWER_UNSUPPORTED_DESCRIPTION, FILE_VIEWER_PREVIEW_MESSAGES, createFileViewerEmptyState, createFileViewerErrorState, createFileViewerPreviewLoadingState, createFileViewerReadyState, createFileViewerUnsupportedState, formatFileViewerErrorMessage, normalizeFileViewerErrorMessage, resolveFileViewerRendererInstallHint, } from './viewer/state.js';
175
175
  export { buildFileViewerWatermarkBackgroundImage, buildFileViewerWatermarkInlineStyle, buildFileViewerWatermarkStyle, buildFileViewerWatermarkSvg, normalizeFileViewerWatermark, resolveFileViewerWatermarkPresentationState, } from './features/watermark.js';
176
- export { buildFileViewerPrintMaskOverlayHtml, normalizeFileViewerPrintMaskOptions, normalizeFileViewerPrintMaskRegion, FILE_VIEWER_PRINT_MASK_STYLE, } from './features/printMask.js';
176
+ export { buildFileViewerPrintMaskOverlayHtml, applyFileViewerPagePrintMasksToHtml, normalizeFileViewerPrintMaskOptions, normalizeFileViewerPrintMaskRegion, FILE_VIEWER_PRINT_MASK_STYLE, } from './features/printMask.js';
177
177
  export { openFileViewerPrintMaskDesignerAsync, } from './features/printMaskLoader.js';
178
178
  export { cancelFileViewerPreviewRequest, DEFAULT_FILE_VIEWER_STREAMING_PDF_FILENAME, DEFAULT_FILE_VIEWER_PREVIEW_LOAD_ERROR_LOGGER, DEFAULT_PDF_RANGE_CHUNK_SIZE, FILE_VIEWER_PREVIEW_LOAD_ERROR_PREFIXES, FILE_VIEWER_REMOTE_MISSING_DATA_ERROR_MESSAGE, applyFileViewerEmptyPreviewState, applyFileViewerPreviewFilenameState, applyFileViewerPreviewSourceUrlState, applyFileViewerReadPreviewState, applyFileViewerRenderReadinessState, applyFileViewerPreviewRequestResetState, commitFileViewerEmptyPreviewResetState, commitFileViewerLoadStartState, commitFileViewerPreviewRequestStartState, commitFileViewerRenderCompleteState, commitFileViewerRemoteDownloadState, createFileViewerEmptyPreviewState, createFileViewerLoadStartState, createFileViewerPreviewStateTarget, createFileViewerSourceLoadingActionHandlers, createFileViewerReadPreviewState, createFileViewerPreviewRequestResetState, createFileViewerRenderCompleteState, createFileViewerRequestController, createFileViewerRequestScope, createFileViewerStreamingPdfPlaceholderFile, finalizeFileViewerPreviewLoadState, hasFileViewerPreviewSource, isFileViewerAbortError, isSameOriginUrl, normalizeFileViewerSourceUrl, normalizePdfStreamingMode, resolveFileViewerFileRefSourcePlan, resolveFileViewerLoadStartMessage, resolveFileViewerMissingRemoteDataErrorMessage, resolveFileViewerPreviewLoadErrorMessage, resolveFileViewerPreviewRequestReason, resolveFileViewerRemoteSourcePlan, resolveFileViewerPageHref, reportFileViewerMissingRemoteData, reportFileViewerPreviewLoadError, runFileViewerLocalFilePreview, runFileViewerPreviewComponentUnmount, runFileViewerPreviewRequest, runFileViewerPreviewSourceChange, runFileViewerRemoteFilePreview, runFileViewerReadAndRenderFile, runFileViewerStreamingPdfPreview, shouldStreamPdfUrl, } from './source/loading.js';
179
179
  export { createViewer } from './viewer/createViewer.js';
@@ -1,4 +1,4 @@
1
- import { buildFileViewerPrintMaskOverlayHtml, FILE_VIEWER_PRINT_MASK_STYLE, normalizeFileViewerPrintMaskOptions, } from '../features/printMask.js';
1
+ import { applyFileViewerPagePrintMasksToHtml, buildFileViewerPrintMaskOverlayHtml, FILE_VIEWER_PRINT_MASK_STYLE, normalizeFileViewerPrintMaskOptions, } from '../features/printMask.js';
2
2
  const escapeHtmlAttribute = (value) => value
3
3
  .replace(/&/g, '&amp;')
4
4
  .replace(/"/g, '&quot;')
@@ -289,13 +289,22 @@ export const collectDocumentStyles = () => {
289
289
  .join('\n');
290
290
  };
291
291
  export const buildExportHtmlDocument = ({ contentHtml, includeDocumentStyles = true, printStyle = '', title, watermarkInlineStyle = '', mask = null, }) => {
292
+ var _a;
292
293
  const watermark = watermarkInlineStyle
293
294
  ? `<div class="viewer-export-watermark" style="${watermarkInlineStyle}"></div>`
294
295
  : '';
295
- const maskHtml = buildFileViewerPrintMaskOverlayHtml(mask);
296
+ const normalizedMask = normalizeFileViewerPrintMaskOptions(mask);
297
+ const globalMask = normalizedMask
298
+ ? {
299
+ ...normalizedMask,
300
+ regions: (_a = normalizedMask.regions) === null || _a === void 0 ? void 0 : _a.filter(region => region.pageIndex === undefined),
301
+ }
302
+ : null;
303
+ const maskHtml = buildFileViewerPrintMaskOverlayHtml(globalMask);
304
+ const maskedContentHtml = applyFileViewerPagePrintMasksToHtml(contentHtml, normalizedMask);
296
305
  const styles = includeDocumentStyles ? collectDocumentStyles() : '';
297
306
  const printOverrideStyle = printStyle ? `<style data-viewer-print-style>${printStyle}</style>` : '';
298
- const maskStyle = maskHtml ? `<style data-viewer-print-mask-style>${FILE_VIEWER_PRINT_MASK_STYLE}</style>` : '';
307
+ const maskStyle = normalizedMask ? `<style data-viewer-print-mask-style>${FILE_VIEWER_PRINT_MASK_STYLE}</style>` : '';
299
308
  const safeTitle = escapeHtmlAttribute(title);
300
309
  return `<!doctype html>
301
310
  <html lang="zh-CN">
@@ -309,7 +318,7 @@ export const buildExportHtmlDocument = ({ contentHtml, includeDocumentStyles = t
309
318
  </head>
310
319
  <body>
311
320
  <main class="viewer-export-shell">
312
- <div class="viewer-export-content">${contentHtml}</div>
321
+ <div class="viewer-export-content">${maskedContentHtml}</div>
313
322
  ${maskHtml}
314
323
  ${watermark}
315
324
  </main>
@@ -79,7 +79,7 @@ export interface ResolveFileViewerRendererAssetsOptions extends ResolveFileViewe
79
79
  export declare const DEFAULT_FILE_VIEWER_RENDERER_ASSET_MANIFESTS: readonly FileViewerRendererAssetManifest[];
80
80
  export declare const resolveFileViewerAssetUrl: (value: string | URL | undefined, fallback: string, options?: ResolveFileViewerAssetUrlOptions) => string;
81
81
  export declare const resolveFileViewerArchiveWorkerUrl: (options?: Pick<FileViewerArchiveOptions, "workerUrl"> | null, baseUrl?: string) => string;
82
- export declare const resolveFileViewerArchiveWasmUrl: (options?: Pick<FileViewerArchiveOptions, "wasmUrl"> | null, fallback?: string) => string;
82
+ export declare const resolveFileViewerArchiveWasmUrl: (options?: Pick<FileViewerArchiveOptions, "wasmUrl"> | null, fallback?: string, documentBaseUrl?: string) => string;
83
83
  export declare const resolveFileViewerCadAssetUrls: (options?: Pick<FileViewerCadOptions, "wasmPath" | "workerUrl" | "dwfWasmUrl"> | null, documentBaseUrl?: string) => ResolvedFileViewerCadAssetUrls;
84
84
  export declare const resolveFileViewerPdfAssetUrls: (options?: Pick<FileViewerPdfOptions, "assetBaseUrl" | "workerUrl" | "cMapUrl" | "wasmUrl" | "standardFontDataUrl" | "cjkFontFallbackPath"> | null, documentBaseUrl?: string) => ResolvedFileViewerPdfAssetUrls;
85
85
  export declare const resolveFileViewerDrawioViewerScriptUrl: (options?: Pick<FileViewerDrawingOptions, "viewerScriptUrl"> | null, documentBaseUrl?: string) => string;
@@ -32,9 +32,18 @@ const resolveFileViewerScriptAssetBaseCandidate = (script, documentBaseUrl, inde
32
32
  return null;
33
33
  }
34
34
  const scriptUrl = new URL(rawScriptUrl, documentBaseUrl);
35
- const assetDirectory = scriptUrl.pathname.match(/^(.*\/)(?:assets|static|js)\/.+\.(?:m?js)$/i);
36
- const entryScript = scriptUrl.pathname.match(/^(.*\/)(?:app|index|main|runtime|umi)(?:[.-][^/]*)?\.(?:m?js)$/i);
37
- const basePath = (assetDirectory === null || assetDirectory === void 0 ? void 0 : assetDirectory[1]) || (entryScript === null || entryScript === void 0 ? void 0 : entryScript[1]);
35
+ const viteClient = scriptUrl.pathname.match(/^(.*\/)@vite\/client$/i);
36
+ const isSourceModule = /\/(?:src|node_modules|@fs|@id|@vite)(?:\/|$)/i.test(scriptUrl.pathname);
37
+ // Start at the first emitted asset directory so nested layouts such as
38
+ // `static/js` and `assets/js/chunks` resolve to the deployment root.
39
+ // Source trees are excluded above and must never become a public base.
40
+ const assetDirectory = isSourceModule
41
+ ? null
42
+ : scriptUrl.pathname.match(/^(.*?\/)(?:assets|static|js)(?:\/[^/]*)*\/[^/]+\.(?:m?js)$/i);
43
+ const entryScript = isSourceModule
44
+ ? null
45
+ : scriptUrl.pathname.match(/^(.*\/)(?:app|index|main|runtime|umi)(?:[.-][^/]*)?\.(?:m?js)$/i);
46
+ const basePath = (viteClient === null || viteClient === void 0 ? void 0 : viteClient[1]) || (!isSourceModule ? assetDirectory === null || assetDirectory === void 0 ? void 0 : assetDirectory[1] : null) || (entryScript === null || entryScript === void 0 ? void 0 : entryScript[1]);
38
47
  if (!basePath) {
39
48
  return null;
40
49
  }
@@ -55,6 +64,9 @@ const resolveFileViewerScriptAssetBaseCandidate = (script, documentBaseUrl, inde
55
64
  if (/^(?:app|index|main|runtime|umi)(?:[.-]|$)/i.test(scriptName)) {
56
65
  score += 2;
57
66
  }
67
+ if (viteClient) {
68
+ score += 8;
69
+ }
58
70
  return { url, score };
59
71
  }
60
72
  catch {
@@ -345,11 +357,13 @@ export const resolveFileViewerAssetUrl = (value, fallback, options = {}) => {
345
357
  export const resolveFileViewerArchiveWorkerUrl = (options, baseUrl) => {
346
358
  return resolveFileViewerAssetUrl(options === null || options === void 0 ? void 0 : options.workerUrl, DEFAULT_FILE_VIEWER_ARCHIVE_WORKER_PATH, { baseUrl });
347
359
  };
348
- export const resolveFileViewerArchiveWasmUrl = (options, fallback = '') => {
360
+ export const resolveFileViewerArchiveWasmUrl = (options, fallback = '', documentBaseUrl) => {
349
361
  if (!(options === null || options === void 0 ? void 0 : options.wasmUrl)) {
350
362
  return fallback;
351
363
  }
352
- return resolveFileViewerAssetUrl(options.wasmUrl, fallback || options.wasmUrl);
364
+ return resolveFileViewerAssetUrl(options.wasmUrl, fallback || options.wasmUrl, {
365
+ documentBaseUrl,
366
+ });
353
367
  };
354
368
  export const resolveFileViewerCadAssetUrls = (options, documentBaseUrl) => {
355
369
  return {
@@ -634,12 +634,13 @@ export const createViewer = (container, createOptions = {}) => {
634
634
  });
635
635
  },
636
636
  async printWithMask(printOptions = {}) {
637
- var _a, _b;
637
+ var _a, _b, _c, _d;
638
638
  const result = await openFileViewerPrintMaskDesignerAsync({
639
639
  root: container,
640
+ pages: (_b = (_a = renderSurfaceState.exportAdapter) === null || _a === void 0 ? void 0 : _a.getPrintMaskPages) === null || _b === void 0 ? void 0 : _b.call(_a),
640
641
  i18n: options,
641
- color: (_a = printOptions.mask) === null || _a === void 0 ? void 0 : _a.color,
642
- initialRegions: (_b = printOptions.mask) === null || _b === void 0 ? void 0 : _b.regions,
642
+ color: (_c = printOptions.mask) === null || _c === void 0 ? void 0 : _c.color,
643
+ initialRegions: (_d = printOptions.mask) === null || _d === void 0 ? void 0 : _d.regions,
643
644
  });
644
645
  if (!(result === null || result === void 0 ? void 0 : result.mask)) {
645
646
  return;
@@ -246,18 +246,20 @@ export const createFileViewerOperationActionHandlers = ({ getBuffer, getFile, ge
246
246
  }
247
247
  },
248
248
  async printWithMask(options = {}) {
249
- var _a, _b, _c, _d, _e;
249
+ var _a, _b, _c, _d, _e, _f, _g;
250
250
  try {
251
251
  const source = getRenderedSource();
252
252
  if (!source) {
253
253
  throw new Error(translateFileViewerMessage(resolveI18n(), 'error.noExportContent'));
254
254
  }
255
255
  const { openFileViewerPrintMaskDesignerAsync } = await import('../features/printMaskLoader.js');
256
+ const adapter = (_a = getAdapter === null || getAdapter === void 0 ? void 0 : getAdapter()) !== null && _a !== void 0 ? _a : null;
256
257
  const result = await openFileViewerPrintMaskDesignerAsync({
257
258
  root: source,
259
+ pages: (_b = adapter === null || adapter === void 0 ? void 0 : adapter.getPrintMaskPages) === null || _b === void 0 ? void 0 : _b.call(adapter),
258
260
  i18n: resolveI18n(),
259
- color: (_a = options.mask) === null || _a === void 0 ? void 0 : _a.color,
260
- initialRegions: (_b = options.mask) === null || _b === void 0 ? void 0 : _b.regions,
261
+ color: (_c = options.mask) === null || _c === void 0 ? void 0 : _c.color,
262
+ initialRegions: (_d = options.mask) === null || _d === void 0 ? void 0 : _d.regions,
261
263
  });
262
264
  if (!(result === null || result === void 0 ? void 0 : result.mask)) {
263
265
  return undefined;
@@ -265,9 +267,9 @@ export const createFileViewerOperationActionHandlers = ({ getBuffer, getFile, ge
265
267
  return await executeFileViewerPrintOperation({
266
268
  ...getRenderedOperationInput(),
267
269
  ...options,
268
- watermarkInlineStyle: (_d = (_c = options.watermarkInlineStyle) !== null && _c !== void 0 ? _c : getWatermarkInlineStyle === null || getWatermarkInlineStyle === void 0 ? void 0 : getWatermarkInlineStyle()) !== null && _d !== void 0 ? _d : undefined,
270
+ watermarkInlineStyle: (_f = (_e = options.watermarkInlineStyle) !== null && _e !== void 0 ? _e : getWatermarkInlineStyle === null || getWatermarkInlineStyle === void 0 ? void 0 : getWatermarkInlineStyle()) !== null && _f !== void 0 ? _f : undefined,
269
271
  mask: result.mask,
270
- printAvailable: (_e = getPrintAvailable === null || getPrintAvailable === void 0 ? void 0 : getPrintAvailable()) !== null && _e !== void 0 ? _e : true,
272
+ printAvailable: (_g = getPrintAvailable === null || getPrintAvailable === void 0 ? void 0 : getPrintAvailable()) !== null && _g !== void 0 ? _g : true,
271
273
  });
272
274
  }
273
275
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/core",
3
- "version": "2.1.28",
3
+ "version": "2.1.30",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Framework-neutral modular TypeScript core for Flyfish File Viewer renderer orchestration, lifecycle APIs, search, zoom, print, and export",