@a3s-lab/office 0.12.0 → 0.13.1
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.md +112 -7
- package/dist/0~1403.js +20 -12
- package/dist/0~2805.js +241 -241
- package/dist/0~4980.js +1 -1
- package/dist/0~5050.js +1 -20
- package/dist/0~5093.js +547 -547
- package/dist/0~6090.js +63 -4
- package/dist/0~7048.js +31 -29
- package/dist/0~document-editor.js +694 -90
- package/dist/0~pdf-viewer.js +41 -10
- package/dist/0~presentation-editor.js +8 -6
- package/dist/0~spreadsheet-editor.js +11892 -9682
- package/dist/0~work-document-import-worker-client.js +237 -0
- package/dist/0~work-docx-export.js +1 -1
- package/dist/0~work-docx-import.js +1 -1
- package/dist/0~work-docx-large-document-import.js +559 -0
- package/dist/0~work-office-diagnostics.js +46 -32
- package/dist/4104.js +5070 -3112
- package/dist/432.js +51 -7
- package/dist/4476.js +201 -7
- package/dist/5184.js +24 -2
- package/dist/{8928.js → 6282.js} +2338 -93
- package/dist/8715.js +895 -888
- package/dist/9356.js +3 -1
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/features/work/editors/document-editor-support.d.ts +1 -9
- package/dist/internal/features/work/editors/document-external-content.d.ts +1 -1
- package/dist/internal/features/work/editors/office-menu-keyboard.d.ts +1 -0
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +3 -1
- package/dist/internal/features/work/editors/presentation-slide-thumbnail.d.ts +3 -2
- package/dist/internal/features/work/editors/presentation-thumbnail-rail.d.ts +2 -1
- package/dist/internal/features/work/editors/presentation-workspace.d.ts +2 -1
- package/dist/internal/features/work/editors/spreadsheet-border-ribbon.d.ts +5 -0
- package/dist/internal/features/work/editors/spreadsheet-calculation-projection.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-border-command.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-border-per-cell.d.ts +5 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-border.d.ts +28 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-fill-command.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-fill.d.ts +12 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-range.d.ts +14 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-style-command.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-style-ribbon.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-style.d.ts +41 -0
- package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +293 -0
- package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +10 -4
- package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +3 -1
- package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +8 -2
- package/dist/internal/features/work/editors/spreadsheet-home-format-ribbon.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-in-place-workbook-sync.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-number-format.d.ts +15 -3
- package/dist/internal/features/work/editors/spreadsheet-operation-projection.d.ts +14 -0
- package/dist/internal/features/work/editors/spreadsheet-structure-selection.d.ts +7 -0
- package/dist/internal/features/work/editors/use-document-pagination.d.ts +5 -0
- package/dist/internal/features/work/editors/use-presentation-design-commands.d.ts +2 -1
- package/dist/internal/features/work/editors/use-spreadsheet-workbook-sync.d.ts +2 -1
- package/dist/internal/features/work/spreadsheet-history-value.d.ts +1 -0
- package/dist/internal/features/work/spreadsheet-sparse.d.ts +6 -1
- package/dist/internal/features/work/work-document-chunk-geometry.d.ts +28 -0
- package/dist/internal/features/work/work-document-chunk-node.d.ts +23 -0
- package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
- package/dist/internal/features/work/work-document-html-fingerprint.d.ts +15 -0
- package/dist/internal/features/work/work-document-import-worker-client.d.ts +13 -0
- package/dist/internal/features/work/work-document-import-worker-protocol.d.ts +36 -0
- package/dist/internal/features/work/work-document-import.worker.d.ts +1 -0
- package/dist/internal/features/work/work-document-incremental-layout.d.ts +1 -1
- package/dist/internal/features/work/work-document-integrity-index.d.ts +14 -0
- package/dist/internal/features/work/work-document-lazy-block.d.ts +3 -0
- package/dist/internal/features/work/work-document-lazy-html.d.ts +21 -0
- package/dist/internal/features/work/work-document-lazy-model.d.ts +29 -0
- package/dist/internal/features/work/work-document-lazy-pagination-measurement.d.ts +3 -0
- package/dist/internal/features/work/work-document-lazy-preview.d.ts +21 -0
- package/dist/internal/features/work/work-document-lazy-update.d.ts +14 -0
- package/dist/internal/features/work/work-document-model.d.ts +18 -2
- package/dist/internal/features/work/work-document-page-surface-registry.d.ts +16 -0
- package/dist/internal/features/work/work-document-pagination-dom.d.ts +1 -0
- package/dist/internal/features/work/work-document-pagination-types.d.ts +1 -0
- package/dist/internal/features/work/work-document-pagination.d.ts +1 -1
- package/dist/internal/features/work/work-document-text-statistics.d.ts +25 -0
- package/dist/internal/features/work/work-document-windowing.d.ts +23 -0
- package/dist/internal/features/work/work-docx-large-document-import.d.ts +35 -0
- package/dist/internal/features/work/work-docx-large-document-parser.d.ts +29 -0
- package/dist/internal/features/work/work-file-import.d.ts +5 -0
- package/dist/internal/features/work/work-file-io.d.ts +3 -3
- package/dist/internal/features/work/work-file-kind.d.ts +5 -0
- package/dist/internal/features/work/work-office-diagnostics.d.ts +10 -1
- package/dist/internal/features/work/work-ooxml-package.d.ts +8 -0
- package/dist/internal/features/work/work-presentation-layouts.d.ts +7 -1
- package/dist/internal/features/work/work-repository.d.ts +1 -0
- package/dist/internal/features/work/work-spreadsheet-compatibility-summary.d.ts +10 -0
- package/dist/internal/features/work/work-spreadsheet-file-export.d.ts +2 -0
- package/dist/internal/features/work/work-spreadsheet-file-import.d.ts +3 -0
- package/dist/internal/features/work/work-spreadsheet-import-diagnostics.d.ts +2 -0
- package/dist/internal/features/work/work-spreadsheet-import-worker-client.d.ts +6 -0
- package/dist/internal/features/work/work-spreadsheet-import-worker-protocol.d.ts +32 -0
- package/dist/internal/features/work/work-spreadsheet-import.worker.d.ts +1 -0
- package/dist/internal/features/work/work-spreadsheet-matrix-profile.d.ts +64 -0
- package/dist/internal/features/work/work-spreadsheet-package-scan-worker-client.d.ts +6 -0
- package/dist/internal/features/work/work-spreadsheet-package-scan-worker-protocol.d.ts +49 -0
- package/dist/internal/features/work/work-spreadsheet-package-scan.worker.d.ts +1 -0
- package/dist/internal/features/work/work-xlsx-cell-borders.d.ts +25 -0
- package/dist/internal/features/work/work-xlsx-cell-style-writer.d.ts +21 -0
- package/dist/internal/features/work/work-xlsx-cell-style-xml.d.ts +17 -0
- package/dist/internal/features/work/work-xlsx-cell-styles.d.ts +13 -0
- package/dist/internal/features/work/work-xlsx-colors.d.ts +6 -0
- package/dist/internal/features/work/work-xlsx-formula-diagnostics.d.ts +3 -1
- package/dist/internal/features/work/work-xlsx-formulas.d.ts +2 -1
- package/dist/internal/features/work/work-xlsx-interop.d.ts +8 -5
- package/dist/internal/features/work/work-xlsx-pivot-diagnostics.d.ts +2 -1
- package/dist/internal/features/work/work-xlsx-plain-fast-path.d.ts +54 -0
- package/dist/internal/features/work/work-xlsx-style-gate.d.ts +1 -0
- package/dist/internal/features/work/work-xlsx-worksheet-scan.d.ts +13 -0
- package/dist/internal/features/work/work-xlsx-worksheet.d.ts +12 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/office-kernel.worker.js +31 -29
- package/dist/react.d.ts +7 -1
- package/dist/styles.css +557 -3
- package/dist/work-document-import.worker.js +558 -0
- package/dist/work-spreadsheet-import.worker.js +24745 -0
- package/dist/work-spreadsheet-import.worker.js.LICENSE.txt +3 -0
- package/dist/work-spreadsheet-package-scan.worker.js +4161 -0
- package/dist/work-spreadsheet-package-scan.worker.js.LICENSE.txt +11 -0
- package/docs/latest/en/browser-editor-architecture.md +351 -15
- package/package.json +23 -3
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
(()=>{
|
|
2
|
+
const DOCUMENT_PAPER_SIZE_PRESETS = [
|
|
3
|
+
'a3',
|
|
4
|
+
'a4',
|
|
5
|
+
'a5',
|
|
6
|
+
'letter',
|
|
7
|
+
'legal',
|
|
8
|
+
'tabloid'
|
|
9
|
+
];
|
|
10
|
+
const PAGE_GEOMETRY_PROPERTY_SET = new Set([
|
|
11
|
+
'width',
|
|
12
|
+
'height',
|
|
13
|
+
'orientation',
|
|
14
|
+
'code'
|
|
15
|
+
]);
|
|
16
|
+
new Set([
|
|
17
|
+
'first',
|
|
18
|
+
'other'
|
|
19
|
+
]);
|
|
20
|
+
const MAX_WORD_PAGE_TWIPS = 31680;
|
|
21
|
+
const MAX_WORD_PAPER_CODE = 118;
|
|
22
|
+
const PRESET_TOLERANCE_TWIPS = 2;
|
|
23
|
+
const PORTRAIT_PAGE_PRESETS = {
|
|
24
|
+
a3: {
|
|
25
|
+
width: 16838,
|
|
26
|
+
height: 23811
|
|
27
|
+
},
|
|
28
|
+
a4: {
|
|
29
|
+
width: 11906,
|
|
30
|
+
height: 16838
|
|
31
|
+
},
|
|
32
|
+
a5: {
|
|
33
|
+
width: 8391,
|
|
34
|
+
height: 11906
|
|
35
|
+
},
|
|
36
|
+
letter: {
|
|
37
|
+
width: 12240,
|
|
38
|
+
height: 15840
|
|
39
|
+
},
|
|
40
|
+
legal: {
|
|
41
|
+
width: 12240,
|
|
42
|
+
height: 20160
|
|
43
|
+
},
|
|
44
|
+
tabloid: {
|
|
45
|
+
width: 15840,
|
|
46
|
+
height: 24480
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
function normalizeDocumentPageGeometry(source) {
|
|
50
|
+
if (!source || 'object' != typeof source || Array.isArray(source)) return null;
|
|
51
|
+
const record = source;
|
|
52
|
+
if (Object.keys(record).some((key)=>!PAGE_GEOMETRY_PROPERTY_SET.has(key))) return null;
|
|
53
|
+
const width = pageTwips(record.width);
|
|
54
|
+
const height = pageTwips(record.height);
|
|
55
|
+
if (null === width || null === height) return null;
|
|
56
|
+
const orientation = record.orientation;
|
|
57
|
+
if (void 0 !== orientation && 'portrait' !== orientation && 'landscape' !== orientation) return null;
|
|
58
|
+
const code = record.code;
|
|
59
|
+
if (void 0 !== code && (!Number.isSafeInteger(code) || Number(code) < 0 || Number(code) > MAX_WORD_PAPER_CODE)) return null;
|
|
60
|
+
return {
|
|
61
|
+
width,
|
|
62
|
+
height,
|
|
63
|
+
...void 0 !== orientation ? {
|
|
64
|
+
orientation
|
|
65
|
+
} : {},
|
|
66
|
+
...void 0 !== code ? {
|
|
67
|
+
code: Number(code)
|
|
68
|
+
} : {}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function documentPaperSizeForGeometry(source) {
|
|
72
|
+
const geometry = normalizeDocumentPageGeometry(source);
|
|
73
|
+
if (!geometry) return 'custom';
|
|
74
|
+
for (const preset of DOCUMENT_PAPER_SIZE_PRESETS){
|
|
75
|
+
const portrait = PORTRAIT_PAGE_PRESETS[preset];
|
|
76
|
+
if (dimensionsMatch(geometry, portrait.width, portrait.height) || dimensionsMatch(geometry, portrait.height, portrait.width)) return preset;
|
|
77
|
+
}
|
|
78
|
+
return 'custom';
|
|
79
|
+
}
|
|
80
|
+
function documentPageOrientationForGeometry(source, fallback = 'portrait') {
|
|
81
|
+
const geometry = normalizeDocumentPageGeometry(source);
|
|
82
|
+
if (!geometry) return fallback;
|
|
83
|
+
return geometry.orientation ?? (geometry.width > geometry.height ? 'landscape' : 'portrait');
|
|
84
|
+
}
|
|
85
|
+
function applyDocumentPageGeometry(layout, source) {
|
|
86
|
+
const pageGeometry = normalizeDocumentPageGeometry(source);
|
|
87
|
+
if (!pageGeometry) return layout;
|
|
88
|
+
return {
|
|
89
|
+
...layout,
|
|
90
|
+
pageSize: documentPaperSizeForGeometry(pageGeometry),
|
|
91
|
+
orientation: documentPageOrientationForGeometry(pageGeometry, layout.orientation),
|
|
92
|
+
pageGeometry
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function pageTwips(value) {
|
|
96
|
+
return Number.isSafeInteger(value) && Number(value) >= 1 && Number(value) <= MAX_WORD_PAGE_TWIPS ? Number(value) : null;
|
|
97
|
+
}
|
|
98
|
+
function dimensionsMatch(geometry, width, height) {
|
|
99
|
+
return Math.abs(geometry.width - width) <= PRESET_TOLERANCE_TWIPS && Math.abs(geometry.height - height) <= PRESET_TOLERANCE_TWIPS;
|
|
100
|
+
}
|
|
101
|
+
const LARGE_SIMPLE_DOCX_MINIMUM_LOGICAL_BLOCKS = 20000;
|
|
102
|
+
const WORD_TWIPS_PER_MILLIMETER = 1440 / 25.4;
|
|
103
|
+
const UNSUPPORTED_SIMPLE_WORD_ELEMENT = /<(?:\/)?(?!w:(?:t(?:bl(?:Grid|Layout|Pr|W)?|c(?:Pr|W)?|r)?|p(?:g(?:Mar|Sz))?|r|sectPr|body|document|gridCol)(?=[\s/>]))[A-Za-z_][\w.-]*:[A-Za-z_][\w.-]*(?=[\s/>])/;
|
|
104
|
+
const SIMPLE_WORD_ENVELOPE_ELEMENT = /<w:(document|body|sectPr)(?=[\s/>])/g;
|
|
105
|
+
const WORD_PARAGRAPH_CLOSE = '</w:p>';
|
|
106
|
+
const WORD_TABLE_CLOSE = '</w:tbl>';
|
|
107
|
+
const WORD_TABLE_CELL_CLOSE = '</w:tc>';
|
|
108
|
+
const WORD_TABLE_ROW_CLOSE = '</w:tr>';
|
|
109
|
+
const WORD_TEXT_CLOSE = '</w:t>';
|
|
110
|
+
function streamLargeSimpleDocxDocumentXml(source, options, sink) {
|
|
111
|
+
const minimumLogicalBlocks = positiveInteger(options.minimumLogicalBlocks, LARGE_SIMPLE_DOCX_MINIMUM_LOGICAL_BLOCKS);
|
|
112
|
+
const eligibilityStartedAt = largeDocxImportNow();
|
|
113
|
+
if (!simpleDocxXmlHasSupportedStructure(source)) {
|
|
114
|
+
recordLargeDocxImportMeasure('a3s-office.document.large-simple-eligibility', eligibilityStartedAt, largeDocxImportNow(), {
|
|
115
|
+
accepted: false
|
|
116
|
+
});
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
recordLargeDocxImportMeasure('a3s-office.document.large-simple-eligibility', eligibilityStartedAt, largeDocxImportNow(), {
|
|
120
|
+
accepted: true
|
|
121
|
+
});
|
|
122
|
+
const envelopeStartedAt = largeDocxImportNow();
|
|
123
|
+
const body = elementContentRange(source, 'body');
|
|
124
|
+
if (null === body) return null;
|
|
125
|
+
const layout = simpleDocumentLayout(source, body.start, body.end);
|
|
126
|
+
recordLargeDocxImportMeasure('a3s-office.document.large-simple-envelope', envelopeStartedAt, largeDocxImportNow());
|
|
127
|
+
let topLevelBlockCount = 0;
|
|
128
|
+
let paragraphCount = 0;
|
|
129
|
+
let standaloneParagraphCount = 0;
|
|
130
|
+
let tableRowCount = 0;
|
|
131
|
+
const contentStartedAt = largeDocxImportNow();
|
|
132
|
+
let blockCursor = body.start;
|
|
133
|
+
let paragraphStart = findWordStartTag(source, 'p', blockCursor, body.end);
|
|
134
|
+
let tableStart = findWordStartTag(source, 'tbl', blockCursor, body.end);
|
|
135
|
+
while(paragraphStart >= 0 || tableStart >= 0){
|
|
136
|
+
const blockStart = firstIndex(paragraphStart, tableStart);
|
|
137
|
+
const tagEnd = source.indexOf('>', blockStart);
|
|
138
|
+
if (tagEnd < 0 || tagEnd >= body.end) return null;
|
|
139
|
+
if (blockStart === paragraphStart) {
|
|
140
|
+
const contentEnd = source.indexOf(WORD_PARAGRAPH_CLOSE, tagEnd + 1);
|
|
141
|
+
if (contentEnd < 0 || contentEnd > body.end) return null;
|
|
142
|
+
sink.paragraph(simpleText(source, tagEnd + 1, contentEnd));
|
|
143
|
+
topLevelBlockCount += 1;
|
|
144
|
+
paragraphCount += 1;
|
|
145
|
+
standaloneParagraphCount += 1;
|
|
146
|
+
blockCursor = contentEnd + WORD_PARAGRAPH_CLOSE.length;
|
|
147
|
+
paragraphStart = findWordStartTag(source, 'p', blockCursor, body.end);
|
|
148
|
+
if (tableStart >= 0 && tableStart < blockCursor) tableStart = findWordStartTag(source, 'tbl', blockCursor, body.end);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const contentEnd = source.indexOf(WORD_TABLE_CLOSE, tagEnd + 1);
|
|
152
|
+
if (contentEnd < 0 || contentEnd > body.end) return null;
|
|
153
|
+
if (findWordStartTag(source, 'tbl', tagEnd + 1, contentEnd) >= 0) return null;
|
|
154
|
+
sink.tableStart();
|
|
155
|
+
const table = streamSimpleTableRows(source, tagEnd + 1, contentEnd, sink.tableRow);
|
|
156
|
+
if (!table) return null;
|
|
157
|
+
sink.tableEnd();
|
|
158
|
+
topLevelBlockCount += 1;
|
|
159
|
+
paragraphCount += table.paragraphCount;
|
|
160
|
+
tableRowCount += table.rowCount;
|
|
161
|
+
blockCursor = contentEnd + WORD_TABLE_CLOSE.length;
|
|
162
|
+
tableStart = findWordStartTag(source, 'tbl', blockCursor, body.end);
|
|
163
|
+
if (paragraphStart >= 0 && paragraphStart < blockCursor) paragraphStart = findWordStartTag(source, 'p', blockCursor, body.end);
|
|
164
|
+
}
|
|
165
|
+
if (!topLevelBlockCount) return null;
|
|
166
|
+
recordLargeDocxImportMeasure('a3s-office.document.large-simple-content', contentStartedAt, largeDocxImportNow(), {
|
|
167
|
+
paragraphs: paragraphCount,
|
|
168
|
+
tableRows: tableRowCount
|
|
169
|
+
});
|
|
170
|
+
const logicalBlockCount = tableRowCount + standaloneParagraphCount;
|
|
171
|
+
if (logicalBlockCount < minimumLogicalBlocks) return null;
|
|
172
|
+
return {
|
|
173
|
+
layout,
|
|
174
|
+
logicalBlockCount,
|
|
175
|
+
paragraphCount,
|
|
176
|
+
tableRowCount
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function simpleDocxXmlHasSupportedStructure(source) {
|
|
180
|
+
if (!source || source.includes('<!DOCTYPE') || source.includes('<![CDATA[')) return false;
|
|
181
|
+
if (UNSUPPORTED_SIMPLE_WORD_ELEMENT.test(source)) return false;
|
|
182
|
+
const counts = simpleDocxEnvelopeElementCounts(source);
|
|
183
|
+
return 1 === counts.document && 1 === counts.body && 1 === counts.sectPr;
|
|
184
|
+
}
|
|
185
|
+
function streamSimpleTableRows(source, start, end, visit) {
|
|
186
|
+
let paragraphCount = 0;
|
|
187
|
+
let rowCount = 0;
|
|
188
|
+
let rowCursor = start;
|
|
189
|
+
while(rowCursor < end){
|
|
190
|
+
const rowStart = findWordStartTag(source, 'tr', rowCursor, end);
|
|
191
|
+
if (rowStart < 0) break;
|
|
192
|
+
const rowTagEnd = source.indexOf('>', rowStart);
|
|
193
|
+
if (rowTagEnd < 0 || rowTagEnd >= end) return null;
|
|
194
|
+
const rowEnd = source.indexOf(WORD_TABLE_ROW_CLOSE, rowTagEnd + 1);
|
|
195
|
+
if (rowEnd < 0 || rowEnd > end) return null;
|
|
196
|
+
const cellParagraphCounts = [];
|
|
197
|
+
const texts = [];
|
|
198
|
+
let cellCursor = rowTagEnd + 1;
|
|
199
|
+
while(cellCursor < rowEnd){
|
|
200
|
+
const cellStart = findWordStartTag(source, 'tc', cellCursor, rowEnd);
|
|
201
|
+
if (cellStart < 0) break;
|
|
202
|
+
const cellTagEnd = source.indexOf('>', cellStart);
|
|
203
|
+
if (cellTagEnd < 0 || cellTagEnd >= rowEnd) return null;
|
|
204
|
+
const cellEnd = source.indexOf(WORD_TABLE_CELL_CLOSE, cellTagEnd + 1);
|
|
205
|
+
if (cellEnd < 0 || cellEnd > rowEnd) return null;
|
|
206
|
+
let cellParagraphCount = 0;
|
|
207
|
+
let paragraphCursor = cellTagEnd + 1;
|
|
208
|
+
while(paragraphCursor < cellEnd){
|
|
209
|
+
const paragraphStart = findWordStartTag(source, 'p', paragraphCursor, cellEnd);
|
|
210
|
+
if (paragraphStart < 0) break;
|
|
211
|
+
const paragraphTagEnd = source.indexOf('>', paragraphStart);
|
|
212
|
+
if (paragraphTagEnd < 0 || paragraphTagEnd >= cellEnd) return null;
|
|
213
|
+
const paragraphEnd = source.indexOf(WORD_PARAGRAPH_CLOSE, paragraphTagEnd + 1);
|
|
214
|
+
if (paragraphEnd < 0 || paragraphEnd > cellEnd) return null;
|
|
215
|
+
texts.push(simpleText(source, paragraphTagEnd + 1, paragraphEnd));
|
|
216
|
+
cellParagraphCount += 1;
|
|
217
|
+
paragraphCount += 1;
|
|
218
|
+
paragraphCursor = paragraphEnd + WORD_PARAGRAPH_CLOSE.length;
|
|
219
|
+
}
|
|
220
|
+
if (!cellParagraphCount) {
|
|
221
|
+
texts.push('');
|
|
222
|
+
cellParagraphCount = 1;
|
|
223
|
+
paragraphCount += 1;
|
|
224
|
+
}
|
|
225
|
+
cellParagraphCounts.push(cellParagraphCount);
|
|
226
|
+
cellCursor = cellEnd + WORD_TABLE_CELL_CLOSE.length;
|
|
227
|
+
}
|
|
228
|
+
if (!cellParagraphCounts.length) return null;
|
|
229
|
+
visit({
|
|
230
|
+
cellParagraphCounts,
|
|
231
|
+
texts
|
|
232
|
+
});
|
|
233
|
+
rowCount += 1;
|
|
234
|
+
rowCursor = rowEnd + WORD_TABLE_ROW_CLOSE.length;
|
|
235
|
+
}
|
|
236
|
+
return rowCount ? {
|
|
237
|
+
paragraphCount,
|
|
238
|
+
rowCount
|
|
239
|
+
} : null;
|
|
240
|
+
}
|
|
241
|
+
function simpleText(source, start, end) {
|
|
242
|
+
let result = '';
|
|
243
|
+
let cursor = start;
|
|
244
|
+
while(cursor < end){
|
|
245
|
+
const textStart = findWordStartTag(source, 't', cursor, end);
|
|
246
|
+
if (textStart < 0) break;
|
|
247
|
+
const tagEnd = source.indexOf('>', textStart);
|
|
248
|
+
if (tagEnd < 0 || tagEnd >= end) return '';
|
|
249
|
+
const textEnd = source.indexOf(WORD_TEXT_CLOSE, tagEnd + 1);
|
|
250
|
+
if (textEnd < 0 || textEnd > end) return '';
|
|
251
|
+
result += decodeXmlText(source.slice(tagEnd + 1, textEnd));
|
|
252
|
+
cursor = textEnd + WORD_TEXT_CLOSE.length;
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
function simpleDocumentLayout(source, bodyStart = 0, bodyEnd = source.length) {
|
|
257
|
+
const base = initialSimpleDocumentLayout();
|
|
258
|
+
const sectionCandidate = source.lastIndexOf('<w:sectPr', bodyEnd);
|
|
259
|
+
const sectionStart = sectionCandidate >= bodyStart && wordTagNameBoundary(source.charCodeAt(sectionCandidate + 9)) ? sectionCandidate : bodyStart;
|
|
260
|
+
const pageSizeTag = startTag(source, 'pgSz', sectionStart, bodyEnd);
|
|
261
|
+
const width = wordIntegerAttribute(pageSizeTag, 'w');
|
|
262
|
+
const height = wordIntegerAttribute(pageSizeTag, 'h');
|
|
263
|
+
const orientation = 'landscape' === wordAttribute(pageSizeTag, 'orient') ? 'landscape' : 'portrait';
|
|
264
|
+
const geometry = width && height ? {
|
|
265
|
+
width,
|
|
266
|
+
height,
|
|
267
|
+
orientation
|
|
268
|
+
} : null;
|
|
269
|
+
const pageMarginsTag = startTag(source, 'pgMar', sectionStart, bodyEnd);
|
|
270
|
+
const margins = {
|
|
271
|
+
top: wordMarginMillimeters(pageMarginsTag, 'top', base.margins.top),
|
|
272
|
+
right: wordMarginMillimeters(pageMarginsTag, 'right', base.margins.right),
|
|
273
|
+
bottom: wordMarginMillimeters(pageMarginsTag, 'bottom', base.margins.bottom),
|
|
274
|
+
left: wordMarginMillimeters(pageMarginsTag, 'left', base.margins.left)
|
|
275
|
+
};
|
|
276
|
+
const layout = {
|
|
277
|
+
...base,
|
|
278
|
+
margins,
|
|
279
|
+
orientation,
|
|
280
|
+
...geometry ? {
|
|
281
|
+
pageSize: documentPaperSizeForGeometry(geometry),
|
|
282
|
+
pageGeometry: geometry
|
|
283
|
+
} : {}
|
|
284
|
+
};
|
|
285
|
+
return geometry ? applyDocumentPageGeometry(layout, geometry) : layout;
|
|
286
|
+
}
|
|
287
|
+
function initialSimpleDocumentLayout() {
|
|
288
|
+
return {
|
|
289
|
+
pageSize: 'a4',
|
|
290
|
+
orientation: 'portrait',
|
|
291
|
+
margins: {
|
|
292
|
+
top: 25,
|
|
293
|
+
right: 23,
|
|
294
|
+
bottom: 25,
|
|
295
|
+
left: 23
|
|
296
|
+
},
|
|
297
|
+
columns: {
|
|
298
|
+
count: 1,
|
|
299
|
+
spacing: 12,
|
|
300
|
+
separator: false
|
|
301
|
+
},
|
|
302
|
+
breakAfter: 'nextPage',
|
|
303
|
+
headerText: void 0,
|
|
304
|
+
footerText: void 0,
|
|
305
|
+
showPageNumbers: false,
|
|
306
|
+
pageNumberStart: void 0,
|
|
307
|
+
pageChrome: {
|
|
308
|
+
differentFirstPage: false,
|
|
309
|
+
differentOddEvenPages: false,
|
|
310
|
+
default: {
|
|
311
|
+
headerHtml: '',
|
|
312
|
+
footerHtml: '',
|
|
313
|
+
showPageNumber: false
|
|
314
|
+
},
|
|
315
|
+
first: {
|
|
316
|
+
headerHtml: '',
|
|
317
|
+
footerHtml: '',
|
|
318
|
+
showPageNumber: false
|
|
319
|
+
},
|
|
320
|
+
even: {
|
|
321
|
+
headerHtml: '',
|
|
322
|
+
footerHtml: '',
|
|
323
|
+
showPageNumber: false
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
function elementContentRange(source, localName) {
|
|
329
|
+
const start = findWordStartTag(source, localName, 0);
|
|
330
|
+
if (start < 0) return null;
|
|
331
|
+
const tagEnd = source.indexOf('>', start);
|
|
332
|
+
if (tagEnd < 0) return null;
|
|
333
|
+
const end = source.indexOf(`</w:${localName}>`, tagEnd + 1);
|
|
334
|
+
return end < 0 ? null : {
|
|
335
|
+
end,
|
|
336
|
+
start: tagEnd + 1
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function startTag(source, localName, searchStart = 0, searchEnd = source.length) {
|
|
340
|
+
const start = findWordStartTag(source, localName, searchStart, searchEnd);
|
|
341
|
+
if (start < 0) return '';
|
|
342
|
+
const end = source.indexOf('>', start);
|
|
343
|
+
return end < 0 || end >= searchEnd ? '' : source.slice(start, end + 1);
|
|
344
|
+
}
|
|
345
|
+
function wordAttribute(source, localName) {
|
|
346
|
+
const escaped = localName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
347
|
+
return new RegExp(`(?:[A-Za-z_][\\w.-]*:)?${escaped}="([^"]*)"`).exec(source)?.[1] ?? null;
|
|
348
|
+
}
|
|
349
|
+
function wordIntegerAttribute(source, localName) {
|
|
350
|
+
const value = Number(wordAttribute(source, localName));
|
|
351
|
+
return Number.isSafeInteger(value) && value > 0 ? value : null;
|
|
352
|
+
}
|
|
353
|
+
function wordMarginMillimeters(source, name, fallback) {
|
|
354
|
+
const value = wordAttribute(source, name);
|
|
355
|
+
if (null === value) return fallback;
|
|
356
|
+
const twips = Number(value);
|
|
357
|
+
return Number.isFinite(twips) && twips >= 0 ? Math.round(twips / WORD_TWIPS_PER_MILLIMETER * 100) / 100 : fallback;
|
|
358
|
+
}
|
|
359
|
+
function simpleDocxEnvelopeElementCounts(source) {
|
|
360
|
+
let body = 0;
|
|
361
|
+
let document = 0;
|
|
362
|
+
let sectPr = 0;
|
|
363
|
+
for (const match of source.matchAll(SIMPLE_WORD_ENVELOPE_ELEMENT))if ('body' === match[1]) body += 1;
|
|
364
|
+
else if ('document' === match[1]) document += 1;
|
|
365
|
+
else sectPr += 1;
|
|
366
|
+
return {
|
|
367
|
+
body,
|
|
368
|
+
document,
|
|
369
|
+
sectPr
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function findWordStartTag(source, localName, start, end = source.length) {
|
|
373
|
+
const prefix = `<w:${localName}`;
|
|
374
|
+
let index = source.indexOf(prefix, start);
|
|
375
|
+
while(index >= 0 && index < end){
|
|
376
|
+
if (wordTagNameBoundary(source.charCodeAt(index + prefix.length))) return index;
|
|
377
|
+
index = source.indexOf(prefix, index + prefix.length);
|
|
378
|
+
}
|
|
379
|
+
return -1;
|
|
380
|
+
}
|
|
381
|
+
function firstIndex(left, right) {
|
|
382
|
+
if (left < 0) return right;
|
|
383
|
+
if (right < 0) return left;
|
|
384
|
+
return Math.min(left, right);
|
|
385
|
+
}
|
|
386
|
+
function wordTagNameBoundary(code) {
|
|
387
|
+
return 32 === code || 9 === code || 10 === code || 13 === code || 47 === code || 62 === code;
|
|
388
|
+
}
|
|
389
|
+
function decodeXmlText(value) {
|
|
390
|
+
if (!value.includes('&')) return value;
|
|
391
|
+
return value.replace(/&(?:#(\d+)|#x([\da-f]+)|(amp|apos|gt|lt|quot));/gi, (entity, decimal, hexadecimal, named)=>{
|
|
392
|
+
if (decimal) return safeCodePoint(Number.parseInt(decimal, 10), entity);
|
|
393
|
+
if (hexadecimal) return safeCodePoint(Number.parseInt(hexadecimal, 16), entity);
|
|
394
|
+
return ({
|
|
395
|
+
amp: '&',
|
|
396
|
+
apos: "'",
|
|
397
|
+
gt: '>',
|
|
398
|
+
lt: '<',
|
|
399
|
+
quot: '"'
|
|
400
|
+
})[named.toLowerCase()] ?? entity;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
function safeCodePoint(value, fallback) {
|
|
404
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > 0x10ffff) return fallback;
|
|
405
|
+
try {
|
|
406
|
+
return String.fromCodePoint(value);
|
|
407
|
+
} catch {
|
|
408
|
+
return fallback;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function positiveInteger(value, fallback) {
|
|
412
|
+
const number = Number(value);
|
|
413
|
+
return Number.isSafeInteger(number) && number > 0 ? number : fallback;
|
|
414
|
+
}
|
|
415
|
+
function largeDocxImportNow() {
|
|
416
|
+
return globalThis.performance?.now?.() ?? Date.now();
|
|
417
|
+
}
|
|
418
|
+
function recordLargeDocxImportMeasure(name, start, end, detail) {
|
|
419
|
+
try {
|
|
420
|
+
globalThis.performance?.measure(name, {
|
|
421
|
+
detail,
|
|
422
|
+
end,
|
|
423
|
+
start
|
|
424
|
+
});
|
|
425
|
+
} catch {}
|
|
426
|
+
}
|
|
427
|
+
function decodeXmlBytes(bytes, label) {
|
|
428
|
+
let encoding = 'utf-8';
|
|
429
|
+
let offset = 0;
|
|
430
|
+
if (0xef === bytes[0] && 0xbb === bytes[1] && 0xbf === bytes[2]) offset = 3;
|
|
431
|
+
else if (0xff === bytes[0] && 0xfe === bytes[1]) {
|
|
432
|
+
encoding = 'utf-16le';
|
|
433
|
+
offset = 2;
|
|
434
|
+
} else if (0xfe === bytes[0] && 0xff === bytes[1]) {
|
|
435
|
+
encoding = 'utf-16be';
|
|
436
|
+
offset = 2;
|
|
437
|
+
} else if (0x3c === bytes[0] && 0 === bytes[1] && 0x3f === bytes[2] && 0 === bytes[3]) encoding = 'utf-16le';
|
|
438
|
+
else if (0 === bytes[0] && 0x3c === bytes[1] && 0 === bytes[2] && 0x3f === bytes[3]) encoding = 'utf-16be';
|
|
439
|
+
try {
|
|
440
|
+
return new TextDecoder(encoding, {
|
|
441
|
+
fatal: true
|
|
442
|
+
}).decode(bytes.subarray(offset));
|
|
443
|
+
} catch {
|
|
444
|
+
throw new Error(`${label} uses an invalid ${encoding} XML encoding.`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const scope = globalThis;
|
|
448
|
+
scope.onmessage = (event)=>{
|
|
449
|
+
if ('parse' !== event.data.kind) return;
|
|
450
|
+
parseDocument(event.data);
|
|
451
|
+
};
|
|
452
|
+
async function parseDocument(request) {
|
|
453
|
+
try {
|
|
454
|
+
const xmlStartedAt = workerNow();
|
|
455
|
+
const source = decodeXmlBytes(new Uint8Array(request.xmlBytes), 'word/document.xml');
|
|
456
|
+
const xmlFinishedAt = workerNow();
|
|
457
|
+
const stream = createWorkerStream();
|
|
458
|
+
const parseStartedAt = workerNow();
|
|
459
|
+
const streamed = streamLargeSimpleDocxDocumentXml(source, request.options, stream.sink);
|
|
460
|
+
const parseFinishedAt = workerNow();
|
|
461
|
+
if (!streamed || !stream.finish()) return void scope.postMessage({
|
|
462
|
+
kind: 'ineligible'
|
|
463
|
+
});
|
|
464
|
+
scope.postMessage({
|
|
465
|
+
kind: 'success',
|
|
466
|
+
streamed,
|
|
467
|
+
timings: {
|
|
468
|
+
contentMs: measureDuration('a3s-office.document.large-simple-content'),
|
|
469
|
+
eligibilityMs: measureDuration('a3s-office.document.large-simple-eligibility'),
|
|
470
|
+
envelopeMs: measureDuration('a3s-office.document.large-simple-envelope'),
|
|
471
|
+
parseMs: parseFinishedAt - parseStartedAt,
|
|
472
|
+
xmlMs: xmlFinishedAt - xmlStartedAt
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
} catch {
|
|
476
|
+
scope.postMessage({
|
|
477
|
+
kind: 'failure'
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function createWorkerStream() {
|
|
482
|
+
let paragraphTexts = [];
|
|
483
|
+
let rowCellCounts = [];
|
|
484
|
+
let cellParagraphCounts = [];
|
|
485
|
+
let tableTexts = [];
|
|
486
|
+
let tableActive = false;
|
|
487
|
+
const flushParagraphs = ()=>{
|
|
488
|
+
if (!paragraphTexts.length) return;
|
|
489
|
+
scope.postMessage({
|
|
490
|
+
kind: 'paragraphs',
|
|
491
|
+
texts: paragraphTexts
|
|
492
|
+
});
|
|
493
|
+
paragraphTexts = [];
|
|
494
|
+
};
|
|
495
|
+
const flushRows = ()=>{
|
|
496
|
+
if (!rowCellCounts.length) return;
|
|
497
|
+
const transferredRowCellCounts = new Uint32Array(rowCellCounts);
|
|
498
|
+
const transferredCellParagraphCounts = new Uint32Array(cellParagraphCounts);
|
|
499
|
+
scope.postMessage({
|
|
500
|
+
cellParagraphCounts: transferredCellParagraphCounts,
|
|
501
|
+
kind: 'table-rows',
|
|
502
|
+
rowCellCounts: transferredRowCellCounts,
|
|
503
|
+
texts: tableTexts
|
|
504
|
+
}, {
|
|
505
|
+
transfer: [
|
|
506
|
+
transferredRowCellCounts.buffer,
|
|
507
|
+
transferredCellParagraphCounts.buffer
|
|
508
|
+
]
|
|
509
|
+
});
|
|
510
|
+
rowCellCounts = [];
|
|
511
|
+
cellParagraphCounts = [];
|
|
512
|
+
tableTexts = [];
|
|
513
|
+
};
|
|
514
|
+
return {
|
|
515
|
+
finish: ()=>{
|
|
516
|
+
flushParagraphs();
|
|
517
|
+
flushRows();
|
|
518
|
+
return !tableActive;
|
|
519
|
+
},
|
|
520
|
+
sink: {
|
|
521
|
+
paragraph: (text)=>{
|
|
522
|
+
if (tableActive) throw new Error('A table stream cannot contain a paragraph batch.');
|
|
523
|
+
paragraphTexts.push(text);
|
|
524
|
+
if (paragraphTexts.length >= 2048) flushParagraphs();
|
|
525
|
+
},
|
|
526
|
+
tableEnd: ()=>{
|
|
527
|
+
if (!tableActive) throw new Error('A streamed table was not started.');
|
|
528
|
+
flushRows();
|
|
529
|
+
scope.postMessage({
|
|
530
|
+
kind: 'table-end'
|
|
531
|
+
});
|
|
532
|
+
tableActive = false;
|
|
533
|
+
},
|
|
534
|
+
tableRow: ({ cellParagraphCounts: counts, texts })=>{
|
|
535
|
+
if (!tableActive) throw new Error('A streamed table row has no owning table.');
|
|
536
|
+
rowCellCounts.push(counts.length);
|
|
537
|
+
cellParagraphCounts.push(...counts);
|
|
538
|
+
tableTexts.push(...texts);
|
|
539
|
+
if (rowCellCounts.length >= 2048) flushRows();
|
|
540
|
+
},
|
|
541
|
+
tableStart: ()=>{
|
|
542
|
+
if (tableActive) throw new Error('Nested tables are not supported.');
|
|
543
|
+
flushParagraphs();
|
|
544
|
+
tableActive = true;
|
|
545
|
+
scope.postMessage({
|
|
546
|
+
kind: 'table-start'
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
function workerNow() {
|
|
553
|
+
return globalThis.performance?.now?.() ?? Date.now();
|
|
554
|
+
}
|
|
555
|
+
function measureDuration(name) {
|
|
556
|
+
return globalThis.performance?.getEntriesByName?.(name).at(-1)?.duration ?? 0;
|
|
557
|
+
}
|
|
558
|
+
})();
|