@file-viewer/core 2.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/LICENSE +160 -0
- package/README.en.md +78 -0
- package/README.md +83 -0
- package/dist/assets.d.ts +62 -0
- package/dist/assets.js +260 -0
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +1 -0
- package/dist/capabilities.d.ts +24 -0
- package/dist/capabilities.js +95 -0
- package/dist/document.d.ts +9 -0
- package/dist/document.js +86 -0
- package/dist/documentDom/anchors.d.ts +7 -0
- package/dist/documentDom/anchors.js +196 -0
- package/dist/documentDom/index.d.ts +5 -0
- package/dist/documentDom/index.js +3 -0
- package/dist/documentDom/providers.d.ts +13 -0
- package/dist/documentDom/providers.js +52 -0
- package/dist/documentDom/scroll.d.ts +12 -0
- package/dist/documentDom/scroll.js +42 -0
- package/dist/documentDom.d.ts +5 -0
- package/dist/documentDom.js +3 -0
- package/dist/documentEvents.d.ts +73 -0
- package/dist/documentEvents.js +150 -0
- package/dist/documentSearch.d.ts +50 -0
- package/dist/documentSearch.js +459 -0
- package/dist/documentZoom.d.ts +47 -0
- package/dist/documentZoom.js +184 -0
- package/dist/export.d.ts +26 -0
- package/dist/export.js +466 -0
- package/dist/formats.d.ts +305 -0
- package/dist/formats.js +207 -0
- package/dist/headless.d.ts +25 -0
- package/dist/headless.js +14 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +118 -0
- package/dist/lifecycleFacade.d.ts +46 -0
- package/dist/lifecycleFacade.js +68 -0
- package/dist/loading.d.ts +59 -0
- package/dist/loading.js +489 -0
- package/dist/operations.d.ts +287 -0
- package/dist/operations.js +485 -0
- package/dist/options.d.ts +16 -0
- package/dist/options.js +92 -0
- package/dist/presentation.d.ts +14 -0
- package/dist/presentation.js +16 -0
- package/dist/printLayout.d.ts +19 -0
- package/dist/printLayout.js +83 -0
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +63 -0
- package/dist/rendererDispatcher.d.ts +21 -0
- package/dist/rendererDispatcher.js +42 -0
- package/dist/rendererHandler.d.ts +165 -0
- package/dist/rendererHandler.js +354 -0
- package/dist/renderers/archive.d.ts +2 -0
- package/dist/renderers/archive.js +547 -0
- package/dist/renderers/archiveCache.d.ts +10 -0
- package/dist/renderers/archiveCache.js +96 -0
- package/dist/renderers/archiveFallback.d.ts +7 -0
- package/dist/renderers/archiveFallback.js +166 -0
- package/dist/renderers/archiveShared.d.ts +23 -0
- package/dist/renderers/archiveShared.js +71 -0
- package/dist/renderers/audio.d.ts +8 -0
- package/dist/renderers/audio.js +219 -0
- package/dist/renderers/cad.d.ts +2 -0
- package/dist/renderers/cad.js +445 -0
- package/dist/renderers/code.d.ts +11 -0
- package/dist/renderers/code.js +233 -0
- package/dist/renderers/data.d.ts +7 -0
- package/dist/renderers/data.js +370 -0
- package/dist/renderers/drawing.d.ts +10 -0
- package/dist/renderers/drawing.js +537 -0
- package/dist/renderers/eda.d.ts +2 -0
- package/dist/renderers/eda.js +434 -0
- package/dist/renderers/edaParser.d.ts +77 -0
- package/dist/renderers/edaParser.js +569 -0
- package/dist/renderers/email.d.ts +2 -0
- package/dist/renderers/email.js +463 -0
- package/dist/renderers/epub.d.ts +2 -0
- package/dist/renderers/epub.js +330 -0
- package/dist/renderers/geo.d.ts +2 -0
- package/dist/renderers/geo.js +284 -0
- package/dist/renderers/image.d.ts +2 -0
- package/dist/renderers/image.js +179 -0
- package/dist/renderers/index.d.ts +21 -0
- package/dist/renderers/index.js +207 -0
- package/dist/renderers/markdown.d.ts +2 -0
- package/dist/renderers/markdown.js +83 -0
- package/dist/renderers/model.d.ts +2 -0
- package/dist/renderers/model.js +566 -0
- package/dist/renderers/ofd.d.ts +2 -0
- package/dist/renderers/ofd.js +255 -0
- package/dist/renderers/openDocument.d.ts +2 -0
- package/dist/renderers/openDocument.js +122 -0
- package/dist/renderers/pdf.d.ts +3 -0
- package/dist/renderers/pdf.js +846 -0
- package/dist/renderers/pdfStyles.d.ts +1 -0
- package/dist/renderers/pdfStyles.js +1 -0
- package/dist/renderers/pptx.d.ts +2 -0
- package/dist/renderers/pptx.js +202 -0
- package/dist/renderers/spreadsheet/state.d.ts +80 -0
- package/dist/renderers/spreadsheet/state.js +96 -0
- package/dist/renderers/spreadsheet/view.d.ts +25 -0
- package/dist/renderers/spreadsheet/view.js +833 -0
- package/dist/renderers/spreadsheet/worker/index.d.ts +2 -0
- package/dist/renderers/spreadsheet/worker/index.js +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.d.ts +73 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.js +623 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/color.d.ts +2 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/color.js +73 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/index.d.ts +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/index.js +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.d.ts +18 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.js +106 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.d.ts +1 -0
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.js +11 -0
- package/dist/renderers/spreadsheet/worker/type.d.ts +57 -0
- package/dist/renderers/spreadsheet/worker/type.js +1 -0
- package/dist/renderers/spreadsheet.d.ts +3 -0
- package/dist/renderers/spreadsheet.js +929 -0
- package/dist/renderers/typst.d.ts +8 -0
- package/dist/renderers/typst.js +415 -0
- package/dist/renderers/umd/parser.d.ts +30 -0
- package/dist/renderers/umd/parser.js +408 -0
- package/dist/renderers/umd.d.ts +2 -0
- package/dist/renderers/umd.js +297 -0
- package/dist/renderers/video.d.ts +8 -0
- package/dist/renderers/video.js +108 -0
- package/dist/renderers/wordDoc.d.ts +5 -0
- package/dist/renderers/wordDoc.js +284 -0
- package/dist/renderers/wordDocx.d.ts +5 -0
- package/dist/renderers/wordDocx.js +501 -0
- package/dist/renderers/wordDocx.worker.d.ts +1 -0
- package/dist/renderers/wordDocx.worker.js +96 -0
- package/dist/source.d.ts +18 -0
- package/dist/source.js +152 -0
- package/dist/sourceLoading.d.ts +566 -0
- package/dist/sourceLoading.js +918 -0
- package/dist/state.d.ts +16 -0
- package/dist/state.js +81 -0
- package/dist/types.d.ts +446 -0
- package/dist/types.js +1 -0
- package/dist/viewer.d.ts +8 -0
- package/dist/viewer.js +285 -0
- package/dist/viewerOperations.d.ts +88 -0
- package/dist/viewerOperations.js +242 -0
- package/dist/watermark.d.ts +15 -0
- package/dist/watermark.js +81 -0
- package/dist/worker.d.ts +34 -0
- package/dist/worker.js +101 -0
- package/package.json +109 -0
- package/vendor/ofd/dltech/jbig2/arithmetic_decoder.js +183 -0
- package/vendor/ofd/dltech/jbig2/ccitt.js +1070 -0
- package/vendor/ofd/dltech/jbig2/compatibility.js +12 -0
- package/vendor/ofd/dltech/jbig2/core_utils.js +180 -0
- package/vendor/ofd/dltech/jbig2/is_node.js +27 -0
- package/vendor/ofd/dltech/jbig2/jbig2.js +2589 -0
- package/vendor/ofd/dltech/jbig2/jbig2_stream.js +81 -0
- package/vendor/ofd/dltech/jbig2/primitives.js +387 -0
- package/vendor/ofd/dltech/jbig2/stream.js +1348 -0
- package/vendor/ofd/dltech/jbig2/util.js +972 -0
- package/vendor/ofd/dltech/ofd/ofd.d.ts +11 -0
- package/vendor/ofd/dltech/ofd/ofd.js +100 -0
- package/vendor/ofd/dltech/ofd/ofd_parser.js +395 -0
- package/vendor/ofd/dltech/ofd/ofd_render.js +473 -0
- package/vendor/ofd/dltech/ofd/ofd_util.js +350 -0
- package/vendor/ofd/dltech/ofd/pipeline.js +26 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FileRenderContext, FileViewerRenderedInstance } from '../types';
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
__FLYFISH_TYPST_COMPILER_WASM_URL__?: string;
|
|
5
|
+
__FLYFISH_TYPST_RENDERER_WASM_URL__?: string;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export default function renderTypst(buffer: ArrayBuffer, target: HTMLDivElement, _type?: string, context?: FileRenderContext): Promise<FileViewerRenderedInstance>;
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { $typst } from '@myriaddreamin/typst.ts';
|
|
2
|
+
import { resolveFileViewerTypstCompilerWasmUrl, resolveFileViewerTypstRendererWasmUrl, } from '../assets.js';
|
|
3
|
+
import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '../documentDom.js';
|
|
4
|
+
import { createFileViewerZoomChangeEmitter } from '../documentZoom.js';
|
|
5
|
+
import { formatCssPixels } from '../printLayout.js';
|
|
6
|
+
import { readFileViewerText } from '../source.js';
|
|
7
|
+
const typstStyle = `
|
|
8
|
+
.typst-viewer{min-height:100%;overflow:auto;background:#eef1f4;color:#172033}
|
|
9
|
+
.typst-toolbar{position:sticky;top:0;z-index:2;display:flex;min-height:52px;align-items:center;justify-content:space-between;gap:16px;padding:10px 18px;border-bottom:1px solid rgba(120,134,155,.18);background:rgba(248,250,252,.92);backdrop-filter:blur(16px)}
|
|
10
|
+
.typst-toolbar div{min-width:0}
|
|
11
|
+
.typst-toolbar strong,.typst-toolbar span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
12
|
+
.typst-toolbar strong{color:#172033;font-size:14px;font-weight:800}
|
|
13
|
+
.typst-toolbar span,.typst-toolbar em{color:#6a778b;font-size:12px;font-style:normal;font-weight:700}
|
|
14
|
+
.typst-pages{display:flex;min-height:calc(100% - 52px);flex-direction:column;align-items:center;gap:22px;box-sizing:border-box;padding:28px 16px 44px}
|
|
15
|
+
.typst-page-shell{max-width:100%;overflow:hidden;border:1px solid rgba(20,35,53,.1);background:#fff;box-shadow:0 18px 44px rgba(15,23,42,.14)}
|
|
16
|
+
.typst-page-content svg{display:block;width:100%;height:auto}
|
|
17
|
+
.typst-loading,.typst-error{width:min(520px,calc(100% - 32px));box-sizing:border-box;margin:80px auto;padding:26px;border:1px solid rgba(120,134,155,.18);border-radius:14px;background:#fff;box-shadow:0 18px 44px rgba(15,23,42,.12)}
|
|
18
|
+
.typst-loading{display:grid;justify-items:center;gap:10px;text-align:center}
|
|
19
|
+
.typst-loading span{width:34px;height:34px;border:3px solid rgba(46,130,94,.18);border-top-color:#239661;border-radius:999px;animation:typst-spin .8s linear infinite}
|
|
20
|
+
.typst-loading strong,.typst-error strong{color:#172033;font-size:16px}
|
|
21
|
+
.typst-loading p{margin:0;color:#6a778b;font-size:13px}
|
|
22
|
+
.typst-error{color:#9f1d1d}
|
|
23
|
+
.typst-error pre{max-height:360px;margin:14px 0 0;overflow:auto;border-radius:10px;background:#fff1f2;color:#9f1d1d;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;font-size:12px;line-height:1.7;padding:14px;white-space:pre-wrap}
|
|
24
|
+
.file-viewer[data-viewer-theme='dark'] .typst-viewer{background:#101820;color:#e6edf3}
|
|
25
|
+
.file-viewer[data-viewer-theme='dark'] .typst-toolbar{border-bottom-color:rgba(139,148,158,.22);background:rgba(15,23,42,.9)}
|
|
26
|
+
.file-viewer[data-viewer-theme='dark'] .typst-toolbar strong{color:#f8fafc}
|
|
27
|
+
.file-viewer[data-viewer-theme='dark'] .typst-toolbar span,.file-viewer[data-viewer-theme='dark'] .typst-toolbar em{color:#9aa7b8}
|
|
28
|
+
.file-viewer[data-viewer-theme='dark'] .typst-page-shell{border-color:rgba(139,148,158,.26);box-shadow:0 24px 56px rgba(0,0,0,.38)}
|
|
29
|
+
.file-viewer[data-viewer-theme='dark'] .typst-loading,.file-viewer[data-viewer-theme='dark'] .typst-error{border-color:rgba(139,148,158,.22);background:#151b23;box-shadow:0 24px 56px rgba(0,0,0,.32)}
|
|
30
|
+
.file-viewer[data-viewer-theme='dark'] .typst-loading strong,.file-viewer[data-viewer-theme='dark'] .typst-error strong{color:#f8fafc}
|
|
31
|
+
@keyframes typst-spin{to{transform:rotate(360deg)}}
|
|
32
|
+
@media (max-width:767px){.typst-toolbar{align-items:flex-start;flex-direction:column;gap:4px}.typst-pages{gap:16px;padding:16px 10px 28px}}
|
|
33
|
+
@media (prefers-color-scheme:dark){.file-viewer[data-viewer-theme='system'] .typst-viewer{background:#101820;color:#e6edf3}.file-viewer[data-viewer-theme='system'] .typst-toolbar{border-bottom-color:rgba(139,148,158,.22);background:rgba(15,23,42,.9)}.file-viewer[data-viewer-theme='system'] .typst-toolbar strong{color:#f8fafc}.file-viewer[data-viewer-theme='system'] .typst-toolbar span,.file-viewer[data-viewer-theme='system'] .typst-toolbar em{color:#9aa7b8}.file-viewer[data-viewer-theme='system'] .typst-page-shell{border-color:rgba(139,148,158,.26);box-shadow:0 24px 56px rgba(0,0,0,.38)}.file-viewer[data-viewer-theme='system'] .typst-loading,.file-viewer[data-viewer-theme='system'] .typst-error{border-color:rgba(139,148,158,.22);background:#151b23;box-shadow:0 24px 56px rgba(0,0,0,.32)}.file-viewer[data-viewer-theme='system'] .typst-loading strong,.file-viewer[data-viewer-theme='system'] .typst-error strong{color:#f8fafc}}
|
|
34
|
+
`;
|
|
35
|
+
let typstEngineConfigKey = '';
|
|
36
|
+
const createStyle = (documentRef) => {
|
|
37
|
+
const style = documentRef.createElement('style');
|
|
38
|
+
style.textContent = typstStyle;
|
|
39
|
+
return style;
|
|
40
|
+
};
|
|
41
|
+
const createElement = (documentRef, tagName, className, text) => {
|
|
42
|
+
const element = documentRef.createElement(tagName);
|
|
43
|
+
if (className) {
|
|
44
|
+
element.className = className;
|
|
45
|
+
}
|
|
46
|
+
if (text !== undefined) {
|
|
47
|
+
element.textContent = text;
|
|
48
|
+
}
|
|
49
|
+
return element;
|
|
50
|
+
};
|
|
51
|
+
const getWindowOverride = (key) => {
|
|
52
|
+
if (typeof window === 'undefined') {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return window[key];
|
|
56
|
+
};
|
|
57
|
+
const configureTypstEngine = (context) => {
|
|
58
|
+
var _a;
|
|
59
|
+
const typstOptions = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.typst;
|
|
60
|
+
const compilerWasmUrl = resolveFileViewerTypstCompilerWasmUrl(typstOptions, [
|
|
61
|
+
getWindowOverride('__FLYFISH_TYPST_COMPILER_WASM_URL__'),
|
|
62
|
+
]);
|
|
63
|
+
const rendererWasmUrl = resolveFileViewerTypstRendererWasmUrl(typstOptions, [
|
|
64
|
+
getWindowOverride('__FLYFISH_TYPST_RENDERER_WASM_URL__'),
|
|
65
|
+
]);
|
|
66
|
+
const configKey = `${compilerWasmUrl}\n${rendererWasmUrl}`;
|
|
67
|
+
if (typstEngineConfigKey === configKey) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
$typst.setCompilerInitOptions({
|
|
71
|
+
getModule: () => compilerWasmUrl,
|
|
72
|
+
});
|
|
73
|
+
$typst.setRendererInitOptions({
|
|
74
|
+
getModule: () => rendererWasmUrl,
|
|
75
|
+
});
|
|
76
|
+
typstEngineConfigKey = configKey;
|
|
77
|
+
};
|
|
78
|
+
const escapeAttribute = (value) => {
|
|
79
|
+
return value.replace(/[&<>"']/g, char => {
|
|
80
|
+
const entities = {
|
|
81
|
+
'&': '&',
|
|
82
|
+
'<': '<',
|
|
83
|
+
'>': '>',
|
|
84
|
+
'"': '"',
|
|
85
|
+
"'": ''',
|
|
86
|
+
};
|
|
87
|
+
return entities[char] || char;
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const readNumberAttribute = (element, name) => {
|
|
91
|
+
const value = Number.parseFloat(element.getAttribute(name) || '');
|
|
92
|
+
return Number.isFinite(value) && value > 0 ? value : 0;
|
|
93
|
+
};
|
|
94
|
+
const removeUnsafeSvgContent = (root) => {
|
|
95
|
+
root.querySelectorAll('script').forEach(script => script.remove());
|
|
96
|
+
root.querySelectorAll('*').forEach(element => {
|
|
97
|
+
Array.from(element.attributes).forEach(attribute => {
|
|
98
|
+
const name = attribute.name.toLowerCase();
|
|
99
|
+
const value = attribute.value.trim().toLowerCase();
|
|
100
|
+
if (name.startsWith('on') || value.startsWith('javascript:')) {
|
|
101
|
+
element.removeAttribute(attribute.name);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const serializeNode = (node) => {
|
|
107
|
+
return new XMLSerializer().serializeToString(node);
|
|
108
|
+
};
|
|
109
|
+
const parseTypstSvgPages = (svgText) => {
|
|
110
|
+
const parser = new DOMParser();
|
|
111
|
+
const documentSvg = parser.parseFromString(svgText, 'image/svg+xml');
|
|
112
|
+
const parseError = documentSvg.querySelector('parsererror');
|
|
113
|
+
if (parseError) {
|
|
114
|
+
throw new Error(parseError.textContent || 'Typst SVG 解析失败');
|
|
115
|
+
}
|
|
116
|
+
removeUnsafeSvgContent(documentSvg);
|
|
117
|
+
const root = documentSvg.documentElement;
|
|
118
|
+
const sharedNodes = Array.from(root.children)
|
|
119
|
+
.filter(child => ['style', 'defs'].includes(child.tagName.toLowerCase()))
|
|
120
|
+
.map(serializeNode)
|
|
121
|
+
.join('');
|
|
122
|
+
const pageGroups = Array.from(root.querySelectorAll('g.typst-page'));
|
|
123
|
+
const fallbackWidth = readNumberAttribute(root, 'data-width') ||
|
|
124
|
+
readNumberAttribute(root, 'width') ||
|
|
125
|
+
596;
|
|
126
|
+
const fallbackHeight = readNumberAttribute(root, 'data-height') ||
|
|
127
|
+
readNumberAttribute(root, 'height') ||
|
|
128
|
+
842;
|
|
129
|
+
if (!pageGroups.length) {
|
|
130
|
+
return [{
|
|
131
|
+
index: 1,
|
|
132
|
+
width: fallbackWidth,
|
|
133
|
+
height: fallbackHeight,
|
|
134
|
+
svg: svgText,
|
|
135
|
+
}];
|
|
136
|
+
}
|
|
137
|
+
return pageGroups.map((group, index) => {
|
|
138
|
+
const pageWidth = readNumberAttribute(group, 'data-page-width') || fallbackWidth;
|
|
139
|
+
const pageHeight = readNumberAttribute(group, 'data-page-height') || fallbackHeight;
|
|
140
|
+
const pageClone = group.cloneNode(true);
|
|
141
|
+
pageClone.setAttribute('transform', 'translate(0, 0)');
|
|
142
|
+
const pageSvg = [
|
|
143
|
+
`<svg style="overflow:visible;" class="typst-doc" viewBox="0 0 ${pageWidth} ${pageHeight}" width="${pageWidth}" height="${pageHeight}" data-width="${pageWidth}" data-height="${pageHeight}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">`,
|
|
144
|
+
sharedNodes,
|
|
145
|
+
serializeNode(pageClone),
|
|
146
|
+
'</svg>',
|
|
147
|
+
].join('');
|
|
148
|
+
return {
|
|
149
|
+
index: index + 1,
|
|
150
|
+
width: pageWidth,
|
|
151
|
+
height: pageHeight,
|
|
152
|
+
svg: pageSvg,
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
const formatTypstError = (error) => {
|
|
157
|
+
if (Array.isArray(error)) {
|
|
158
|
+
return error.map(item => {
|
|
159
|
+
if (item && typeof item === 'object' && 'message' in item) {
|
|
160
|
+
const severity = 'severity' in item ? String(item.severity) : 'Error';
|
|
161
|
+
return `${severity}: ${String(item.message)}`;
|
|
162
|
+
}
|
|
163
|
+
return String(item);
|
|
164
|
+
}).join('\n');
|
|
165
|
+
}
|
|
166
|
+
if (error instanceof Error) {
|
|
167
|
+
return error.message;
|
|
168
|
+
}
|
|
169
|
+
return String(error);
|
|
170
|
+
};
|
|
171
|
+
const clampZoom = (value) => {
|
|
172
|
+
return Math.min(3, Math.max(0.3, Number(value.toFixed(2))));
|
|
173
|
+
};
|
|
174
|
+
const buildExportStyles = () => `
|
|
175
|
+
<style>
|
|
176
|
+
.typst-export-document {
|
|
177
|
+
box-sizing: border-box;
|
|
178
|
+
display: flex;
|
|
179
|
+
flex-direction: column;
|
|
180
|
+
align-items: center;
|
|
181
|
+
gap: 18px;
|
|
182
|
+
margin: 0;
|
|
183
|
+
padding: 24px;
|
|
184
|
+
background: #eef1f4;
|
|
185
|
+
}
|
|
186
|
+
.typst-export-page {
|
|
187
|
+
box-sizing: border-box;
|
|
188
|
+
flex: 0 0 auto;
|
|
189
|
+
overflow: hidden;
|
|
190
|
+
background: #ffffff;
|
|
191
|
+
box-shadow: 0 18px 42px rgba(15, 23, 42, 0.14);
|
|
192
|
+
}
|
|
193
|
+
.typst-export-page svg {
|
|
194
|
+
display: block;
|
|
195
|
+
width: 100%;
|
|
196
|
+
height: auto;
|
|
197
|
+
}
|
|
198
|
+
</style>
|
|
199
|
+
`;
|
|
200
|
+
const buildExportHtml = (pages, filename) => {
|
|
201
|
+
return `${buildExportStyles()}<main class="typst-export-document" aria-label="${escapeAttribute(filename || 'Typst document')}">${pages.map(page => {
|
|
202
|
+
const width = formatCssPixels(page.width);
|
|
203
|
+
const height = formatCssPixels(page.height);
|
|
204
|
+
return `<section class="typst-export-page viewer-print-page" style="--viewer-print-page-width:${width};--viewer-print-page-height:${height};width:${width};height:${height};" aria-label="Page ${page.index}">${page.svg}</section>`;
|
|
205
|
+
}).join('')}</main>`;
|
|
206
|
+
};
|
|
207
|
+
const buildPrintStyle = (pages) => {
|
|
208
|
+
const firstPage = pages[0];
|
|
209
|
+
const width = firstPage ? formatCssPixels(firstPage.width) : '596px';
|
|
210
|
+
const height = firstPage ? formatCssPixels(firstPage.height) : '842px';
|
|
211
|
+
return `
|
|
212
|
+
@page { size: ${width} ${height}; margin: 0; }
|
|
213
|
+
@media print {
|
|
214
|
+
html,
|
|
215
|
+
body {
|
|
216
|
+
width: ${width};
|
|
217
|
+
min-width: ${width};
|
|
218
|
+
margin: 0 !important;
|
|
219
|
+
background: #ffffff !important;
|
|
220
|
+
}
|
|
221
|
+
.typst-export-document {
|
|
222
|
+
display: block !important;
|
|
223
|
+
margin: 0 !important;
|
|
224
|
+
padding: 0 !important;
|
|
225
|
+
background: #ffffff !important;
|
|
226
|
+
}
|
|
227
|
+
.typst-export-page {
|
|
228
|
+
display: block !important;
|
|
229
|
+
margin: 0 !important;
|
|
230
|
+
border: 0 !important;
|
|
231
|
+
box-shadow: none !important;
|
|
232
|
+
break-after: page;
|
|
233
|
+
page-break-after: always;
|
|
234
|
+
}
|
|
235
|
+
.typst-export-page:last-child {
|
|
236
|
+
break-after: auto;
|
|
237
|
+
page-break-after: auto;
|
|
238
|
+
}
|
|
239
|
+
.typst-export-page svg {
|
|
240
|
+
width: 100% !important;
|
|
241
|
+
height: auto !important;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
};
|
|
246
|
+
const buildExportAdapter = (pages, filename) => {
|
|
247
|
+
if (!pages.length) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
includeDocumentStyles: false,
|
|
252
|
+
print: true,
|
|
253
|
+
exportHtml: true,
|
|
254
|
+
printStyle: () => buildPrintStyle(pages),
|
|
255
|
+
toHtml: () => buildExportHtml(pages, filename),
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
const getPageSummary = (pages) => {
|
|
259
|
+
if (!pages.length) {
|
|
260
|
+
return '0 pages';
|
|
261
|
+
}
|
|
262
|
+
const firstPage = pages[0];
|
|
263
|
+
return `${pages.length} pages / ${Math.round(firstPage.width)} x ${Math.round(firstPage.height)} pt`;
|
|
264
|
+
};
|
|
265
|
+
export default async function renderTypst(buffer, target, _type, context) {
|
|
266
|
+
const source = await readFileViewerText(buffer);
|
|
267
|
+
const documentRef = target.ownerDocument || document;
|
|
268
|
+
const zoomEmitter = createFileViewerZoomChangeEmitter();
|
|
269
|
+
let state = 'loading';
|
|
270
|
+
let pages = [];
|
|
271
|
+
let errorMessage = '';
|
|
272
|
+
let zoom = 1;
|
|
273
|
+
let renderToken = 0;
|
|
274
|
+
let disposed = false;
|
|
275
|
+
const pageShells = new Map();
|
|
276
|
+
const root = createElement(documentRef, 'div', 'typst-viewer');
|
|
277
|
+
root.dataset.viewerZoomProvider = 'typst';
|
|
278
|
+
const toolbar = createElement(documentRef, 'header', 'typst-toolbar');
|
|
279
|
+
const titleGroup = createElement(documentRef, 'div');
|
|
280
|
+
const title = createElement(documentRef, 'strong', undefined, (context === null || context === void 0 ? void 0 : context.filename) || 'Typst document');
|
|
281
|
+
const summary = createElement(documentRef, 'span', undefined, 'Typst WASM renderer');
|
|
282
|
+
const status = createElement(documentRef, 'em', undefined, '正在编译');
|
|
283
|
+
const body = createElement(documentRef, 'div');
|
|
284
|
+
titleGroup.append(title, summary);
|
|
285
|
+
toolbar.append(titleGroup, status);
|
|
286
|
+
root.append(toolbar, body);
|
|
287
|
+
target.replaceChildren(createStyle(documentRef), root);
|
|
288
|
+
const getZoomState = () => ({
|
|
289
|
+
scale: zoom,
|
|
290
|
+
label: `${Math.round(zoom * 100)}%`,
|
|
291
|
+
canZoomIn: zoom < 3,
|
|
292
|
+
canZoomOut: zoom > 0.3,
|
|
293
|
+
canReset: zoom !== 1,
|
|
294
|
+
minScale: 0.3,
|
|
295
|
+
maxScale: 3,
|
|
296
|
+
});
|
|
297
|
+
const applyPageZoom = () => {
|
|
298
|
+
pages.forEach(page => {
|
|
299
|
+
const shell = pageShells.get(page.index);
|
|
300
|
+
if (!shell) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
shell.style.width = `${page.width * zoom}px`;
|
|
304
|
+
shell.style.maxWidth = '100%';
|
|
305
|
+
shell.style.height = 'auto';
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
const setZoom = (scale) => {
|
|
309
|
+
zoom = clampZoom(scale);
|
|
310
|
+
applyPageZoom();
|
|
311
|
+
zoomEmitter.emit();
|
|
312
|
+
return getZoomState();
|
|
313
|
+
};
|
|
314
|
+
registerFileViewerZoomProvider(root, {
|
|
315
|
+
zoomIn: () => setZoom(zoom + 0.1),
|
|
316
|
+
zoomOut: () => setZoom(zoom - 0.1),
|
|
317
|
+
resetZoom: () => setZoom(1),
|
|
318
|
+
setZoom,
|
|
319
|
+
getState: getZoomState,
|
|
320
|
+
subscribe: zoomEmitter.subscribe,
|
|
321
|
+
});
|
|
322
|
+
const registerExportAdapter = () => {
|
|
323
|
+
var _a;
|
|
324
|
+
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, buildExportAdapter(pages, context.filename));
|
|
325
|
+
};
|
|
326
|
+
const renderLoading = () => {
|
|
327
|
+
const loading = createElement(documentRef, 'div', 'typst-loading');
|
|
328
|
+
loading.setAttribute('role', 'status');
|
|
329
|
+
loading.append(createElement(documentRef, 'span'), createElement(documentRef, 'strong', undefined, '正在解析 Typst'), createElement(documentRef, 'p', undefined, '加载编译器并生成页面预览...'));
|
|
330
|
+
body.replaceChildren(loading);
|
|
331
|
+
};
|
|
332
|
+
const renderError = () => {
|
|
333
|
+
const error = createElement(documentRef, 'div', 'typst-error');
|
|
334
|
+
error.append(createElement(documentRef, 'strong', undefined, 'Typst 渲染失败'), createElement(documentRef, 'pre', undefined, errorMessage));
|
|
335
|
+
body.replaceChildren(error);
|
|
336
|
+
};
|
|
337
|
+
const renderPages = () => {
|
|
338
|
+
pageShells.clear();
|
|
339
|
+
const pagesRoot = createElement(documentRef, 'main', 'typst-pages');
|
|
340
|
+
pagesRoot.setAttribute('aria-label', 'Typst preview pages');
|
|
341
|
+
pages.forEach(page => {
|
|
342
|
+
const shell = createElement(documentRef, 'section', 'typst-page-shell');
|
|
343
|
+
shell.setAttribute('aria-label', `Page ${page.index}`);
|
|
344
|
+
const content = createElement(documentRef, 'div', 'typst-page-content');
|
|
345
|
+
content.innerHTML = page.svg;
|
|
346
|
+
shell.append(content);
|
|
347
|
+
pageShells.set(page.index, shell);
|
|
348
|
+
pagesRoot.append(shell);
|
|
349
|
+
});
|
|
350
|
+
body.replaceChildren(pagesRoot);
|
|
351
|
+
applyPageZoom();
|
|
352
|
+
};
|
|
353
|
+
const syncUi = () => {
|
|
354
|
+
summary.textContent = state === 'ready' ? getPageSummary(pages) : 'Typst WASM renderer';
|
|
355
|
+
status.textContent = state === 'loading' ? '正在编译' : state === 'error' ? '编译失败' : '已渲染';
|
|
356
|
+
if (state === 'loading') {
|
|
357
|
+
renderLoading();
|
|
358
|
+
}
|
|
359
|
+
else if (state === 'error') {
|
|
360
|
+
renderError();
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
renderPages();
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
const render = async () => {
|
|
367
|
+
var _a, _b;
|
|
368
|
+
const token = ++renderToken;
|
|
369
|
+
state = 'loading';
|
|
370
|
+
errorMessage = '';
|
|
371
|
+
pages = [];
|
|
372
|
+
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
|
|
373
|
+
syncUi();
|
|
374
|
+
try {
|
|
375
|
+
configureTypstEngine(context);
|
|
376
|
+
const svg = await $typst.svg({
|
|
377
|
+
mainContent: source,
|
|
378
|
+
data_selection: {
|
|
379
|
+
body: true,
|
|
380
|
+
defs: true,
|
|
381
|
+
css: true,
|
|
382
|
+
js: false,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
if (disposed || token !== renderToken) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
pages = parseTypstSvgPages(svg);
|
|
389
|
+
state = 'ready';
|
|
390
|
+
syncUi();
|
|
391
|
+
registerExportAdapter();
|
|
392
|
+
(_b = context === null || context === void 0 ? void 0 : context.onProgressiveRender) === null || _b === void 0 ? void 0 : _b.call(context);
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
if (disposed || token !== renderToken) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
errorMessage = formatTypstError(error);
|
|
399
|
+
state = 'error';
|
|
400
|
+
syncUi();
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
void render();
|
|
404
|
+
return {
|
|
405
|
+
$el: target,
|
|
406
|
+
unmount() {
|
|
407
|
+
var _a;
|
|
408
|
+
disposed = true;
|
|
409
|
+
renderToken += 1;
|
|
410
|
+
unregisterFileViewerZoomProvider(root);
|
|
411
|
+
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
|
|
412
|
+
target.replaceChildren();
|
|
413
|
+
},
|
|
414
|
+
};
|
|
415
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type UmdBookKind = 'text' | 'comic' | 'mixed' | 'unknown';
|
|
2
|
+
export interface UmdImage {
|
|
3
|
+
bytes: Uint8Array;
|
|
4
|
+
extension: string;
|
|
5
|
+
id: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
}
|
|
8
|
+
export interface UmdChapter {
|
|
9
|
+
content: string;
|
|
10
|
+
end: number;
|
|
11
|
+
id: string;
|
|
12
|
+
images: UmdImage[];
|
|
13
|
+
start: number;
|
|
14
|
+
title: string;
|
|
15
|
+
}
|
|
16
|
+
export interface UmdBook {
|
|
17
|
+
author: string;
|
|
18
|
+
category: string;
|
|
19
|
+
chapters: UmdChapter[];
|
|
20
|
+
contentLength: number;
|
|
21
|
+
cover?: UmdImage;
|
|
22
|
+
kind: UmdBookKind;
|
|
23
|
+
publishedAt: string;
|
|
24
|
+
publisher: string;
|
|
25
|
+
rawType: number;
|
|
26
|
+
title: string;
|
|
27
|
+
vendor: string;
|
|
28
|
+
warnings: string[];
|
|
29
|
+
}
|
|
30
|
+
export declare const parseUmdBook: (buffer: ArrayBuffer) => UmdBook;
|