@file-viewer/core 2.3.0 → 3.0.0
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/README.en.md +18 -1
- package/README.md +18 -1
- package/dist/config/options.js +9 -3
- package/dist/contracts/types.d.ts +12 -8
- package/dist/export.d.ts +10 -0
- package/dist/export.js +9 -0
- package/dist/exportDocument.d.ts +36 -0
- package/dist/exportDocument.js +848 -0
- package/dist/features/printMask.js +138 -13
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/output/export.d.ts +3 -24
- package/dist/output/export.js +27 -372
- package/dist/registry/capabilities.d.ts +2 -2
- package/dist/registry/formats.d.ts +4 -4
- package/dist/registry/formats.generated.d.ts +57 -19
- package/dist/registry/formats.generated.js +101 -2
- package/dist/renderers/index.d.ts +55 -17
- package/dist/security/svgResources.d.ts +13 -0
- package/dist/security/svgResources.js +177 -0
- package/dist/source/textEncoding.js +1 -1
- package/dist/viewer/createViewer.js +42 -0
- package/dist/viewer/operations.d.ts +1 -0
- package/dist/viewer/operations.js +68 -5
- package/package.json +9 -1
|
@@ -0,0 +1,848 @@
|
|
|
1
|
+
import createDOMPurify from 'dompurify';
|
|
2
|
+
import { applyFileViewerPagePrintMasksToHtml, buildFileViewerPrintMaskOverlayHtml, FILE_VIEWER_PRINT_MASK_STYLE, normalizeFileViewerPrintMaskOptions, } from './features/printMask.js';
|
|
3
|
+
import { inlineFileViewerBlobUrlsInHtml, prepareFileViewerRenderedContentForSnapshot, replaceFileViewerCanvasWithImages, resolveFileViewerPrintStyle, } from './output/export.js';
|
|
4
|
+
const escapeHtmlAttribute = (value) => value
|
|
5
|
+
.replace(/&/g, '&')
|
|
6
|
+
.replace(/"/g, '"')
|
|
7
|
+
.replace(/</g, '<')
|
|
8
|
+
.replace(/>/g, '>');
|
|
9
|
+
const EXPORT_SANITIZER_OPTIONS = {
|
|
10
|
+
WHOLE_DOCUMENT: true,
|
|
11
|
+
USE_PROFILES: { html: true, svg: true, svgFilters: true, mathMl: true },
|
|
12
|
+
ADD_TAGS: ['use'],
|
|
13
|
+
ADD_ATTR: ['target', 'rel', 'download'],
|
|
14
|
+
FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'base', 'form', 'link'],
|
|
15
|
+
FORBID_ATTR: ['srcdoc'],
|
|
16
|
+
};
|
|
17
|
+
const resolveExportDocument = (documentRef) => {
|
|
18
|
+
return documentRef || globalThis.document || null;
|
|
19
|
+
};
|
|
20
|
+
const isCssIdentifierCharacter = (value) => /[A-Za-z0-9_-]/.test(value);
|
|
21
|
+
const decodeCssEscape = (value, slashIndex) => {
|
|
22
|
+
let cursor = slashIndex + 1;
|
|
23
|
+
const first = value[cursor] || '';
|
|
24
|
+
if (!first) {
|
|
25
|
+
return { value: '', nextIndex: cursor };
|
|
26
|
+
}
|
|
27
|
+
if (first === '\r' || first === '\n' || first === '\f') {
|
|
28
|
+
if (first === '\r' && value[cursor + 1] === '\n')
|
|
29
|
+
cursor += 1;
|
|
30
|
+
return { value: '', nextIndex: cursor + 1 };
|
|
31
|
+
}
|
|
32
|
+
let hexadecimal = '';
|
|
33
|
+
while (cursor < value.length && hexadecimal.length < 6 && /[0-9a-f]/i.test(value[cursor] || '')) {
|
|
34
|
+
hexadecimal += value[cursor];
|
|
35
|
+
cursor += 1;
|
|
36
|
+
}
|
|
37
|
+
if (hexadecimal) {
|
|
38
|
+
if (/\s/.test(value[cursor] || ''))
|
|
39
|
+
cursor += 1;
|
|
40
|
+
const codePoint = Number.parseInt(hexadecimal, 16);
|
|
41
|
+
return {
|
|
42
|
+
value: codePoint === 0 || codePoint > 0x10ffff
|
|
43
|
+
? '\uFFFD'
|
|
44
|
+
: String.fromCodePoint(codePoint),
|
|
45
|
+
nextIndex: cursor,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return { value: first, nextIndex: cursor + 1 };
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Remove CSS comments and decode identifier escapes before looking for URL
|
|
52
|
+
* functions. CSS comments and escapes may otherwise split security-sensitive
|
|
53
|
+
* tokens such as `url` or `@import` while the browser still accepts them.
|
|
54
|
+
*/
|
|
55
|
+
const canonicalizeCssForSecurity = (css) => {
|
|
56
|
+
let output = '';
|
|
57
|
+
let quote = '';
|
|
58
|
+
let cursor = 0;
|
|
59
|
+
while (cursor < css.length) {
|
|
60
|
+
const character = css[cursor] || '';
|
|
61
|
+
if (quote) {
|
|
62
|
+
output += character;
|
|
63
|
+
if (character === '\\') {
|
|
64
|
+
output += css[cursor + 1] || '';
|
|
65
|
+
cursor += 2;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (character === quote)
|
|
69
|
+
quote = '';
|
|
70
|
+
cursor += 1;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (character === '/' && css[cursor + 1] === '*') {
|
|
74
|
+
const commentEnd = css.indexOf('*/', cursor + 2);
|
|
75
|
+
cursor = commentEnd < 0 ? css.length : commentEnd + 2;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (character === '"' || character === "'") {
|
|
79
|
+
quote = character;
|
|
80
|
+
output += character;
|
|
81
|
+
cursor += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (character === '\\') {
|
|
85
|
+
const decoded = decodeCssEscape(css, cursor);
|
|
86
|
+
output += decoded.value;
|
|
87
|
+
cursor = decoded.nextIndex;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const codePoint = character.charCodeAt(0);
|
|
91
|
+
output += codePoint < 0x20 && character !== '\t' && character !== '\n' && character !== '\r'
|
|
92
|
+
? ' '
|
|
93
|
+
: character;
|
|
94
|
+
cursor += 1;
|
|
95
|
+
}
|
|
96
|
+
return output;
|
|
97
|
+
};
|
|
98
|
+
const SAFE_CSS_DATA_URL_RE = /^data:(?:image\/(?:avif|bmp|gif|jpeg|png|webp|x-icon)|font\/(?:collection|otf|sfnt|ttf|woff2?)|application\/(?:font-sfnt|font-woff|vnd\.ms-fontobject|x-font-opentype|x-font-ttf|x-font-woff));/i;
|
|
99
|
+
const isSafeSvgCssDataUrl = (value) => {
|
|
100
|
+
const match = /^data:image\/svg\+xml(?:;charset=[A-Za-z0-9._-]+)?,([\s\S]*)$/i.exec(value);
|
|
101
|
+
if (!match || match[1].length > 1024 * 1024)
|
|
102
|
+
return false;
|
|
103
|
+
let svg;
|
|
104
|
+
try {
|
|
105
|
+
svg = decodeURIComponent(match[1]);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
if (!/^\s*<svg(?:\s|>)/i.test(svg))
|
|
111
|
+
return false;
|
|
112
|
+
if (/<\/?(?:script|style|foreignObject|iframe|object|embed|form|link)\b/i.test(svg))
|
|
113
|
+
return false;
|
|
114
|
+
if (/<!\s*(?:doctype|entity)\b/i.test(svg) || /\son[a-z0-9_-]+\s*=/i.test(svg))
|
|
115
|
+
return false;
|
|
116
|
+
if (/@import\b/i.test(svg) || /url\s*\(\s*(?!["']?#)/i.test(svg))
|
|
117
|
+
return false;
|
|
118
|
+
for (const resource of svg.matchAll(/\s(?:href|xlink:href|src)\s*=\s*(["'])([\s\S]*?)\1/gi)) {
|
|
119
|
+
const target = normalizeResourceUrlForSvgData(resource[2] || '');
|
|
120
|
+
if (!/^#[A-Za-z0-9_.:-]+$/.test(target) && !SAFE_CSS_DATA_URL_RE.test(target))
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
};
|
|
125
|
+
const stripUrlControlCharacters = (value) => {
|
|
126
|
+
let normalized = '';
|
|
127
|
+
for (const character of value) {
|
|
128
|
+
const codePoint = character.charCodeAt(0);
|
|
129
|
+
if (codePoint <= 0x20 || (codePoint >= 0x7f && codePoint <= 0x9f))
|
|
130
|
+
continue;
|
|
131
|
+
normalized += character;
|
|
132
|
+
}
|
|
133
|
+
return normalized.trim();
|
|
134
|
+
};
|
|
135
|
+
const normalizeResourceUrlForSvgData = stripUrlControlCharacters;
|
|
136
|
+
const SAFE_EXPORT_EMBEDDED_DATA_URL_RE = /^data:(?:image\/(?:avif|bmp|gif|jpeg|png|webp|x-icon)|audio\/[a-z0-9.+-]+|video\/[a-z0-9.+-]+|text\/vtt)(?:;[^,]*)?,/i;
|
|
137
|
+
const isSafeExportEmbeddedResourceUrl = (value) => {
|
|
138
|
+
const normalized = stripUrlControlCharacters(value);
|
|
139
|
+
if (!normalized)
|
|
140
|
+
return false;
|
|
141
|
+
if (normalized.startsWith('#'))
|
|
142
|
+
return true;
|
|
143
|
+
if (/^blob:/i.test(normalized))
|
|
144
|
+
return true;
|
|
145
|
+
return SAFE_EXPORT_EMBEDDED_DATA_URL_RE.test(normalized);
|
|
146
|
+
};
|
|
147
|
+
const sanitizeExportEmbeddedResourceAttribute = (element, attributeName, options = {}) => {
|
|
148
|
+
if (!element.hasAttribute(attributeName))
|
|
149
|
+
return;
|
|
150
|
+
const normalized = stripUrlControlCharacters(element.getAttribute(attributeName) || '');
|
|
151
|
+
const allowed = options.fragmentOnly
|
|
152
|
+
? /^#[A-Za-z0-9_.:-]+$/.test(normalized)
|
|
153
|
+
: isSafeExportEmbeddedResourceUrl(normalized);
|
|
154
|
+
if (allowed) {
|
|
155
|
+
element.setAttribute(attributeName, normalized);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
element.removeAttribute(attributeName);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const normalizeCssUrl = (value) => {
|
|
162
|
+
let normalized = value.trim();
|
|
163
|
+
const quote = normalized[0];
|
|
164
|
+
if ((quote === '"' || quote === "'") && normalized[normalized.length - 1] === quote) {
|
|
165
|
+
normalized = normalized.slice(1, -1).trim();
|
|
166
|
+
}
|
|
167
|
+
return stripUrlControlCharacters(normalized);
|
|
168
|
+
};
|
|
169
|
+
const isSafeCssUrl = (value) => {
|
|
170
|
+
const normalized = normalizeCssUrl(value);
|
|
171
|
+
if (!normalized)
|
|
172
|
+
return false;
|
|
173
|
+
if (normalized.startsWith('#'))
|
|
174
|
+
return true;
|
|
175
|
+
if (/^blob:/i.test(normalized))
|
|
176
|
+
return true;
|
|
177
|
+
return SAFE_CSS_DATA_URL_RE.test(normalized) || isSafeSvgCssDataUrl(normalized);
|
|
178
|
+
};
|
|
179
|
+
const readCssUrlFunctionEnd = (css, start) => {
|
|
180
|
+
let quote = '';
|
|
181
|
+
for (let cursor = start; cursor < css.length; cursor += 1) {
|
|
182
|
+
const character = css[cursor] || '';
|
|
183
|
+
if (quote) {
|
|
184
|
+
if (character === '\\') {
|
|
185
|
+
cursor += 1;
|
|
186
|
+
}
|
|
187
|
+
else if (character === quote) {
|
|
188
|
+
quote = '';
|
|
189
|
+
}
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (character === '"' || character === "'") {
|
|
193
|
+
quote = character;
|
|
194
|
+
}
|
|
195
|
+
else if (character === ')') {
|
|
196
|
+
return cursor;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return -1;
|
|
200
|
+
};
|
|
201
|
+
const inspectCssNetworkTokens = (css) => {
|
|
202
|
+
let hasImport = false;
|
|
203
|
+
let hasUrl = false;
|
|
204
|
+
let quote = '';
|
|
205
|
+
let cursor = 0;
|
|
206
|
+
while (cursor < css.length) {
|
|
207
|
+
const character = css[cursor] || '';
|
|
208
|
+
if (quote) {
|
|
209
|
+
if (character === '\\') {
|
|
210
|
+
cursor += 2;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (character === quote)
|
|
214
|
+
quote = '';
|
|
215
|
+
cursor += 1;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (character === '/' && css[cursor + 1] === '*') {
|
|
219
|
+
const commentEnd = css.indexOf('*/', cursor + 2);
|
|
220
|
+
cursor = commentEnd < 0 ? css.length : commentEnd + 2;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (character === '"' || character === "'") {
|
|
224
|
+
quote = character;
|
|
225
|
+
cursor += 1;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (character === '\\') {
|
|
229
|
+
cursor += 2;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (character === '@' && css.slice(cursor + 1, cursor + 7).toLowerCase() === 'import') {
|
|
233
|
+
const boundary = css[cursor + 7] || '';
|
|
234
|
+
if (!boundary || !isCssIdentifierCharacter(boundary))
|
|
235
|
+
hasImport = true;
|
|
236
|
+
}
|
|
237
|
+
if (css.slice(cursor, cursor + 3).toLowerCase() === 'url' &&
|
|
238
|
+
!isCssIdentifierCharacter(css[cursor - 1] || '')) {
|
|
239
|
+
let openParenthesis = cursor + 3;
|
|
240
|
+
while (/\s/.test(css[openParenthesis] || ''))
|
|
241
|
+
openParenthesis += 1;
|
|
242
|
+
if (css[openParenthesis] === '(')
|
|
243
|
+
hasUrl = true;
|
|
244
|
+
}
|
|
245
|
+
cursor += 1;
|
|
246
|
+
}
|
|
247
|
+
return { hasImport, hasUrl };
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Keep ordinary CSS and local embedded resources, but remove every construct
|
|
251
|
+
* that can make an exported document initiate a new network request. Imported
|
|
252
|
+
* stylesheets fail closed; unsafe url() values become `none` so the rest of a
|
|
253
|
+
* declaration can still render.
|
|
254
|
+
*/
|
|
255
|
+
const sanitizeExportCss = (css) => {
|
|
256
|
+
const canonical = canonicalizeCssForSecurity(css);
|
|
257
|
+
const canonicalTokens = inspectCssNetworkTokens(canonical);
|
|
258
|
+
const originalTokens = inspectCssNetworkTokens(css);
|
|
259
|
+
if (canonicalTokens.hasImport)
|
|
260
|
+
return '';
|
|
261
|
+
if (canonicalTokens.hasUrl && !originalTokens.hasUrl)
|
|
262
|
+
return '';
|
|
263
|
+
if (!originalTokens.hasUrl)
|
|
264
|
+
return css;
|
|
265
|
+
let output = '';
|
|
266
|
+
let quote = '';
|
|
267
|
+
let cursor = 0;
|
|
268
|
+
while (cursor < css.length) {
|
|
269
|
+
const character = css[cursor] || '';
|
|
270
|
+
if (quote) {
|
|
271
|
+
output += character;
|
|
272
|
+
if (character === '\\') {
|
|
273
|
+
output += css[cursor + 1] || '';
|
|
274
|
+
cursor += 2;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (character === quote)
|
|
278
|
+
quote = '';
|
|
279
|
+
cursor += 1;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (character === '"' || character === "'") {
|
|
283
|
+
quote = character;
|
|
284
|
+
output += character;
|
|
285
|
+
cursor += 1;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (character === '/' && css[cursor + 1] === '*') {
|
|
289
|
+
const commentEnd = css.indexOf('*/', cursor + 2);
|
|
290
|
+
if (commentEnd < 0) {
|
|
291
|
+
output += css.slice(cursor);
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
output += css.slice(cursor, commentEnd + 2);
|
|
295
|
+
cursor = commentEnd + 2;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (character === '\\') {
|
|
299
|
+
output += character;
|
|
300
|
+
output += css[cursor + 1] || '';
|
|
301
|
+
cursor += 2;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (css.slice(cursor, cursor + 3).toLowerCase() === 'url' &&
|
|
305
|
+
!isCssIdentifierCharacter(css[cursor - 1] || '')) {
|
|
306
|
+
let openParenthesis = cursor + 3;
|
|
307
|
+
while (/\s/.test(css[openParenthesis] || ''))
|
|
308
|
+
openParenthesis += 1;
|
|
309
|
+
if (css[openParenthesis] === '(') {
|
|
310
|
+
const closeParenthesis = readCssUrlFunctionEnd(css, openParenthesis + 1);
|
|
311
|
+
if (closeParenthesis < 0)
|
|
312
|
+
return '';
|
|
313
|
+
const urlValue = css.slice(openParenthesis + 1, closeParenthesis);
|
|
314
|
+
output += isSafeCssUrl(urlValue)
|
|
315
|
+
? css.slice(cursor, closeParenthesis + 1)
|
|
316
|
+
: 'none';
|
|
317
|
+
cursor = closeParenthesis + 1;
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
output += character;
|
|
322
|
+
cursor += 1;
|
|
323
|
+
}
|
|
324
|
+
return output;
|
|
325
|
+
};
|
|
326
|
+
const getExportPurifier = (documentRef) => {
|
|
327
|
+
const windowRef = documentRef.defaultView;
|
|
328
|
+
if (!windowRef) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
const purifier = createDOMPurify(windowRef);
|
|
332
|
+
if (!purifier.isSupported) {
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
purifier.addHook('afterSanitizeElements', node => {
|
|
336
|
+
var _a;
|
|
337
|
+
const element = node;
|
|
338
|
+
if (((_a = element.localName) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== 'style')
|
|
339
|
+
return;
|
|
340
|
+
const sanitizedCss = sanitizeExportCss(element.textContent || '');
|
|
341
|
+
if (sanitizedCss) {
|
|
342
|
+
element.textContent = sanitizedCss;
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
element.remove();
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
purifier.addHook('afterSanitizeAttributes', node => {
|
|
349
|
+
var _a;
|
|
350
|
+
const element = node;
|
|
351
|
+
const localName = (_a = element.localName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
352
|
+
if (localName === 'a' && (element.getAttribute('target') || '').trim().toLowerCase() === '_blank') {
|
|
353
|
+
element.setAttribute('rel', 'noopener noreferrer');
|
|
354
|
+
}
|
|
355
|
+
if (localName === 'a' || localName === 'area') {
|
|
356
|
+
// Keep ordinary links, but never preserve document-controlled beacons.
|
|
357
|
+
element.removeAttribute('ping');
|
|
358
|
+
}
|
|
359
|
+
if (element.hasAttribute('srcset')) {
|
|
360
|
+
// Candidate lists have their own comma/descriptor grammar. The export
|
|
361
|
+
// keeps the materialized src and drops candidates that could select a
|
|
362
|
+
// remote resource after the standalone document is opened.
|
|
363
|
+
element.removeAttribute('srcset');
|
|
364
|
+
}
|
|
365
|
+
if (['img', 'audio', 'video', 'source', 'track', 'input'].includes(localName || '')) {
|
|
366
|
+
sanitizeExportEmbeddedResourceAttribute(element, 'src');
|
|
367
|
+
}
|
|
368
|
+
if (localName === 'video') {
|
|
369
|
+
sanitizeExportEmbeddedResourceAttribute(element, 'poster');
|
|
370
|
+
}
|
|
371
|
+
if (element.hasAttribute('background')) {
|
|
372
|
+
sanitizeExportEmbeddedResourceAttribute(element, 'background');
|
|
373
|
+
}
|
|
374
|
+
if (element.namespaceURI === 'http://www.w3.org/2000/svg' && localName !== 'a') {
|
|
375
|
+
const fragmentOnly = localName === 'use' || localName === 'mpath';
|
|
376
|
+
sanitizeExportEmbeddedResourceAttribute(element, 'href', { fragmentOnly });
|
|
377
|
+
sanitizeExportEmbeddedResourceAttribute(element, 'xlink:href', { fragmentOnly });
|
|
378
|
+
}
|
|
379
|
+
if (element.hasAttribute('style')) {
|
|
380
|
+
const sanitizedStyle = sanitizeExportCss(element.getAttribute('style') || '');
|
|
381
|
+
if (sanitizedStyle) {
|
|
382
|
+
element.setAttribute('style', sanitizedStyle);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
element.removeAttribute('style');
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
return purifier;
|
|
390
|
+
};
|
|
391
|
+
const EXPORT_STATIC_META = [
|
|
392
|
+
'<meta charset="utf-8" />',
|
|
393
|
+
'<meta name="viewport" content="width=device-width,initial-scale=1" />',
|
|
394
|
+
].join('\n ');
|
|
395
|
+
/**
|
|
396
|
+
* Sanitize the complete standalone export document before it is downloaded or
|
|
397
|
+
* mounted into a print window. This is deliberately enforced in core because
|
|
398
|
+
* third-party render adapters and watermark overrides are library inputs.
|
|
399
|
+
*/
|
|
400
|
+
export const sanitizeFileViewerExportDocumentHtml = (html, documentRef) => {
|
|
401
|
+
const currentDocument = resolveExportDocument(documentRef);
|
|
402
|
+
const purifier = currentDocument
|
|
403
|
+
? getExportPurifier(currentDocument)
|
|
404
|
+
: null;
|
|
405
|
+
if (!purifier) {
|
|
406
|
+
// Export is a browser feature. Fail closed rather than return executable
|
|
407
|
+
// markup when called without a usable DOM implementation.
|
|
408
|
+
return '<!doctype html>\n<html lang="en"><head><meta charset="utf-8" /></head><body></body></html>';
|
|
409
|
+
}
|
|
410
|
+
const sanitized = String(purifier.sanitize(html, EXPORT_SANITIZER_OPTIONS));
|
|
411
|
+
const withStaticMeta = sanitized.replace('<head>', `<head>\n ${EXPORT_STATIC_META}`);
|
|
412
|
+
return `<!doctype html>\n${withStaticMeta}`;
|
|
413
|
+
};
|
|
414
|
+
const createEmptyFileViewerExportDocumentRoot = (documentRef) => {
|
|
415
|
+
const root = documentRef.createElement('html');
|
|
416
|
+
root.lang = 'en';
|
|
417
|
+
const head = documentRef.createElement('head');
|
|
418
|
+
const charset = documentRef.createElement('meta');
|
|
419
|
+
charset.setAttribute('charset', 'utf-8');
|
|
420
|
+
head.append(charset);
|
|
421
|
+
root.append(head, documentRef.createElement('body'));
|
|
422
|
+
return root;
|
|
423
|
+
};
|
|
424
|
+
const ensureFileViewerExportDocumentMeta = (root) => {
|
|
425
|
+
const head = root.querySelector(':scope > head');
|
|
426
|
+
if (!head)
|
|
427
|
+
return root;
|
|
428
|
+
if (!head.querySelector('meta[charset]')) {
|
|
429
|
+
const charset = root.ownerDocument.createElement('meta');
|
|
430
|
+
charset.setAttribute('charset', 'utf-8');
|
|
431
|
+
head.prepend(charset);
|
|
432
|
+
}
|
|
433
|
+
if (!head.querySelector('meta[name="viewport"]')) {
|
|
434
|
+
const viewport = root.ownerDocument.createElement('meta');
|
|
435
|
+
viewport.setAttribute('name', 'viewport');
|
|
436
|
+
viewport.setAttribute('content', 'width=device-width,initial-scale=1');
|
|
437
|
+
const charset = head.querySelector('meta[charset]');
|
|
438
|
+
charset === null || charset === void 0 ? void 0 : charset.after(viewport);
|
|
439
|
+
}
|
|
440
|
+
return root;
|
|
441
|
+
};
|
|
442
|
+
/**
|
|
443
|
+
* Produce a sanitized, inert document tree for the print-window mount path.
|
|
444
|
+
* Returning DOM here avoids reinterpreting the sanitized string through a
|
|
445
|
+
* second HTML sink such as document.write or innerHTML.
|
|
446
|
+
*/
|
|
447
|
+
export const sanitizeFileViewerExportDocumentDom = (html, documentRef) => {
|
|
448
|
+
const currentDocument = resolveExportDocument(documentRef);
|
|
449
|
+
if (!currentDocument) {
|
|
450
|
+
throw new Error('A browser document is required to build printable DOM.');
|
|
451
|
+
}
|
|
452
|
+
const purifier = getExportPurifier(currentDocument);
|
|
453
|
+
if (!purifier) {
|
|
454
|
+
return createEmptyFileViewerExportDocumentRoot(currentDocument);
|
|
455
|
+
}
|
|
456
|
+
const sanitized = purifier.sanitize(html, {
|
|
457
|
+
...EXPORT_SANITIZER_OPTIONS,
|
|
458
|
+
RETURN_DOM: true
|
|
459
|
+
});
|
|
460
|
+
if (!sanitized ||
|
|
461
|
+
sanitized.nodeType !== 1 ||
|
|
462
|
+
sanitized.localName.toLowerCase() !== 'html') {
|
|
463
|
+
return createEmptyFileViewerExportDocumentRoot(currentDocument);
|
|
464
|
+
}
|
|
465
|
+
return ensureFileViewerExportDocumentMeta(sanitized);
|
|
466
|
+
};
|
|
467
|
+
const EXPORT_DOCUMENT_STYLE = `
|
|
468
|
+
* { box-sizing: border-box; }
|
|
469
|
+
html, body { margin: 0; min-height: 100%; background: #f2f4f7; color: #172033; font-family: Aptos, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
|
|
470
|
+
body { padding: 24px; }
|
|
471
|
+
.viewer-export-shell { position: relative; min-height: calc(100vh - 48px); overflow: visible; background: #f2f4f7; }
|
|
472
|
+
.viewer-export-content { position: relative; z-index: 1; contain: none; width: 100%; min-height: 100%; overflow: visible; }
|
|
473
|
+
.viewer-export-watermark { position: absolute; inset: 0; pointer-events: none; z-index: 20; background-repeat: repeat; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
|
|
474
|
+
.viewer-export-content .file-render,
|
|
475
|
+
.viewer-export-content .file-viewer,
|
|
476
|
+
.viewer-export-content .viewer-stage,
|
|
477
|
+
.viewer-export-content .content,
|
|
478
|
+
.viewer-export-content .pdf-shell,
|
|
479
|
+
.viewer-export-content .pdf-content,
|
|
480
|
+
.viewer-export-content .pdf-viewport,
|
|
481
|
+
.viewer-export-content .pdf-wrapper,
|
|
482
|
+
.viewer-export-content .docx-fit-viewer,
|
|
483
|
+
.viewer-export-content .docx-wrapper,
|
|
484
|
+
.viewer-export-content .msdoc-stage,
|
|
485
|
+
.viewer-export-content .code-viewer,
|
|
486
|
+
.viewer-export-content .markdown-viewer,
|
|
487
|
+
.viewer-export-content .email-shell,
|
|
488
|
+
.viewer-export-content .archive-shell,
|
|
489
|
+
.viewer-export-content .eda-shell,
|
|
490
|
+
.viewer-export-content .ebook-shell,
|
|
491
|
+
.viewer-export-content .umd-shell,
|
|
492
|
+
.viewer-export-content .drawing-shell,
|
|
493
|
+
.viewer-export-content .audio-shell,
|
|
494
|
+
.viewer-export-content .cad-shell,
|
|
495
|
+
.viewer-export-content .cad-body,
|
|
496
|
+
.viewer-export-content .cad-canvas-wrap,
|
|
497
|
+
.viewer-export-content .dwg-preview-frame {
|
|
498
|
+
position: relative !important;
|
|
499
|
+
inset: auto !important;
|
|
500
|
+
contain: none !important;
|
|
501
|
+
width: 100% !important;
|
|
502
|
+
height: auto !important;
|
|
503
|
+
min-height: 0 !important;
|
|
504
|
+
max-height: none !important;
|
|
505
|
+
overflow: visible !important;
|
|
506
|
+
}
|
|
507
|
+
.viewer-export-content .docx-wrapper {
|
|
508
|
+
display: block !important;
|
|
509
|
+
padding: 0 !important;
|
|
510
|
+
background: transparent !important;
|
|
511
|
+
}
|
|
512
|
+
.viewer-export-content .docx-print-document {
|
|
513
|
+
display: block !important;
|
|
514
|
+
width: fit-content !important;
|
|
515
|
+
max-width: 100% !important;
|
|
516
|
+
height: auto !important;
|
|
517
|
+
overflow: visible !important;
|
|
518
|
+
margin: 0 auto !important;
|
|
519
|
+
}
|
|
520
|
+
.viewer-export-content .docx-page-frame {
|
|
521
|
+
position: relative !important;
|
|
522
|
+
width: var(--viewer-print-page-width, fit-content) !important;
|
|
523
|
+
height: var(--viewer-print-page-height, auto) !important;
|
|
524
|
+
min-height: var(--viewer-print-page-height, 0) !important;
|
|
525
|
+
max-width: 100% !important;
|
|
526
|
+
margin: 0 auto 18px !important;
|
|
527
|
+
overflow: hidden !important;
|
|
528
|
+
break-inside: avoid;
|
|
529
|
+
page-break-inside: avoid;
|
|
530
|
+
break-after: page;
|
|
531
|
+
page-break-after: always;
|
|
532
|
+
}
|
|
533
|
+
.viewer-export-content .msdoc-page {
|
|
534
|
+
position: relative !important;
|
|
535
|
+
width: var(--viewer-print-page-width, 794px) !important;
|
|
536
|
+
min-height: var(--viewer-print-page-height, 1123px) !important;
|
|
537
|
+
max-width: 100% !important;
|
|
538
|
+
height: auto !important;
|
|
539
|
+
margin: 0 auto 18px !important;
|
|
540
|
+
overflow: visible !important;
|
|
541
|
+
break-after: page;
|
|
542
|
+
page-break-after: always;
|
|
543
|
+
}
|
|
544
|
+
.viewer-export-content .docx-page-frame:last-child,
|
|
545
|
+
.viewer-export-content .msdoc-page:last-child {
|
|
546
|
+
break-after: auto;
|
|
547
|
+
page-break-after: auto;
|
|
548
|
+
}
|
|
549
|
+
.viewer-export-content .docx-page-frame > section.docx {
|
|
550
|
+
position: relative !important;
|
|
551
|
+
top: auto !important;
|
|
552
|
+
left: auto !important;
|
|
553
|
+
width: var(--viewer-print-page-width, auto) !important;
|
|
554
|
+
min-height: var(--viewer-print-page-height, auto) !important;
|
|
555
|
+
max-width: none !important;
|
|
556
|
+
margin: 0 auto !important;
|
|
557
|
+
overflow: visible !important;
|
|
558
|
+
transform: none !important;
|
|
559
|
+
box-shadow: none !important;
|
|
560
|
+
break-inside: avoid;
|
|
561
|
+
page-break-inside: avoid;
|
|
562
|
+
}
|
|
563
|
+
.viewer-export-content .msdoc-stage {
|
|
564
|
+
display: block !important;
|
|
565
|
+
padding: 0 !important;
|
|
566
|
+
background: transparent !important;
|
|
567
|
+
}
|
|
568
|
+
.viewer-export-content .msdoc-page > .msdoc-root {
|
|
569
|
+
margin: 0 auto !important;
|
|
570
|
+
box-shadow: none !important;
|
|
571
|
+
overflow: visible !important;
|
|
572
|
+
}
|
|
573
|
+
.viewer-export-content .pdf-toolbar,
|
|
574
|
+
.viewer-export-content .pdf-nav-pane,
|
|
575
|
+
.viewer-export-content .viewer-actions,
|
|
576
|
+
.viewer-export-content .code-toolbar,
|
|
577
|
+
.viewer-export-content .umd-toolbar,
|
|
578
|
+
.viewer-export-content .drawing-toolbar,
|
|
579
|
+
.viewer-export-content .cad-toolbar {
|
|
580
|
+
display: none !important;
|
|
581
|
+
}
|
|
582
|
+
.viewer-export-content .pdf-content,
|
|
583
|
+
.viewer-export-content .pdf-shell--nav-hidden .pdf-content,
|
|
584
|
+
.viewer-export-content .cad-body.without-layers {
|
|
585
|
+
display: block !important;
|
|
586
|
+
grid-template-columns: none !important;
|
|
587
|
+
}
|
|
588
|
+
.viewer-export-content .pdfViewer { padding: 0 !important; }
|
|
589
|
+
.viewer-export-content .pdfViewer .page {
|
|
590
|
+
margin: 0 auto 16px !important;
|
|
591
|
+
border: 0 !important;
|
|
592
|
+
box-shadow: none !important;
|
|
593
|
+
break-after: page;
|
|
594
|
+
page-break-after: always;
|
|
595
|
+
}
|
|
596
|
+
.viewer-export-content .pdfViewer .page:last-child {
|
|
597
|
+
break-after: auto;
|
|
598
|
+
page-break-after: auto;
|
|
599
|
+
}
|
|
600
|
+
.viewer-export-content .pdf-export-document {
|
|
601
|
+
display: grid;
|
|
602
|
+
justify-items: center;
|
|
603
|
+
gap: 18px;
|
|
604
|
+
padding: 4px 0;
|
|
605
|
+
}
|
|
606
|
+
.viewer-export-content .pdf-export-page {
|
|
607
|
+
width: var(--viewer-print-page-width, auto);
|
|
608
|
+
height: var(--viewer-print-page-height, auto);
|
|
609
|
+
max-width: 100%;
|
|
610
|
+
overflow: hidden;
|
|
611
|
+
background: #ffffff;
|
|
612
|
+
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12);
|
|
613
|
+
break-inside: avoid;
|
|
614
|
+
page-break-inside: avoid;
|
|
615
|
+
break-after: page;
|
|
616
|
+
page-break-after: always;
|
|
617
|
+
}
|
|
618
|
+
.viewer-export-content .pdf-export-page:last-child {
|
|
619
|
+
break-after: auto;
|
|
620
|
+
page-break-after: auto;
|
|
621
|
+
}
|
|
622
|
+
.viewer-export-content .pdf-export-page img {
|
|
623
|
+
display: block;
|
|
624
|
+
width: 100%;
|
|
625
|
+
height: 100%;
|
|
626
|
+
object-fit: contain;
|
|
627
|
+
}
|
|
628
|
+
.viewer-export-content .pptx-wrapper {
|
|
629
|
+
width: 100% !important;
|
|
630
|
+
max-width: 100% !important;
|
|
631
|
+
height: auto !important;
|
|
632
|
+
overflow: visible !important;
|
|
633
|
+
transform: none !important;
|
|
634
|
+
}
|
|
635
|
+
.viewer-export-content .pptx-wrapper .slide {
|
|
636
|
+
margin: 0 auto 18px !important;
|
|
637
|
+
break-inside: avoid;
|
|
638
|
+
page-break-inside: avoid;
|
|
639
|
+
break-after: page;
|
|
640
|
+
page-break-after: always;
|
|
641
|
+
box-shadow: none !important;
|
|
642
|
+
}
|
|
643
|
+
.viewer-export-content .pptx-wrapper .slide:last-child {
|
|
644
|
+
break-after: auto;
|
|
645
|
+
page-break-after: auto;
|
|
646
|
+
}
|
|
647
|
+
.viewer-export-content .ofd-stage {
|
|
648
|
+
padding: 0 !important;
|
|
649
|
+
overflow: visible !important;
|
|
650
|
+
}
|
|
651
|
+
.viewer-export-content .ofd-page,
|
|
652
|
+
.viewer-export-content .drawing-svg,
|
|
653
|
+
.viewer-export-content .cad-canvas-wrap,
|
|
654
|
+
.viewer-export-content .dwg-preview-frame {
|
|
655
|
+
break-inside: avoid;
|
|
656
|
+
page-break-inside: avoid;
|
|
657
|
+
break-after: page;
|
|
658
|
+
page-break-after: always;
|
|
659
|
+
box-shadow: none !important;
|
|
660
|
+
}
|
|
661
|
+
.viewer-export-content .ofd-page:last-child,
|
|
662
|
+
.viewer-export-content .drawing-svg:last-child,
|
|
663
|
+
.viewer-export-content .cad-canvas-wrap:last-child,
|
|
664
|
+
.viewer-export-content .dwg-preview-frame:last-child {
|
|
665
|
+
break-after: auto;
|
|
666
|
+
page-break-after: auto;
|
|
667
|
+
}
|
|
668
|
+
.viewer-export-content .code-area {
|
|
669
|
+
overflow: visible !important;
|
|
670
|
+
white-space: pre-wrap !important;
|
|
671
|
+
word-break: break-word !important;
|
|
672
|
+
}
|
|
673
|
+
.viewer-export-content .umd-body,
|
|
674
|
+
.viewer-export-content .umd-stage-wrap,
|
|
675
|
+
.viewer-export-content .umd-stage {
|
|
676
|
+
display: block !important;
|
|
677
|
+
height: auto !important;
|
|
678
|
+
max-height: none !important;
|
|
679
|
+
overflow: visible !important;
|
|
680
|
+
}
|
|
681
|
+
.viewer-export-content .umd-toc {
|
|
682
|
+
display: none !important;
|
|
683
|
+
}
|
|
684
|
+
img, canvas, svg, video { max-width: 100%; }
|
|
685
|
+
@media print {
|
|
686
|
+
@page { margin: 12mm; }
|
|
687
|
+
html, body { min-height: auto; background: #ffffff; }
|
|
688
|
+
body { padding: 0; }
|
|
689
|
+
.viewer-export-shell,
|
|
690
|
+
.viewer-export-content {
|
|
691
|
+
min-height: 0;
|
|
692
|
+
overflow: visible;
|
|
693
|
+
background: #ffffff;
|
|
694
|
+
}
|
|
695
|
+
.viewer-export-content .pdf-export-document {
|
|
696
|
+
display: block;
|
|
697
|
+
padding: 0;
|
|
698
|
+
}
|
|
699
|
+
.viewer-export-content .pdf-export-page {
|
|
700
|
+
width: var(--viewer-print-page-width, auto) !important;
|
|
701
|
+
height: var(--viewer-print-page-height, auto) !important;
|
|
702
|
+
max-width: none !important;
|
|
703
|
+
margin: 0;
|
|
704
|
+
overflow: hidden;
|
|
705
|
+
box-shadow: none;
|
|
706
|
+
}
|
|
707
|
+
.viewer-export-content .docx-page-frame {
|
|
708
|
+
width: var(--viewer-print-page-width, auto) !important;
|
|
709
|
+
height: var(--viewer-print-page-height, auto) !important;
|
|
710
|
+
min-height: var(--viewer-print-page-height, 0) !important;
|
|
711
|
+
max-width: none !important;
|
|
712
|
+
margin: 0 !important;
|
|
713
|
+
overflow: hidden !important;
|
|
714
|
+
}
|
|
715
|
+
.viewer-export-content .msdoc-page {
|
|
716
|
+
width: var(--viewer-print-page-width, 794px) !important;
|
|
717
|
+
min-height: var(--viewer-print-page-height, 1123px) !important;
|
|
718
|
+
max-width: none !important;
|
|
719
|
+
margin: 0 !important;
|
|
720
|
+
overflow: visible !important;
|
|
721
|
+
}
|
|
722
|
+
.viewer-export-content .docx-page-frame > section.docx,
|
|
723
|
+
.viewer-export-content .msdoc-page > .msdoc-root {
|
|
724
|
+
width: var(--viewer-print-page-width, 100%) !important;
|
|
725
|
+
max-width: none !important;
|
|
726
|
+
border: 0 !important;
|
|
727
|
+
}
|
|
728
|
+
.viewer-export-content .pptx-wrapper .slide,
|
|
729
|
+
.viewer-export-content .ofd-page,
|
|
730
|
+
.viewer-export-content .drawing-svg,
|
|
731
|
+
.viewer-export-content .cad-canvas-wrap,
|
|
732
|
+
.viewer-export-content .dwg-preview-frame {
|
|
733
|
+
box-shadow: none !important;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
`;
|
|
737
|
+
export const collectDocumentStyles = (documentRef) => {
|
|
738
|
+
const currentDocument = resolveExportDocument(documentRef);
|
|
739
|
+
if (!currentDocument) {
|
|
740
|
+
return '';
|
|
741
|
+
}
|
|
742
|
+
return Array.from(currentDocument.querySelectorAll('style, link[rel="stylesheet"]'))
|
|
743
|
+
.map(node => {
|
|
744
|
+
var _a;
|
|
745
|
+
if (node.localName.toLowerCase() === 'style') {
|
|
746
|
+
return `<style>${node.textContent || ''}</style>`;
|
|
747
|
+
}
|
|
748
|
+
const link = node;
|
|
749
|
+
try {
|
|
750
|
+
const cssText = Array.from(((_a = link.sheet) === null || _a === void 0 ? void 0 : _a.cssRules) || [])
|
|
751
|
+
.map(rule => rule.cssText)
|
|
752
|
+
.join('\n');
|
|
753
|
+
return cssText
|
|
754
|
+
? `<style data-viewer-inlined-stylesheet>${cssText}</style>`
|
|
755
|
+
: '';
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
// Cross-origin and unreadable stylesheets fail closed. Keeping their
|
|
759
|
+
// link would make a standalone export initiate a second request.
|
|
760
|
+
return '';
|
|
761
|
+
}
|
|
762
|
+
})
|
|
763
|
+
.filter(Boolean)
|
|
764
|
+
.join('\n');
|
|
765
|
+
};
|
|
766
|
+
const buildExportDocumentMarkup = ({ contentHtml, includeDocumentStyles = true, printStyle = '', title, watermarkInlineStyle = '', mask = null, documentRef, }) => {
|
|
767
|
+
var _a, _b;
|
|
768
|
+
const watermark = watermarkInlineStyle
|
|
769
|
+
? `<div class="viewer-export-watermark" style="${escapeHtmlAttribute(watermarkInlineStyle)}"></div>`
|
|
770
|
+
: '';
|
|
771
|
+
const normalizedMask = normalizeFileViewerPrintMaskOptions(mask);
|
|
772
|
+
const globalMask = normalizedMask
|
|
773
|
+
? {
|
|
774
|
+
...normalizedMask,
|
|
775
|
+
regions: (_a = normalizedMask.regions) === null || _a === void 0 ? void 0 : _a.filter(region => region.pageIndex === undefined),
|
|
776
|
+
stamps: (_b = normalizedMask.stamps) === null || _b === void 0 ? void 0 : _b.filter(stamp => stamp.pageIndex === undefined),
|
|
777
|
+
}
|
|
778
|
+
: null;
|
|
779
|
+
const maskHtml = buildFileViewerPrintMaskOverlayHtml(globalMask);
|
|
780
|
+
const maskedContentHtml = applyFileViewerPagePrintMasksToHtml(contentHtml, normalizedMask);
|
|
781
|
+
const styles = includeDocumentStyles ? collectDocumentStyles(documentRef) : '';
|
|
782
|
+
const printOverrideStyle = printStyle ? `<style data-viewer-print-style>${printStyle}</style>` : '';
|
|
783
|
+
const maskStyle = normalizedMask ? `<style data-viewer-print-mask-style>${FILE_VIEWER_PRINT_MASK_STYLE}</style>` : '';
|
|
784
|
+
const safeTitle = escapeHtmlAttribute(title);
|
|
785
|
+
return `<!doctype html>
|
|
786
|
+
<html lang="zh-CN">
|
|
787
|
+
<head>
|
|
788
|
+
<meta charset="utf-8" />
|
|
789
|
+
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
790
|
+
<title>${safeTitle}</title>
|
|
791
|
+
${styles}
|
|
792
|
+
<style>${EXPORT_DOCUMENT_STYLE}</style>
|
|
793
|
+
${maskStyle}
|
|
794
|
+
</head>
|
|
795
|
+
<body>
|
|
796
|
+
<main class="viewer-export-shell">
|
|
797
|
+
<div class="viewer-export-content">${maskedContentHtml}</div>
|
|
798
|
+
${maskHtml}
|
|
799
|
+
${watermark}
|
|
800
|
+
</main>
|
|
801
|
+
${printOverrideStyle}
|
|
802
|
+
</body>
|
|
803
|
+
</html>`;
|
|
804
|
+
};
|
|
805
|
+
export const buildExportHtmlDocument = (options) => {
|
|
806
|
+
return sanitizeFileViewerExportDocumentHtml(buildExportDocumentMarkup(options), options.documentRef);
|
|
807
|
+
};
|
|
808
|
+
export const buildExportDomDocument = (options) => {
|
|
809
|
+
return sanitizeFileViewerExportDocumentDom(buildExportDocumentMarkup(options), options.documentRef);
|
|
810
|
+
};
|
|
811
|
+
const resolveFileViewerRenderedDocumentOptions = async ({ source, mode = 'export', title, adapter = null, watermarkInlineStyle = '', mask = null, }) => {
|
|
812
|
+
const exportOptions = { mode, title };
|
|
813
|
+
const toHtml = adapter === null || adapter === void 0 ? void 0 : adapter.toHtml;
|
|
814
|
+
const normalizedMask = normalizeFileViewerPrintMaskOptions(mask);
|
|
815
|
+
if (toHtml) {
|
|
816
|
+
await prepareFileViewerRenderedContentForSnapshot(source, adapter);
|
|
817
|
+
const contentHtml = await inlineFileViewerBlobUrlsInHtml(await toHtml(exportOptions));
|
|
818
|
+
const printStyle = await resolveFileViewerPrintStyle(adapter, exportOptions);
|
|
819
|
+
return {
|
|
820
|
+
contentHtml,
|
|
821
|
+
includeDocumentStyles: adapter.includeDocumentStyles !== false,
|
|
822
|
+
printStyle,
|
|
823
|
+
title,
|
|
824
|
+
watermarkInlineStyle,
|
|
825
|
+
mask: normalizedMask,
|
|
826
|
+
documentRef: source.ownerDocument,
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
await prepareFileViewerRenderedContentForSnapshot(source, adapter);
|
|
830
|
+
const clone = source.cloneNode(true);
|
|
831
|
+
clone.querySelectorAll('.viewer-watermark').forEach(node => node.remove());
|
|
832
|
+
replaceFileViewerCanvasWithImages(source, clone);
|
|
833
|
+
const printStyle = await resolveFileViewerPrintStyle(adapter, exportOptions);
|
|
834
|
+
return {
|
|
835
|
+
contentHtml: await inlineFileViewerBlobUrlsInHtml(clone.innerHTML),
|
|
836
|
+
printStyle,
|
|
837
|
+
title,
|
|
838
|
+
watermarkInlineStyle,
|
|
839
|
+
mask: normalizedMask,
|
|
840
|
+
documentRef: source.ownerDocument,
|
|
841
|
+
};
|
|
842
|
+
};
|
|
843
|
+
export const buildFileViewerRenderedHtmlDocument = async (options) => {
|
|
844
|
+
return buildExportHtmlDocument(await resolveFileViewerRenderedDocumentOptions(options));
|
|
845
|
+
};
|
|
846
|
+
export const buildFileViewerRenderedDomDocument = async (options) => {
|
|
847
|
+
return buildExportDomDocument(await resolveFileViewerRenderedDocumentOptions(options));
|
|
848
|
+
};
|