@a3s-lab/office 0.35.0 → 0.37.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.md +34 -10
- package/dist/0~7048.js +590 -3
- package/dist/0~spreadsheet-editor.js +515 -44
- package/dist/0~work-office-diagnostics.js +1 -1
- package/dist/3266.js +549 -10
- package/dist/4121.js +2202 -1942
- package/dist/8715.js +299 -299
- package/dist/core.js +41 -4
- package/dist/internal/features/work/editors/spreadsheet-calculation-model.d.ts +1 -1
- package/dist/internal/features/work/editors/spreadsheet-calculation-projection.d.ts +5 -2
- package/dist/internal/features/work/editors/spreadsheet-table-calculated-columns.d.ts +42 -0
- package/dist/internal/features/work/editors/spreadsheet-table-totals.d.ts +39 -0
- package/dist/internal/features/work/editors/spreadsheet-table.d.ts +11 -1
- package/dist/internal/features/work/work-types.d.ts +20 -1
- package/dist/internal/kernel/office-kernel-protocol.d.ts +1 -1
- package/dist/internal/kernel/office-kernel-spreadsheet-fallback-formula.d.ts +11 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-protocol.d.ts +18 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-structured-reference.d.ts +48 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/office-kernel.worker.js +598 -3
- package/dist/styles.css +120 -0
- package/docs/latest/en/browser-editor-architecture.md +27 -8
- package/package.json +4 -1
package/dist/4121.js
CHANGED
|
@@ -68,2146 +68,2146 @@ function nonNegativeInteger(value) {
|
|
|
68
68
|
const number = Number(value);
|
|
69
69
|
return Number.isSafeInteger(number) && number >= 0 ? number : null;
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const DEFAULT_DOCUMENT_INDEX_OPTIONS = {
|
|
72
|
+
columns: 1,
|
|
73
|
+
format: 'indented',
|
|
74
|
+
rightAlignPageNumbers: true,
|
|
75
|
+
leader: 'dot'
|
|
76
|
+
};
|
|
77
|
+
const MAX_DOCUMENT_INDEX_ENTRIES = 512;
|
|
78
|
+
const MAX_DOCUMENT_INDEX_MARKERS = 2048;
|
|
79
|
+
const MAX_DOCUMENT_INDEX_TERM_LENGTH = 240;
|
|
80
|
+
const INDEX_ENTRY_SELECTOR = '[data-document-index-entry]';
|
|
81
|
+
const INDEX_SELECTOR = '[data-document-index]';
|
|
82
|
+
const INDEX_ID_PATTERN = /^[a-z0-9][a-z0-9._:-]{0,127}$/i;
|
|
83
|
+
function normalizeDocumentIndexEntryDraft(source) {
|
|
84
|
+
const mainEntry = normalizedIndexTerm(source?.mainEntry);
|
|
85
|
+
if (!mainEntry) return null;
|
|
86
|
+
const subEntry = normalizedIndexTerm(source?.subEntry);
|
|
87
|
+
const crossReference = normalizedIndexTerm(source?.crossReference);
|
|
88
|
+
return {
|
|
89
|
+
mainEntry,
|
|
90
|
+
subEntry,
|
|
91
|
+
crossReference,
|
|
92
|
+
pageBold: !crossReference && Boolean(source?.pageBold),
|
|
93
|
+
pageItalic: !crossReference && Boolean(source?.pageItalic)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function normalizeDocumentIndexEntry(source, fallbackId = 'index-entry') {
|
|
97
|
+
const value = normalizeDocumentIndexEntryDraft(source);
|
|
73
98
|
if (!value) return null;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const rgb = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*([\d.]+))?\s*\)$/i.exec(value);
|
|
79
|
-
if (!rgb) return null;
|
|
80
|
-
const channels = rgb.slice(1, 4).map(Number);
|
|
81
|
-
if (channels.some((channel)=>channel < 0 || channel > 255)) return null;
|
|
82
|
-
if (void 0 !== rgb[4] && 0 === Number(rgb[4])) return 'transparent';
|
|
83
|
-
if (void 0 !== rgb[4] && 1 !== Number(rgb[4])) return null;
|
|
84
|
-
return `#${channels.map((channel)=>channel.toString(16).padStart(2, '0')).join('')}`;
|
|
99
|
+
return {
|
|
100
|
+
id: validIndexId(source?.id) ?? fallbackId,
|
|
101
|
+
...value
|
|
102
|
+
};
|
|
85
103
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
} else if (0xfe === bytes[0] && 0xff === bytes[1]) {
|
|
94
|
-
encoding = 'utf-16be';
|
|
95
|
-
offset = 2;
|
|
96
|
-
} else if (0x3c === bytes[0] && 0 === bytes[1] && 0x3f === bytes[2] && 0 === bytes[3]) encoding = 'utf-16le';
|
|
97
|
-
else if (0 === bytes[0] && 0x3c === bytes[1] && 0 === bytes[2] && 0x3f === bytes[3]) encoding = 'utf-16be';
|
|
98
|
-
try {
|
|
99
|
-
return new TextDecoder(encoding, {
|
|
100
|
-
fatal: true
|
|
101
|
-
}).decode(bytes.subarray(offset));
|
|
102
|
-
} catch {
|
|
103
|
-
throw new Error(`${label} uses an invalid ${encoding} XML encoding.`);
|
|
104
|
-
}
|
|
104
|
+
function normalizeDocumentIndexOptions(source) {
|
|
105
|
+
return {
|
|
106
|
+
columns: boundedColumns(source?.columns),
|
|
107
|
+
format: source?.format === 'run-in' ? 'run-in' : 'indented',
|
|
108
|
+
rightAlignPageNumbers: source?.rightAlignPageNumbers !== false,
|
|
109
|
+
leader: indexLeader(source?.leader) ?? 'dot'
|
|
110
|
+
};
|
|
105
111
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
function normalizeDocumentIndexValue(source) {
|
|
113
|
+
return {
|
|
114
|
+
id: validIndexId(source.id) ?? 'document-index',
|
|
115
|
+
options: normalizeDocumentIndexOptions(source.options),
|
|
116
|
+
entries: normalizeGeneratedEntries(source.entries),
|
|
117
|
+
truncated: Boolean(source.truncated)
|
|
118
|
+
};
|
|
110
119
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const pending = entry.async('uint8array').then((bytes)=>decodeXmlBytes(bytes, partPath));
|
|
132
|
-
this.textCache.set(partPath, pending);
|
|
133
|
-
try {
|
|
134
|
-
return await pending;
|
|
135
|
-
} catch (error) {
|
|
136
|
-
this.textCache.delete(partPath);
|
|
137
|
-
throw error;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
async xml(partPath) {
|
|
141
|
-
return parseXml(await this.text(partPath), partPath);
|
|
142
|
-
}
|
|
143
|
-
async bytes(partPath) {
|
|
144
|
-
const entry = this.zip.file(partPath);
|
|
145
|
-
if (!entry) throw new Error(`Office package part is missing: ${partPath}`);
|
|
146
|
-
return entry.async('uint8array');
|
|
147
|
-
}
|
|
148
|
-
async relationships(sourcePart) {
|
|
149
|
-
const partPath = relationshipsPartPath(sourcePart);
|
|
150
|
-
if (!this.has(partPath)) return new Map();
|
|
151
|
-
const document = await this.xml(partPath);
|
|
152
|
-
return new Map(descendants(document, 'Relationship').map((element)=>{
|
|
153
|
-
const relationship = {
|
|
154
|
-
id: attribute(element, 'Id') ?? '',
|
|
155
|
-
target: resolvePartTarget(sourcePart, attribute(element, 'Target') ?? ''),
|
|
156
|
-
type: attribute(element, 'Type') ?? '',
|
|
157
|
-
targetMode: attribute(element, 'TargetMode') ?? void 0
|
|
120
|
+
function buildDocumentIndexEntries(document, options = {}) {
|
|
121
|
+
const grouped = new Map();
|
|
122
|
+
let markerCount = 0;
|
|
123
|
+
document.descendants((node, position)=>{
|
|
124
|
+
if ('documentIndexEntry' !== node.type.name) return;
|
|
125
|
+
markerCount += 1;
|
|
126
|
+
if (markerCount > MAX_DOCUMENT_INDEX_MARKERS) return;
|
|
127
|
+
const marker = normalizeDocumentIndexEntry(node.attrs, `index-entry-${markerCount}`);
|
|
128
|
+
if (!marker) return;
|
|
129
|
+
const key = indexEntryKey(marker);
|
|
130
|
+
let group = grouped.get(key);
|
|
131
|
+
if (!group) {
|
|
132
|
+
group = {
|
|
133
|
+
entry: {
|
|
134
|
+
mainEntry: marker.mainEntry,
|
|
135
|
+
subEntry: marker.subEntry,
|
|
136
|
+
crossReference: marker.crossReference,
|
|
137
|
+
pages: []
|
|
138
|
+
},
|
|
139
|
+
pages: new Map()
|
|
158
140
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
141
|
+
grouped.set(key, group);
|
|
142
|
+
}
|
|
143
|
+
if (marker.crossReference) return;
|
|
144
|
+
const pageNumber = positiveInteger(options.resolveContext?.(position)?.pageNumber) ?? fallbackDocumentPageNumber(document, position);
|
|
145
|
+
const existing = group.pages.get(pageNumber);
|
|
146
|
+
if (existing) {
|
|
147
|
+
existing.pageBold ||= marker.pageBold;
|
|
148
|
+
existing.pageItalic ||= marker.pageItalic;
|
|
149
|
+
if (!existing.targetIds.includes(marker.id)) existing.targetIds.push(marker.id);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
group.pages.set(pageNumber, {
|
|
153
|
+
pageNumber,
|
|
154
|
+
pageBold: marker.pageBold,
|
|
155
|
+
pageItalic: marker.pageItalic,
|
|
156
|
+
targetIds: [
|
|
157
|
+
marker.id
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
const collator = new Intl.Collator(void 0, {
|
|
162
|
+
numeric: true,
|
|
163
|
+
sensitivity: 'base',
|
|
164
|
+
usage: 'sort'
|
|
165
|
+
});
|
|
166
|
+
const allEntries = Array.from(grouped.values()).map(({ entry, pages })=>({
|
|
167
|
+
...entry,
|
|
168
|
+
pages: Array.from(pages.values()).sort((left, right)=>left.pageNumber - right.pageNumber)
|
|
169
|
+
})).sort((left, right)=>collator.compare(left.mainEntry, right.mainEntry) || collator.compare(left.subEntry, right.subEntry) || collator.compare(left.crossReference, right.crossReference));
|
|
170
|
+
return {
|
|
171
|
+
entries: allEntries.slice(0, MAX_DOCUMENT_INDEX_ENTRIES),
|
|
172
|
+
truncated: markerCount > MAX_DOCUMENT_INDEX_MARKERS || allEntries.length > MAX_DOCUMENT_INDEX_ENTRIES
|
|
173
|
+
};
|
|
165
174
|
}
|
|
166
|
-
function
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return
|
|
175
|
+
function documentIndexEntryHtml(source) {
|
|
176
|
+
const value = normalizeDocumentIndexEntry(source);
|
|
177
|
+
if (!value) return '';
|
|
178
|
+
const detail = indexEntryDisplay(value);
|
|
179
|
+
return [
|
|
180
|
+
`<span data-document-index-entry="true" data-index-entry-id="${escapeHtmlAttribute(value.id)}" data-index-main-entry="${escapeHtmlAttribute(value.mainEntry)}" data-index-sub-entry="${escapeHtmlAttribute(value.subEntry)}" data-index-cross-reference="${escapeHtmlAttribute(value.crossReference)}" data-index-page-bold="${String(value.pageBold)}" data-index-page-italic="${String(value.pageItalic)}" class="work-document-index-entry" contenteditable="false" aria-label="索引项:${escapeHtmlAttribute(detail)}">`,
|
|
181
|
+
'<span aria-hidden="true">索引项</span>',
|
|
182
|
+
`<strong>${escapeHtml(detail)}</strong>`,
|
|
183
|
+
'</span>'
|
|
184
|
+
].join('');
|
|
171
185
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
186
|
+
function documentIndexHtml(source) {
|
|
187
|
+
const value = normalizeDocumentIndexValue(source);
|
|
188
|
+
const rows = value.entries.length ? value.entries.map(documentIndexRowHtml) : [
|
|
189
|
+
'<li class="work-document-index-empty">没有已标记的索引项</li>'
|
|
190
|
+
];
|
|
191
|
+
const status = value.truncated ? `仅显示前 ${MAX_DOCUMENT_INDEX_ENTRIES} 项` : `${value.entries.length} 项`;
|
|
192
|
+
return [
|
|
193
|
+
`<div data-document-index="true" data-index-id="${escapeHtmlAttribute(value.id)}" data-index-columns="${value.options.columns}" data-index-format="${value.options.format}" data-index-right-align-page-numbers="${String(value.options.rightAlignPageNumbers)}" data-index-leader="${value.options.leader}" data-index-entries="${escapeHtmlAttribute(JSON.stringify(value.entries))}" data-index-truncated="${String(value.truncated)}" class="work-document-index" contenteditable="false" aria-label="索引">`,
|
|
194
|
+
`<div class="work-document-index-header"><strong>索引</strong><span>${status}</span></div>`,
|
|
195
|
+
'<ol class="work-document-index-list">',
|
|
196
|
+
...rows,
|
|
197
|
+
'</ol></div>'
|
|
198
|
+
].join('');
|
|
183
199
|
}
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
200
|
+
function documentIndexEntryFromElement(element, index = 1) {
|
|
201
|
+
return normalizeDocumentIndexEntry({
|
|
202
|
+
id: element.dataset.indexEntryId,
|
|
203
|
+
mainEntry: element.dataset.indexMainEntry,
|
|
204
|
+
subEntry: element.dataset.indexSubEntry,
|
|
205
|
+
crossReference: element.dataset.indexCrossReference,
|
|
206
|
+
pageBold: 'true' === element.dataset.indexPageBold,
|
|
207
|
+
pageItalic: 'true' === element.dataset.indexPageItalic
|
|
208
|
+
}, `index-entry-${index}`);
|
|
192
209
|
}
|
|
193
|
-
function
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
function directChildren(parent, localName) {
|
|
209
|
-
return Array.from(parent.children).filter((element)=>!localName || element.localName === localName);
|
|
210
|
-
}
|
|
211
|
-
function directChild(parent, localName) {
|
|
212
|
-
return directChildren(parent, localName)[0];
|
|
213
|
-
}
|
|
214
|
-
function descendants(parent, localName) {
|
|
215
|
-
return Array.from(parent.querySelectorAll('*')).filter((element)=>element.localName === localName);
|
|
216
|
-
}
|
|
217
|
-
function firstDescendant(parent, localName) {
|
|
218
|
-
if (!parent) return;
|
|
219
|
-
return descendants(parent, localName)[0];
|
|
210
|
+
function documentIndexValueFromElement(element, index = 1) {
|
|
211
|
+
return normalizeDocumentIndexValue({
|
|
212
|
+
id: validIndexId(element.dataset.indexId) ?? `document-index-${index}`,
|
|
213
|
+
options: {
|
|
214
|
+
columns: Number(element.dataset.indexColumns),
|
|
215
|
+
format: 'run-in' === element.dataset.indexFormat ? 'run-in' : 'indented',
|
|
216
|
+
rightAlignPageNumbers: 'false' !== element.dataset.indexRightAlignPageNumbers,
|
|
217
|
+
leader: indexLeader(element.dataset.indexLeader) ?? 'dot'
|
|
218
|
+
},
|
|
219
|
+
entries: parseGeneratedEntries(element.dataset.indexEntries),
|
|
220
|
+
truncated: 'true' === element.dataset.indexTruncated
|
|
221
|
+
});
|
|
220
222
|
}
|
|
221
|
-
function
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
function normalizeDocumentIndexesHtml(source) {
|
|
224
|
+
const document = new DOMParser().parseFromString(source, 'text/html');
|
|
225
|
+
const usedEntryIds = new Set();
|
|
226
|
+
for (const [index, element] of Array.from(document.body.querySelectorAll(INDEX_ENTRY_SELECTOR)).entries()){
|
|
227
|
+
const value = documentIndexEntryFromElement(element, index + 1);
|
|
228
|
+
if (!value) {
|
|
229
|
+
element.remove();
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
value.id = uniqueIndexId(value.id, 'index-entry', index + 1, usedEntryIds);
|
|
233
|
+
element.replaceWith(document.createRange().createContextualFragment(documentIndexEntryHtml(value)));
|
|
226
234
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
return segments.join('/');
|
|
235
|
-
}
|
|
236
|
-
function contentTypeForPart(partPath) {
|
|
237
|
-
const extension = partPath.split('.').pop()?.toLowerCase();
|
|
238
|
-
const types = {
|
|
239
|
-
apng: 'image/apng',
|
|
240
|
-
bmp: 'image/bmp',
|
|
241
|
-
emf: 'image/emf',
|
|
242
|
-
gif: 'image/gif',
|
|
243
|
-
jpeg: 'image/jpeg',
|
|
244
|
-
jpg: 'image/jpeg',
|
|
245
|
-
png: 'image/png',
|
|
246
|
-
svg: 'image/svg+xml',
|
|
247
|
-
tif: 'image/tiff',
|
|
248
|
-
tiff: 'image/tiff',
|
|
249
|
-
webp: 'image/webp',
|
|
250
|
-
wmf: 'image/wmf'
|
|
251
|
-
};
|
|
252
|
-
return types[extension ?? ''] ?? 'application/octet-stream';
|
|
235
|
+
const usedIndexIds = new Set();
|
|
236
|
+
for (const [index, element] of Array.from(document.body.querySelectorAll(INDEX_SELECTOR)).entries()){
|
|
237
|
+
const value = documentIndexValueFromElement(element, index + 1);
|
|
238
|
+
value.id = uniqueIndexId(value.id, 'document-index', index + 1, usedIndexIds);
|
|
239
|
+
element.replaceWith(document.createRange().createContextualFragment(documentIndexHtml(value)));
|
|
240
|
+
}
|
|
241
|
+
return document.body.innerHTML;
|
|
253
242
|
}
|
|
254
|
-
function
|
|
255
|
-
|
|
256
|
-
const chunkSize = 32768;
|
|
257
|
-
for(let offset = 0; offset < bytes.length; offset += chunkSize)binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
|
|
258
|
-
return `data:${contentType};base64,${btoa(binary)}`;
|
|
243
|
+
function indexLeader(value) {
|
|
244
|
+
return 'dot' === value || 'dash' === value || 'underline' === value || 'none' === value ? value : null;
|
|
259
245
|
}
|
|
260
|
-
function
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
246
|
+
function documentIndexRowHtml(entry) {
|
|
247
|
+
const term = entry.subEntry ? `<span class="work-document-index-main">${escapeHtml(entry.mainEntry)}</span><span class="work-document-index-sub">${escapeHtml(entry.subEntry)}</span>` : `<span class="work-document-index-main">${escapeHtml(entry.mainEntry)}</span>`;
|
|
248
|
+
const pages = entry.crossReference ? `<span class="work-document-index-cross-reference">参见 ${escapeHtml(entry.crossReference)}</span>` : entry.pages.map((page)=>{
|
|
249
|
+
const target = page.targetIds[0] ?? '';
|
|
250
|
+
const classes = [
|
|
251
|
+
page.pageBold ? 'bold' : '',
|
|
252
|
+
page.pageItalic ? 'italic' : ''
|
|
253
|
+
].filter(Boolean).join(' ');
|
|
254
|
+
return `<a href="#${escapeHtmlAttribute(target)}" data-index-target="${escapeHtmlAttribute(target)}" tabindex="-1"${classes ? ` class="${classes}"` : ''}>${page.pageNumber}</a>`;
|
|
255
|
+
}).join('<span aria-hidden="true">, </span>');
|
|
256
|
+
return `<li data-index-main-entry="${escapeHtmlAttribute(entry.mainEntry)}" data-index-sub-entry="${escapeHtmlAttribute(entry.subEntry)}">${term}<i aria-hidden="true"></i><span class="work-document-index-pages">${pages}</span></li>`;
|
|
265
257
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
this.usedColors.add(marker);
|
|
282
|
-
this.patches.push({
|
|
283
|
-
kind,
|
|
284
|
-
marker,
|
|
285
|
-
reference
|
|
258
|
+
function normalizeGeneratedEntries(source) {
|
|
259
|
+
const entries = [];
|
|
260
|
+
for (const item of source.slice(0, MAX_DOCUMENT_INDEX_ENTRIES)){
|
|
261
|
+
if (!item || 'object' != typeof item) continue;
|
|
262
|
+
const candidate = item;
|
|
263
|
+
const draft = normalizeDocumentIndexEntryDraft({
|
|
264
|
+
mainEntry: normalizedIndexTerm(candidate.mainEntry),
|
|
265
|
+
subEntry: normalizedIndexTerm(candidate.subEntry),
|
|
266
|
+
crossReference: normalizedIndexTerm(candidate.crossReference)
|
|
267
|
+
});
|
|
268
|
+
if (draft) entries.push({
|
|
269
|
+
mainEntry: draft.mainEntry,
|
|
270
|
+
subEntry: draft.subEntry,
|
|
271
|
+
crossReference: draft.crossReference,
|
|
272
|
+
pages: draft.crossReference ? [] : normalizeIndexPages(candidate.pages)
|
|
286
273
|
});
|
|
287
|
-
return marker;
|
|
288
274
|
}
|
|
275
|
+
return entries;
|
|
289
276
|
}
|
|
290
|
-
function
|
|
291
|
-
|
|
277
|
+
function normalizeIndexPages(source) {
|
|
278
|
+
if (!Array.isArray(source)) return [];
|
|
279
|
+
const pages = [];
|
|
280
|
+
const seen = new Set();
|
|
281
|
+
for (const item of source.slice(0, MAX_DOCUMENT_INDEX_MARKERS)){
|
|
282
|
+
if (!item || 'object' != typeof item) continue;
|
|
283
|
+
const candidate = item;
|
|
284
|
+
const pageNumber = positiveInteger(candidate.pageNumber);
|
|
285
|
+
if (!pageNumber || seen.has(pageNumber)) continue;
|
|
286
|
+
const targetIds = Array.isArray(candidate.targetIds) ? candidate.targetIds.flatMap((value)=>validIndexId(value) ? [
|
|
287
|
+
String(value)
|
|
288
|
+
] : []).slice(0, 64) : [];
|
|
289
|
+
if (targetIds.length) {
|
|
290
|
+
seen.add(pageNumber);
|
|
291
|
+
pages.push({
|
|
292
|
+
pageNumber,
|
|
293
|
+
pageBold: Boolean(candidate.pageBold),
|
|
294
|
+
pageItalic: Boolean(candidate.pageItalic),
|
|
295
|
+
targetIds
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return pages.sort((left, right)=>left.pageNumber - right.pageNumber);
|
|
292
300
|
}
|
|
293
|
-
function
|
|
294
|
-
if (!
|
|
301
|
+
function parseGeneratedEntries(source) {
|
|
302
|
+
if (!source) return [];
|
|
295
303
|
try {
|
|
296
|
-
const parsed = JSON.parse(
|
|
297
|
-
|
|
298
|
-
const resolved = normalizeColor('string' == typeof parsed.resolved ? parsed.resolved : null);
|
|
299
|
-
const tint = byteHex(parsed.tint);
|
|
300
|
-
const shade = byteHex(parsed.shade);
|
|
301
|
-
if (!theme || !resolved) return null;
|
|
302
|
-
return {
|
|
303
|
-
theme,
|
|
304
|
-
resolved,
|
|
305
|
-
...tint ? {
|
|
306
|
-
tint
|
|
307
|
-
} : {},
|
|
308
|
-
...shade ? {
|
|
309
|
-
shade
|
|
310
|
-
} : {}
|
|
311
|
-
};
|
|
304
|
+
const parsed = JSON.parse(source);
|
|
305
|
+
return Array.isArray(parsed) ? normalizeGeneratedEntries(parsed) : [];
|
|
312
306
|
} catch {
|
|
313
|
-
return
|
|
307
|
+
return [];
|
|
314
308
|
}
|
|
315
309
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
]));
|
|
323
|
-
const entries = Object.values(archive.files).filter((entry)=>!entry.dir && /^word\/(?:document|header\d+|footer\d+|footnotes|endnotes|comments)\.xml$/.test(entry.name));
|
|
324
|
-
for (const entry of entries){
|
|
325
|
-
const document = parseXml(await entry.async('text'), entry.name);
|
|
326
|
-
let changed = false;
|
|
327
|
-
for (const element of Array.from(document.getElementsByTagName('*')))for (const target of themePatchTargets(element.localName)){
|
|
328
|
-
const marker = wordAttribute(element, target.directAttribute)?.toUpperCase();
|
|
329
|
-
const patch = marker ? byMarker.get(marker) : void 0;
|
|
330
|
-
if (patch && patch.kind === target.kind) {
|
|
331
|
-
setWordAttribute(document, element, target.directAttribute, patch.reference.resolved.slice(1).toUpperCase());
|
|
332
|
-
setWordAttribute(document, element, target.themeAttribute, patch.reference.theme);
|
|
333
|
-
setOptionalWordAttribute(document, element, target.tintAttribute, patch.reference.tint);
|
|
334
|
-
setOptionalWordAttribute(document, element, target.shadeAttribute, patch.reference.shade);
|
|
335
|
-
changed = true;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (changed) archive.file(entry.name, new XMLSerializer().serializeToString(document));
|
|
339
|
-
}
|
|
340
|
-
return archive.generateAsync({
|
|
341
|
-
type: 'arraybuffer'
|
|
310
|
+
function fallbackDocumentPageNumber(document, position) {
|
|
311
|
+
let pageNumber = 1;
|
|
312
|
+
document.descendants((node, offset)=>{
|
|
313
|
+
if (offset >= position) return false;
|
|
314
|
+
if ('pageBreak' === node.type.name) pageNumber += 1;
|
|
315
|
+
return true;
|
|
342
316
|
});
|
|
317
|
+
return pageNumber;
|
|
343
318
|
}
|
|
344
|
-
function
|
|
345
|
-
if ('color' === localName) return [
|
|
346
|
-
{
|
|
347
|
-
kind: 'color',
|
|
348
|
-
directAttribute: 'val',
|
|
349
|
-
themeAttribute: 'themeColor',
|
|
350
|
-
tintAttribute: 'themeTint',
|
|
351
|
-
shadeAttribute: 'themeShade'
|
|
352
|
-
}
|
|
353
|
-
];
|
|
354
|
-
if ('u' === localName) return [
|
|
355
|
-
{
|
|
356
|
-
kind: 'underline',
|
|
357
|
-
directAttribute: 'color',
|
|
358
|
-
themeAttribute: 'themeColor',
|
|
359
|
-
tintAttribute: 'themeTint',
|
|
360
|
-
shadeAttribute: 'themeShade'
|
|
361
|
-
}
|
|
362
|
-
];
|
|
363
|
-
if ('shd' === localName) return [
|
|
364
|
-
{
|
|
365
|
-
kind: 'fill',
|
|
366
|
-
directAttribute: 'fill',
|
|
367
|
-
themeAttribute: 'themeFill',
|
|
368
|
-
tintAttribute: 'themeFillTint',
|
|
369
|
-
shadeAttribute: 'themeFillShade'
|
|
370
|
-
},
|
|
371
|
-
{
|
|
372
|
-
kind: 'shadingColor',
|
|
373
|
-
directAttribute: 'color',
|
|
374
|
-
themeAttribute: 'themeColor',
|
|
375
|
-
tintAttribute: 'themeTint',
|
|
376
|
-
shadeAttribute: 'themeShade'
|
|
377
|
-
}
|
|
378
|
-
];
|
|
319
|
+
function indexEntryKey(entry) {
|
|
379
320
|
return [
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
'start',
|
|
385
|
-
'end'
|
|
386
|
-
].includes(localName) ? [
|
|
387
|
-
{
|
|
388
|
-
kind: 'border',
|
|
389
|
-
directAttribute: 'color',
|
|
390
|
-
themeAttribute: 'themeColor',
|
|
391
|
-
tintAttribute: 'themeTint',
|
|
392
|
-
shadeAttribute: 'themeShade'
|
|
393
|
-
}
|
|
394
|
-
] : [];
|
|
321
|
+
entry.mainEntry,
|
|
322
|
+
entry.subEntry,
|
|
323
|
+
entry.crossReference
|
|
324
|
+
].map((value)=>value.normalize('NFKC').toLocaleLowerCase()).join('\u0000');
|
|
395
325
|
}
|
|
396
|
-
function
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
return normalized;
|
|
326
|
+
function indexEntryDisplay(entry) {
|
|
327
|
+
const term = entry.subEntry ? `${entry.mainEntry} › ${entry.subEntry}` : entry.mainEntry;
|
|
328
|
+
return entry.crossReference ? `${term} · 参见 ${entry.crossReference}` : term;
|
|
400
329
|
}
|
|
401
|
-
function
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
330
|
+
function normalizedIndexTerm(value) {
|
|
331
|
+
return 'string' == typeof value ? value.replace(/\s+/g, ' ').trim().slice(0, MAX_DOCUMENT_INDEX_TERM_LENGTH) : '';
|
|
332
|
+
}
|
|
333
|
+
function boundedColumns(value) {
|
|
334
|
+
const number = Number(value);
|
|
335
|
+
return Number.isInteger(number) && number >= 1 && number <= 4 ? number : 1;
|
|
336
|
+
}
|
|
337
|
+
function positiveInteger(value) {
|
|
338
|
+
const number = Number(value);
|
|
339
|
+
return Number.isSafeInteger(number) && number > 0 ? Math.min(999999, number) : null;
|
|
340
|
+
}
|
|
341
|
+
function validIndexId(value) {
|
|
342
|
+
return 'string' == typeof value && INDEX_ID_PATTERN.test(value) ? value : null;
|
|
343
|
+
}
|
|
344
|
+
function uniqueIndexId(source, prefix, index, used) {
|
|
345
|
+
if (!used.has(source)) {
|
|
346
|
+
used.add(source);
|
|
347
|
+
return source;
|
|
407
348
|
}
|
|
408
|
-
|
|
349
|
+
let suffix = index;
|
|
350
|
+
while(used.has(`${prefix}-${suffix}`))suffix += 1;
|
|
351
|
+
const id = `${prefix}-${suffix}`;
|
|
352
|
+
used.add(id);
|
|
353
|
+
return id;
|
|
409
354
|
}
|
|
410
|
-
function
|
|
411
|
-
|
|
412
|
-
const normalized = value.trim().toUpperCase();
|
|
413
|
-
return /^[0-9A-F]{2}$/.test(normalized) ? normalized : void 0;
|
|
355
|
+
function escapeHtml(value) {
|
|
356
|
+
return value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
414
357
|
}
|
|
415
|
-
function
|
|
416
|
-
return
|
|
358
|
+
function escapeHtmlAttribute(value) {
|
|
359
|
+
return escapeHtml(value).replaceAll('"', '"').replaceAll("'", ''');
|
|
417
360
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
361
|
+
const DOCUMENT_SCRIPT_FONTS_ATTRIBUTE = "data-office-script-fonts";
|
|
362
|
+
const DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE = "data-office-script-font-slot";
|
|
363
|
+
const MAX_FONT_NAME_LENGTH = 127;
|
|
364
|
+
const MAX_SERIALIZED_SCRIPT_FONTS_LENGTH = 4096;
|
|
365
|
+
const SCRIPT_FONT_KEYS = new Set([
|
|
366
|
+
'ascii',
|
|
367
|
+
'highAnsi',
|
|
368
|
+
'eastAsia',
|
|
369
|
+
'complexScript',
|
|
370
|
+
'hint'
|
|
371
|
+
]);
|
|
372
|
+
const SCRIPT_FONT_FACE_KEYS = new Set([
|
|
373
|
+
'name',
|
|
374
|
+
'theme',
|
|
375
|
+
'resolved'
|
|
376
|
+
]);
|
|
377
|
+
const SCRIPT_FONT_HINTS = new Set([
|
|
378
|
+
'default',
|
|
379
|
+
'eastAsia',
|
|
380
|
+
'cs'
|
|
381
|
+
]);
|
|
382
|
+
const THEME_FONTS = new Set([
|
|
383
|
+
'majorEastAsia',
|
|
384
|
+
'majorBidi',
|
|
385
|
+
'majorAscii',
|
|
386
|
+
'majorHAnsi',
|
|
387
|
+
'minorEastAsia',
|
|
388
|
+
'minorBidi',
|
|
389
|
+
'minorAscii',
|
|
390
|
+
'minorHAnsi'
|
|
391
|
+
]);
|
|
392
|
+
const SLOT_FALLBACK_ORDER = {
|
|
393
|
+
ascii: [
|
|
394
|
+
'ascii',
|
|
395
|
+
'highAnsi',
|
|
396
|
+
'eastAsia',
|
|
397
|
+
'complexScript'
|
|
398
|
+
],
|
|
399
|
+
highAnsi: [
|
|
400
|
+
'highAnsi',
|
|
401
|
+
'ascii',
|
|
402
|
+
'eastAsia',
|
|
403
|
+
'complexScript'
|
|
404
|
+
],
|
|
405
|
+
eastAsia: [
|
|
406
|
+
'eastAsia',
|
|
407
|
+
'highAnsi',
|
|
408
|
+
'ascii',
|
|
409
|
+
'complexScript'
|
|
410
|
+
],
|
|
411
|
+
complexScript: [
|
|
412
|
+
'complexScript',
|
|
413
|
+
'highAnsi',
|
|
414
|
+
'ascii',
|
|
415
|
+
'eastAsia'
|
|
416
|
+
]
|
|
417
|
+
};
|
|
418
|
+
const NEUTRAL_SCRIPT_CHARACTER = /^[\p{Cc}\p{Cf}\p{M}\p{N}\p{P}\p{S}\p{Z}]$/u;
|
|
419
|
+
function normalizeDocumentScriptFonts(source) {
|
|
420
|
+
if (!isRecordWithKeys(source, SCRIPT_FONT_KEYS)) return null;
|
|
421
|
+
const normalized = {};
|
|
422
|
+
for (const slot of scriptFontSlots){
|
|
423
|
+
if (void 0 === source[slot]) continue;
|
|
424
|
+
const face = normalizeDocumentScriptFontFace(source[slot]);
|
|
425
|
+
if (!face) return null;
|
|
426
|
+
normalized[slot] = face;
|
|
427
|
+
}
|
|
428
|
+
if (void 0 !== source.hint) {
|
|
429
|
+
const hint = normalizeDocumentScriptFontHint(source.hint);
|
|
430
|
+
if (!hint) return null;
|
|
431
|
+
normalized.hint = hint;
|
|
432
|
+
}
|
|
433
|
+
return Object.keys(normalized).length ? normalized : null;
|
|
421
434
|
}
|
|
422
|
-
function
|
|
423
|
-
|
|
424
|
-
|
|
435
|
+
function serializeDocumentScriptFonts(source) {
|
|
436
|
+
const fonts = normalizeDocumentScriptFonts(source);
|
|
437
|
+
return fonts ? JSON.stringify(fonts) : null;
|
|
425
438
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
'between',
|
|
433
|
-
'bar'
|
|
434
|
-
];
|
|
435
|
-
const DOCUMENT_PARAGRAPH_BORDER_STYLES = [
|
|
436
|
-
'nil',
|
|
437
|
-
'none',
|
|
438
|
-
'single',
|
|
439
|
-
'thick',
|
|
440
|
-
'double',
|
|
441
|
-
'dotted',
|
|
442
|
-
'dashed',
|
|
443
|
-
'dotDash',
|
|
444
|
-
'dotDotDash',
|
|
445
|
-
'triple',
|
|
446
|
-
'thinThickSmallGap',
|
|
447
|
-
'thickThinSmallGap',
|
|
448
|
-
'thinThickThinSmallGap',
|
|
449
|
-
'thinThickMediumGap',
|
|
450
|
-
'thickThinMediumGap',
|
|
451
|
-
'thinThickThinMediumGap',
|
|
452
|
-
'thinThickLargeGap',
|
|
453
|
-
'thickThinLargeGap',
|
|
454
|
-
'thinThickThinLargeGap',
|
|
455
|
-
'wave',
|
|
456
|
-
'doubleWave',
|
|
457
|
-
'dashSmallGap',
|
|
458
|
-
'dashDotStroked',
|
|
459
|
-
'threeDEmboss',
|
|
460
|
-
'threeDEngrave',
|
|
461
|
-
'outset',
|
|
462
|
-
'inset',
|
|
463
|
-
'apples',
|
|
464
|
-
'archedScallops',
|
|
465
|
-
'babyPacifier',
|
|
466
|
-
'babyRattle',
|
|
467
|
-
'balloons3Colors',
|
|
468
|
-
'balloonsHotAir',
|
|
469
|
-
'basicBlackDashes',
|
|
470
|
-
'basicBlackDots',
|
|
471
|
-
'basicBlackSquares',
|
|
472
|
-
'basicThinLines',
|
|
473
|
-
'basicWhiteDashes',
|
|
474
|
-
'basicWhiteDots',
|
|
475
|
-
'basicWhiteSquares',
|
|
476
|
-
'basicWideInline',
|
|
477
|
-
'basicWideMidline',
|
|
478
|
-
'basicWideOutline',
|
|
479
|
-
'bats',
|
|
480
|
-
'birds',
|
|
481
|
-
'birdsFlight',
|
|
482
|
-
'cabins',
|
|
483
|
-
'cakeSlice',
|
|
484
|
-
'candyCorn',
|
|
485
|
-
'celticKnotwork',
|
|
486
|
-
'certificateBanner',
|
|
487
|
-
'chainLink',
|
|
488
|
-
'champagneBottle',
|
|
489
|
-
'checkedBarBlack',
|
|
490
|
-
'checkedBarColor',
|
|
491
|
-
'checkered',
|
|
492
|
-
'christmasTree',
|
|
493
|
-
'circlesLines',
|
|
494
|
-
'circlesRectangles',
|
|
495
|
-
'classicalWave',
|
|
496
|
-
'clocks',
|
|
497
|
-
'compass',
|
|
498
|
-
'confetti',
|
|
499
|
-
'confettiGrays',
|
|
500
|
-
'confettiOutline',
|
|
501
|
-
'confettiStreamers',
|
|
502
|
-
'confettiWhite',
|
|
503
|
-
'cornerTriangles',
|
|
504
|
-
'couponCutoutDashes',
|
|
505
|
-
'couponCutoutDots',
|
|
506
|
-
'crazyMaze',
|
|
507
|
-
'creaturesButterfly',
|
|
508
|
-
'creaturesFish',
|
|
509
|
-
'creaturesInsects',
|
|
510
|
-
'creaturesLadyBug',
|
|
511
|
-
'crossStitch',
|
|
512
|
-
'cup',
|
|
513
|
-
'decoArch',
|
|
514
|
-
'decoArchColor',
|
|
515
|
-
'decoBlocks',
|
|
516
|
-
'diamondsGray',
|
|
517
|
-
'doubleD',
|
|
518
|
-
'doubleDiamonds',
|
|
519
|
-
'earth1',
|
|
520
|
-
'earth2',
|
|
521
|
-
'eclipsingSquares1',
|
|
522
|
-
'eclipsingSquares2',
|
|
523
|
-
'eggsBlack',
|
|
524
|
-
'fans',
|
|
525
|
-
'film',
|
|
526
|
-
'firecrackers',
|
|
527
|
-
'flowersBlockPrint',
|
|
528
|
-
'flowersDaisies',
|
|
529
|
-
'flowersModern1',
|
|
530
|
-
'flowersModern2',
|
|
531
|
-
'flowersPansy',
|
|
532
|
-
'flowersRedRose',
|
|
533
|
-
'flowersRoses',
|
|
534
|
-
'flowersTeacup',
|
|
535
|
-
'flowersTiny',
|
|
536
|
-
'gems',
|
|
537
|
-
'gingerbreadMan',
|
|
538
|
-
'gradient',
|
|
539
|
-
'handmade1',
|
|
540
|
-
'handmade2',
|
|
541
|
-
'heartBalloon',
|
|
542
|
-
'heartGray',
|
|
543
|
-
'hearts',
|
|
544
|
-
'heebieJeebies',
|
|
545
|
-
'holly',
|
|
546
|
-
'houseFunky',
|
|
547
|
-
'hypnotic',
|
|
548
|
-
'iceCreamCones',
|
|
549
|
-
'lightBulb',
|
|
550
|
-
'lightning1',
|
|
551
|
-
'lightning2',
|
|
552
|
-
'mapPins',
|
|
553
|
-
'mapleLeaf',
|
|
554
|
-
'mapleMuffins',
|
|
555
|
-
'marquee',
|
|
556
|
-
'marqueeToothed',
|
|
557
|
-
'moons',
|
|
558
|
-
'mosaic',
|
|
559
|
-
'musicNotes',
|
|
560
|
-
'northwest',
|
|
561
|
-
'ovals',
|
|
562
|
-
'packages',
|
|
563
|
-
'palmsBlack',
|
|
564
|
-
'palmsColor',
|
|
565
|
-
'paperClips',
|
|
566
|
-
'papyrus',
|
|
567
|
-
'partyFavor',
|
|
568
|
-
'partyGlass',
|
|
569
|
-
'pencils',
|
|
570
|
-
'people',
|
|
571
|
-
'peopleWaving',
|
|
572
|
-
'peopleHats',
|
|
573
|
-
'poinsettias',
|
|
574
|
-
'postageStamp',
|
|
575
|
-
'pumpkin1',
|
|
576
|
-
'pushPinNote2',
|
|
577
|
-
'pushPinNote1',
|
|
578
|
-
'pyramids',
|
|
579
|
-
'pyramidsAbove',
|
|
580
|
-
'quadrants',
|
|
581
|
-
'rings',
|
|
582
|
-
'safari',
|
|
583
|
-
'sawtooth',
|
|
584
|
-
'sawtoothGray',
|
|
585
|
-
'scaredCat',
|
|
586
|
-
'seattle',
|
|
587
|
-
'shadowedSquares',
|
|
588
|
-
'sharksTeeth',
|
|
589
|
-
'shorebirdTracks',
|
|
590
|
-
'skyrocket',
|
|
591
|
-
'snowflakeFancy',
|
|
592
|
-
'snowflakes',
|
|
593
|
-
'sombrero',
|
|
594
|
-
'southwest',
|
|
595
|
-
'stars',
|
|
596
|
-
'starsTop',
|
|
597
|
-
'stars3d',
|
|
598
|
-
'starsBlack',
|
|
599
|
-
'starsShadowed',
|
|
600
|
-
'sun',
|
|
601
|
-
'swirligig',
|
|
602
|
-
'tornPaper',
|
|
603
|
-
'tornPaperBlack',
|
|
604
|
-
'trees',
|
|
605
|
-
'triangleParty',
|
|
606
|
-
'triangles',
|
|
607
|
-
'tribal1',
|
|
608
|
-
'tribal2',
|
|
609
|
-
'tribal3',
|
|
610
|
-
'tribal4',
|
|
611
|
-
'tribal5',
|
|
612
|
-
'tribal6',
|
|
613
|
-
'triangle1',
|
|
614
|
-
'triangle2',
|
|
615
|
-
'triangleCircle1',
|
|
616
|
-
'triangleCircle2',
|
|
617
|
-
'shapes1',
|
|
618
|
-
'shapes2',
|
|
619
|
-
'twistedLines1',
|
|
620
|
-
'twistedLines2',
|
|
621
|
-
'vine',
|
|
622
|
-
'waveline',
|
|
623
|
-
'weavingAngles',
|
|
624
|
-
'weavingBraid',
|
|
625
|
-
'weavingRibbon',
|
|
626
|
-
'weavingStrips',
|
|
627
|
-
'whiteFlowers',
|
|
628
|
-
'woodwork',
|
|
629
|
-
'xIllusions',
|
|
630
|
-
'zanyTriangles',
|
|
631
|
-
'zigZag',
|
|
632
|
-
'zigZagStitch'
|
|
633
|
-
];
|
|
634
|
-
const BORDER_STYLE_SET = new Set(DOCUMENT_PARAGRAPH_BORDER_STYLES);
|
|
635
|
-
const BORDER_EDGE_SET = new Set(DOCUMENT_PARAGRAPH_BORDER_EDGES);
|
|
636
|
-
const BORDER_PROPERTY_SET = new Set([
|
|
637
|
-
'style',
|
|
638
|
-
'color',
|
|
639
|
-
'size',
|
|
640
|
-
'space',
|
|
641
|
-
'shadow',
|
|
642
|
-
'frame'
|
|
643
|
-
]);
|
|
644
|
-
const LINE_BORDER_STYLES = new Set(DOCUMENT_PARAGRAPH_BORDER_STYLES.slice(0, 27));
|
|
645
|
-
const DASHED_BORDER_STYLES = new Set([
|
|
646
|
-
'dashed',
|
|
647
|
-
'dashSmallGap',
|
|
648
|
-
'dashDotStroked',
|
|
649
|
-
'dotDash',
|
|
650
|
-
'dotDotDash'
|
|
651
|
-
]);
|
|
652
|
-
const DOUBLE_BORDER_STYLES = new Set([
|
|
653
|
-
'double',
|
|
654
|
-
'triple',
|
|
655
|
-
'thinThickSmallGap',
|
|
656
|
-
'thickThinSmallGap',
|
|
657
|
-
'thinThickThinSmallGap',
|
|
658
|
-
'thinThickMediumGap',
|
|
659
|
-
'thickThinMediumGap',
|
|
660
|
-
'thinThickThinMediumGap',
|
|
661
|
-
'thinThickLargeGap',
|
|
662
|
-
'thickThinLargeGap',
|
|
663
|
-
'thinThickThinLargeGap',
|
|
664
|
-
'doubleWave'
|
|
665
|
-
]);
|
|
666
|
-
const MAX_SERIALIZED_PARAGRAPH_BORDERS = 32768;
|
|
667
|
-
const POINTS_TO_PIXELS = 96 / 72;
|
|
668
|
-
function normalizeDocumentParagraphBorders(source) {
|
|
669
|
-
if (!source || 'object' != typeof source || Array.isArray(source)) return null;
|
|
670
|
-
const record = source;
|
|
671
|
-
if (Object.keys(record).some((key)=>!BORDER_EDGE_SET.has(key))) return null;
|
|
672
|
-
const borders = {};
|
|
673
|
-
for (const edge of DOCUMENT_PARAGRAPH_BORDER_EDGES){
|
|
674
|
-
if (void 0 === record[edge]) continue;
|
|
675
|
-
const border = normalizeDocumentParagraphBorder(record[edge]);
|
|
676
|
-
if (!border) return null;
|
|
677
|
-
borders[edge] = border;
|
|
439
|
+
function parseDocumentScriptFonts(source) {
|
|
440
|
+
if (!source || source.length > MAX_SERIALIZED_SCRIPT_FONTS_LENGTH) return null;
|
|
441
|
+
try {
|
|
442
|
+
return normalizeDocumentScriptFonts(JSON.parse(source));
|
|
443
|
+
} catch {
|
|
444
|
+
return null;
|
|
678
445
|
}
|
|
679
|
-
return Object.keys(borders).length ? borders : null;
|
|
680
446
|
}
|
|
681
|
-
function
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const shadow = optionalBoolean(record.shadow);
|
|
695
|
-
const frame = optionalBoolean(record.frame);
|
|
696
|
-
if (null === shadow || null === frame) return null;
|
|
447
|
+
function documentScriptFontsFromElement(element) {
|
|
448
|
+
return parseDocumentScriptFonts(element.getAttribute(DOCUMENT_SCRIPT_FONTS_ATTRIBUTE));
|
|
449
|
+
}
|
|
450
|
+
function documentScriptFontSlotFromElement(element) {
|
|
451
|
+
return normalizeDocumentScriptFontSlot(element.getAttribute(DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE));
|
|
452
|
+
}
|
|
453
|
+
function documentScriptFontsDomAttributes(source, slot) {
|
|
454
|
+
const fonts = normalizeDocumentScriptFonts(source);
|
|
455
|
+
const normalizedSlot = normalizeDocumentScriptFontSlot(slot);
|
|
456
|
+
if (!fonts) return {};
|
|
457
|
+
const serialized = serializeDocumentScriptFonts(fonts);
|
|
458
|
+
if (!serialized) return {};
|
|
459
|
+
const family = documentScriptFontFamily(fonts, normalizedSlot ?? documentScriptFontSlotFromHint(fonts.hint));
|
|
697
460
|
return {
|
|
698
|
-
|
|
699
|
-
...
|
|
700
|
-
|
|
701
|
-
} : {},
|
|
702
|
-
...void 0 !== size ? {
|
|
703
|
-
size
|
|
704
|
-
} : {},
|
|
705
|
-
...void 0 !== space ? {
|
|
706
|
-
space
|
|
707
|
-
} : {},
|
|
708
|
-
...void 0 !== shadow ? {
|
|
709
|
-
shadow
|
|
461
|
+
[DOCUMENT_SCRIPT_FONTS_ATTRIBUTE]: serialized,
|
|
462
|
+
...normalizedSlot ? {
|
|
463
|
+
[DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE]: normalizedSlot
|
|
710
464
|
} : {},
|
|
711
|
-
...
|
|
712
|
-
|
|
465
|
+
...family ? {
|
|
466
|
+
style: `font-family: ${family}`
|
|
713
467
|
} : {}
|
|
714
468
|
};
|
|
715
469
|
}
|
|
716
|
-
function
|
|
717
|
-
|
|
718
|
-
if (!
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
470
|
+
function documentScriptFontFamily(source, slot) {
|
|
471
|
+
const fonts = normalizeDocumentScriptFonts(source);
|
|
472
|
+
if (!fonts) return;
|
|
473
|
+
const families = [];
|
|
474
|
+
const seen = new Set();
|
|
475
|
+
for (const candidate of documentScriptFontFallbackSlots(slot)){
|
|
476
|
+
const family = documentScriptFontFaceFamily(fonts[candidate]);
|
|
477
|
+
const key = family?.toLocaleLowerCase();
|
|
478
|
+
if (!(!family || !key || seen.has(key))) {
|
|
479
|
+
seen.add(key);
|
|
480
|
+
families.push(cssFontFamily(family));
|
|
481
|
+
}
|
|
723
482
|
}
|
|
483
|
+
return families.length ? families.join(', ') : void 0;
|
|
724
484
|
}
|
|
725
|
-
function
|
|
726
|
-
|
|
727
|
-
if (!borders) return;
|
|
728
|
-
return JSON.stringify(Object.fromEntries(DOCUMENT_PARAGRAPH_BORDER_EDGES.flatMap((edge)=>{
|
|
729
|
-
const border = borders[edge];
|
|
730
|
-
return border ? [
|
|
731
|
-
[
|
|
732
|
-
edge,
|
|
733
|
-
serializedBorder(border)
|
|
734
|
-
]
|
|
735
|
-
] : [];
|
|
736
|
-
})));
|
|
485
|
+
function documentScriptFontFallbackSlots(slot) {
|
|
486
|
+
return SLOT_FALLBACK_ORDER[slot];
|
|
737
487
|
}
|
|
738
|
-
function
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
488
|
+
function documentScriptFontFamilyForRendering(source, slot, currentFontFamily) {
|
|
489
|
+
const projected = documentScriptFontFamily(source, slot);
|
|
490
|
+
const safeCurrent = safeCssFontFamilyList(currentFontFamily);
|
|
491
|
+
if (!safeCurrent || !projected) return projected;
|
|
492
|
+
const currentPrimary = documentFontNameFromCssFamily(safeCurrent);
|
|
493
|
+
const projectedPrimary = documentFontNameFromCssFamily(projected);
|
|
494
|
+
return currentPrimary && projectedPrimary && currentPrimary.toLocaleLowerCase() === projectedPrimary.toLocaleLowerCase() ? safeCurrent : projected;
|
|
495
|
+
}
|
|
496
|
+
function documentScriptFontDirectFamily(source, slot) {
|
|
497
|
+
const fonts = normalizeDocumentScriptFonts(source);
|
|
498
|
+
return fonts ? documentScriptFontFaceFamily(fonts[slot]) ?? null : null;
|
|
499
|
+
}
|
|
500
|
+
function documentScriptFontsForAllText(fontFamily) {
|
|
501
|
+
const name = documentFontNameFromCssFamily(fontFamily);
|
|
502
|
+
if (!name) return null;
|
|
503
|
+
const face = {
|
|
504
|
+
name,
|
|
505
|
+
resolved: name
|
|
743
506
|
};
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
value: css.color
|
|
766
|
-
},
|
|
767
|
-
size: cssWidthToEighthPoints(css.width)
|
|
768
|
-
};
|
|
507
|
+
return {
|
|
508
|
+
ascii: face,
|
|
509
|
+
highAnsi: face,
|
|
510
|
+
eastAsia: face,
|
|
511
|
+
complexScript: face,
|
|
512
|
+
hint: 'default'
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function patchDocumentScriptFonts(source, patch, fallbackFontFamily) {
|
|
516
|
+
const current = normalizeDocumentScriptFonts(source) ?? documentScriptFontsForAllText(fallbackFontFamily) ?? {};
|
|
517
|
+
const next = {
|
|
518
|
+
...current
|
|
519
|
+
};
|
|
520
|
+
if (void 0 !== patch.latin) {
|
|
521
|
+
const face = directFontFace(patch.latin);
|
|
522
|
+
if (face) {
|
|
523
|
+
next.ascii = face;
|
|
524
|
+
next.highAnsi = face;
|
|
525
|
+
} else {
|
|
526
|
+
delete next.ascii;
|
|
527
|
+
delete next.highAnsi;
|
|
769
528
|
}
|
|
770
529
|
}
|
|
771
|
-
|
|
530
|
+
if (void 0 !== patch.eastAsia) {
|
|
531
|
+
const face = directFontFace(patch.eastAsia);
|
|
532
|
+
if (face) next.eastAsia = face;
|
|
533
|
+
else delete next.eastAsia;
|
|
534
|
+
}
|
|
535
|
+
if (void 0 !== patch.complexScript) {
|
|
536
|
+
const face = directFontFace(patch.complexScript);
|
|
537
|
+
if (face) next.complexScript = face;
|
|
538
|
+
else delete next.complexScript;
|
|
539
|
+
}
|
|
540
|
+
return normalizeDocumentScriptFonts(next);
|
|
772
541
|
}
|
|
773
|
-
function
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
const
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
542
|
+
function documentScriptFontSegments(text, hint = 'default', forceComplexScript = false) {
|
|
543
|
+
if (!text) return [];
|
|
544
|
+
if (forceComplexScript) return [
|
|
545
|
+
{
|
|
546
|
+
from: 0,
|
|
547
|
+
to: text.length,
|
|
548
|
+
slot: 'complexScript'
|
|
549
|
+
}
|
|
550
|
+
];
|
|
551
|
+
const characters = [];
|
|
552
|
+
let offset = 0;
|
|
553
|
+
for (const character of text){
|
|
554
|
+
const from = offset;
|
|
555
|
+
offset += character.length;
|
|
556
|
+
characters.push({
|
|
557
|
+
from,
|
|
558
|
+
to: offset,
|
|
559
|
+
slot: strongDocumentScriptFontSlot(character)
|
|
560
|
+
});
|
|
791
561
|
}
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
562
|
+
const fallback = documentScriptFontSlotFromHint(hint);
|
|
563
|
+
let previous = null;
|
|
564
|
+
for(let index = 0; index < characters.length; index += 1){
|
|
565
|
+
const entry = characters[index];
|
|
566
|
+
if (!entry) continue;
|
|
567
|
+
if (entry.slot) {
|
|
568
|
+
previous = entry.slot;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
let next = previous;
|
|
572
|
+
if (!next) for(let cursor = index + 1; cursor < characters.length; cursor += 1){
|
|
573
|
+
const candidate = characters[cursor]?.slot;
|
|
574
|
+
if (candidate) {
|
|
575
|
+
next = candidate;
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
entry.slot = next ?? fallback;
|
|
580
|
+
}
|
|
581
|
+
const segments = [];
|
|
582
|
+
for (const entry of characters){
|
|
583
|
+
const slot = entry.slot ?? fallback;
|
|
584
|
+
const prior = segments[segments.length - 1];
|
|
585
|
+
if (prior?.slot === slot && prior.to === entry.from) prior.to = entry.to;
|
|
586
|
+
else segments.push({
|
|
587
|
+
from: entry.from,
|
|
588
|
+
to: entry.to,
|
|
589
|
+
slot
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
return segments;
|
|
803
593
|
}
|
|
804
|
-
function
|
|
805
|
-
return
|
|
594
|
+
function normalizeDocumentScriptFontSlot(value) {
|
|
595
|
+
return scriptFontSlots.includes(value) ? value : null;
|
|
806
596
|
}
|
|
807
|
-
function
|
|
808
|
-
|
|
809
|
-
const record = source;
|
|
810
|
-
if (Object.keys(record).some((key)=>'value' !== key && 'theme' !== key)) return null;
|
|
811
|
-
const theme = parseDocxThemeReference('string' == typeof record.theme ? record.theme : record.theme ? JSON.stringify(record.theme) : void 0);
|
|
812
|
-
const direct = 'auto' === record.value ? 'auto' : 'string' == typeof record.value ? normalizeCssColor(record.value) : null;
|
|
813
|
-
const resolved = direct ?? theme?.resolved ?? null;
|
|
814
|
-
if (!resolved || 'transparent' === resolved) return null;
|
|
815
|
-
if (theme && resolved !== theme.resolved && !('auto' === resolved && 'none' === theme.theme && '#000000' === theme.resolved)) return null;
|
|
816
|
-
return {
|
|
817
|
-
value: resolved,
|
|
818
|
-
...theme ? {
|
|
819
|
-
theme
|
|
820
|
-
} : {}
|
|
821
|
-
};
|
|
597
|
+
function normalizeDocumentScriptFontHint(value) {
|
|
598
|
+
return SCRIPT_FONT_HINTS.has(value) ? value : null;
|
|
822
599
|
}
|
|
823
|
-
function
|
|
824
|
-
|
|
825
|
-
return {
|
|
826
|
-
style: border.style,
|
|
827
|
-
...color ? {
|
|
828
|
-
color
|
|
829
|
-
} : {},
|
|
830
|
-
...void 0 !== border.size ? {
|
|
831
|
-
size: border.size
|
|
832
|
-
} : {},
|
|
833
|
-
...void 0 !== border.space ? {
|
|
834
|
-
space: border.space
|
|
835
|
-
} : {},
|
|
836
|
-
...void 0 !== border.shadow ? {
|
|
837
|
-
shadow: border.shadow
|
|
838
|
-
} : {},
|
|
839
|
-
...void 0 !== border.frame ? {
|
|
840
|
-
frame: border.frame
|
|
841
|
-
} : {}
|
|
842
|
-
};
|
|
600
|
+
function normalizeDocumentThemeFont(value) {
|
|
601
|
+
return THEME_FONTS.has(value) ? value : null;
|
|
843
602
|
}
|
|
844
|
-
function
|
|
845
|
-
|
|
603
|
+
function documentScriptFontSlotFromHint(hint) {
|
|
604
|
+
if ('eastAsia' === hint) return 'eastAsia';
|
|
605
|
+
if ('cs' === hint) return 'complexScript';
|
|
606
|
+
return 'ascii';
|
|
607
|
+
}
|
|
608
|
+
function documentFontNameFromCssFamily(value) {
|
|
609
|
+
if ('string' != typeof value) return null;
|
|
610
|
+
const source = value.trim();
|
|
611
|
+
if (!source) return null;
|
|
612
|
+
let family = '';
|
|
613
|
+
const quote = source[0];
|
|
614
|
+
if ('"' === quote || "'" === quote) {
|
|
615
|
+
let closed = false;
|
|
616
|
+
for(let index = 1; index < source.length; index += 1){
|
|
617
|
+
const character = source[index];
|
|
618
|
+
if (character === quote) {
|
|
619
|
+
closed = true;
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
if ('\\' !== character) {
|
|
623
|
+
family += character;
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
const decoded = decodeCssEscape(source, index + 1);
|
|
627
|
+
if (!decoded) return null;
|
|
628
|
+
family += decoded.value;
|
|
629
|
+
index = decoded.end - 1;
|
|
630
|
+
}
|
|
631
|
+
if (!closed) return null;
|
|
632
|
+
} else family = source.split(',')[0] ?? '';
|
|
633
|
+
return normalizeDocumentFontName(family);
|
|
634
|
+
}
|
|
635
|
+
function cssDocumentFontFamily(value) {
|
|
636
|
+
const family = normalizeDocumentFontName(value);
|
|
637
|
+
return family ? cssFontFamily(family) : null;
|
|
638
|
+
}
|
|
639
|
+
function normalizeDocumentFontName(value) {
|
|
640
|
+
if ('string' != typeof value) return null;
|
|
641
|
+
const normalized = value.trim();
|
|
642
|
+
return normalized && normalized.length <= MAX_FONT_NAME_LENGTH && !/[\p{Cc}\p{Cs}]/u.test(normalized) ? normalized : null;
|
|
643
|
+
}
|
|
644
|
+
const scriptFontSlots = [
|
|
645
|
+
'ascii',
|
|
646
|
+
'highAnsi',
|
|
647
|
+
'eastAsia',
|
|
648
|
+
'complexScript'
|
|
649
|
+
];
|
|
650
|
+
function normalizeDocumentScriptFontFace(source) {
|
|
651
|
+
if (!isRecordWithKeys(source, SCRIPT_FONT_FACE_KEYS)) return null;
|
|
652
|
+
const name = void 0 === source.name ? void 0 : normalizeDocumentFontName(source.name);
|
|
653
|
+
const resolved = void 0 === source.resolved ? void 0 : normalizeDocumentFontName(source.resolved);
|
|
654
|
+
const theme = void 0 === source.theme ? void 0 : normalizeDocumentThemeFont(source.theme);
|
|
655
|
+
if (void 0 !== source.name && !name || void 0 !== source.resolved && !resolved || null === theme || !name && !theme && !resolved) return null;
|
|
846
656
|
return {
|
|
847
|
-
|
|
657
|
+
...name ? {
|
|
658
|
+
name
|
|
659
|
+
} : {},
|
|
848
660
|
...theme ? {
|
|
849
|
-
theme
|
|
661
|
+
theme
|
|
662
|
+
} : {},
|
|
663
|
+
...resolved ? {
|
|
664
|
+
resolved
|
|
850
665
|
} : {}
|
|
851
666
|
};
|
|
852
667
|
}
|
|
853
|
-
function
|
|
854
|
-
if (
|
|
855
|
-
|
|
668
|
+
function directFontFace(value) {
|
|
669
|
+
if (null === value) return null;
|
|
670
|
+
const name = documentFontNameFromCssFamily(value) ?? normalizeDocumentFontName(value);
|
|
671
|
+
return name ? {
|
|
672
|
+
name,
|
|
673
|
+
resolved: name
|
|
674
|
+
} : null;
|
|
856
675
|
}
|
|
857
|
-
function
|
|
858
|
-
|
|
859
|
-
return 'boolean' == typeof value ? value : null;
|
|
676
|
+
function documentScriptFontFaceFamily(face) {
|
|
677
|
+
return face?.resolved ?? face?.name;
|
|
860
678
|
}
|
|
861
|
-
function
|
|
862
|
-
if (
|
|
863
|
-
|
|
679
|
+
function strongDocumentScriptFontSlot(character) {
|
|
680
|
+
if (NEUTRAL_SCRIPT_CHARACTER.test(character)) return null;
|
|
681
|
+
const codePoint = character.codePointAt(0);
|
|
682
|
+
if (void 0 === codePoint) return null;
|
|
683
|
+
if (isComplexScriptCodePoint(codePoint)) return 'complexScript';
|
|
684
|
+
if (isEastAsianCodePoint(codePoint)) return 'eastAsia';
|
|
685
|
+
return codePoint <= 0x7f ? 'ascii' : 'highAnsi';
|
|
864
686
|
}
|
|
865
|
-
function
|
|
866
|
-
|
|
867
|
-
color: 'transparent',
|
|
868
|
-
style: 'none',
|
|
869
|
-
width: 0
|
|
870
|
-
};
|
|
871
|
-
const width = Math.min(16, isDocumentParagraphArtBorderStyle(border.style) ? border.size * POINTS_TO_PIXELS : border.size / 6);
|
|
872
|
-
return {
|
|
873
|
-
color: border.color && 'auto' !== border.color.value ? border.color.value : '#000000',
|
|
874
|
-
style: borderStyleToCssStyle(border.style),
|
|
875
|
-
width
|
|
876
|
-
};
|
|
687
|
+
function isComplexScriptCodePoint(codePoint) {
|
|
688
|
+
return codePoint >= 0x0590 && codePoint <= 0x08ff || codePoint >= 0x0900 && codePoint <= 0x109f || codePoint >= 0x1780 && codePoint <= 0x18af || codePoint >= 0x1900 && codePoint <= 0x1cff || codePoint >= 0xa800 && codePoint <= 0xa8ff || codePoint >= 0xa980 && codePoint <= 0xa9df || codePoint >= 0xaa00 && codePoint <= 0xaa7f || codePoint >= 0xabc0 && codePoint <= 0xabff || codePoint >= 0xfb1d && codePoint <= 0xfdff || codePoint >= 0xfe70 && codePoint <= 0xfeff || codePoint >= 0x10a00 && codePoint <= 0x10fff || codePoint >= 0x11000 && codePoint <= 0x11fff || codePoint >= 0x1e900 && codePoint <= 0x1edff || codePoint >= 0x1ee00 && codePoint <= 0x1eeff;
|
|
877
689
|
}
|
|
878
|
-
function
|
|
879
|
-
|
|
880
|
-
if ('dotted' === style) return 'dotted';
|
|
881
|
-
if (DASHED_BORDER_STYLES.has(style)) return 'dashed';
|
|
882
|
-
if (DOUBLE_BORDER_STYLES.has(style)) return 'double';
|
|
883
|
-
if ('inset' === style || 'threeDEngrave' === style) return 'inset';
|
|
884
|
-
if ('outset' === style || 'threeDEmboss' === style) return 'outset';
|
|
885
|
-
return 'solid';
|
|
690
|
+
function isEastAsianCodePoint(codePoint) {
|
|
691
|
+
return codePoint >= 0x1100 && codePoint <= 0x11ff || codePoint >= 0x2e80 && codePoint <= 0xa4cf || codePoint >= 0xac00 && codePoint <= 0xd7af || codePoint >= 0xf900 && codePoint <= 0xfaff || codePoint >= 0xfe10 && codePoint <= 0xfe6f || codePoint >= 0xff00 && codePoint <= 0xffef || codePoint >= 0x20000 && codePoint <= 0x323af;
|
|
886
692
|
}
|
|
887
|
-
function
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
693
|
+
function cssFontFamily(value) {
|
|
694
|
+
return /^(?:-?[\p{L}_])[\p{L}\p{N}_-]*$/u.test(value) ? value : `"${Array.from(value, cssStringCharacter).join('')}"`;
|
|
695
|
+
}
|
|
696
|
+
function cssStringCharacter(character) {
|
|
697
|
+
return /[\\":;{}<>]/u.test(character) ? `\\${character.codePointAt(0)?.toString(16)} ` : character;
|
|
698
|
+
}
|
|
699
|
+
function safeCssFontFamilyList(value) {
|
|
700
|
+
if ('string' != typeof value) return null;
|
|
701
|
+
const source = value.trim();
|
|
702
|
+
if (!source || source.length > 1024 || /[;{}]/u.test(source)) return null;
|
|
703
|
+
const tokens = [];
|
|
704
|
+
let start = 0;
|
|
705
|
+
let quote = '';
|
|
706
|
+
for(let index = 0; index < source.length; index += 1){
|
|
707
|
+
const character = source[index] ?? '';
|
|
708
|
+
if (quote) {
|
|
709
|
+
if ('\\' === character) {
|
|
710
|
+
index += 1;
|
|
711
|
+
if (index >= source.length) return null;
|
|
712
|
+
} else if (character === quote) quote = '';
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if ('"' === character || "'" === character) {
|
|
716
|
+
quote = character;
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
if (',' === character) {
|
|
720
|
+
tokens.push(source.slice(start, index).trim());
|
|
721
|
+
start = index + 1;
|
|
722
|
+
}
|
|
903
723
|
}
|
|
904
|
-
|
|
724
|
+
if (quote) return null;
|
|
725
|
+
tokens.push(source.slice(start).trim());
|
|
726
|
+
return tokens.length && tokens.every(validCssFontFamilyToken) ? source : null;
|
|
905
727
|
}
|
|
906
|
-
function
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
if ('none' === style || 'hidden' === style) return {
|
|
912
|
-
color: '#000000',
|
|
913
|
-
style: 'none',
|
|
914
|
-
width: 0
|
|
915
|
-
};
|
|
916
|
-
return color && 'transparent' !== color && Number.isFinite(width) && width > 0 ? {
|
|
917
|
-
color,
|
|
918
|
-
style,
|
|
919
|
-
width
|
|
920
|
-
} : null;
|
|
728
|
+
function validCssFontFamilyToken(value) {
|
|
729
|
+
if (!value) return false;
|
|
730
|
+
if (value.startsWith('"')) return /^"(?:[^"\\\r\n\f]|\\(?:[\da-f]{1,6}\s?|[^\r\n\f]))*"$/iu.test(value) && null !== documentFontNameFromCssFamily(value);
|
|
731
|
+
if (value.startsWith("'")) return /^'(?:[^'\\\r\n\f]|\\(?:[\da-f]{1,6}\s?|[^\r\n\f]))*'$/iu.test(value) && null !== documentFontNameFromCssFamily(value);
|
|
732
|
+
return /[\p{L}_]/u.test(value) && /^[\p{L}_-][\p{L}\p{N}_ -]*$/u.test(value) && !/^(?:inherit|initial|revert(?:-layer)?|unset)$/iu.test(value);
|
|
921
733
|
}
|
|
922
|
-
function
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
734
|
+
function decodeCssEscape(source, start) {
|
|
735
|
+
if (start >= source.length) return null;
|
|
736
|
+
const hexadecimal = /^[\da-f]{1,6}/i.exec(source.slice(start))?.[0];
|
|
737
|
+
if (hexadecimal) {
|
|
738
|
+
const codePoint = Number.parseInt(hexadecimal, 16);
|
|
739
|
+
if (0 === codePoint || codePoint > 0x10ffff || codePoint >= 0xd800 && codePoint <= 0xdfff) return null;
|
|
740
|
+
let end = start + hexadecimal.length;
|
|
741
|
+
if (/\s/u.test(source[end] ?? '')) end += 1;
|
|
742
|
+
return {
|
|
743
|
+
value: String.fromCodePoint(codePoint),
|
|
744
|
+
end
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
const value = source[start];
|
|
748
|
+
return !value || /[\r\n\f]/u.test(value) ? null : {
|
|
749
|
+
value,
|
|
750
|
+
end: start + 1
|
|
751
|
+
};
|
|
926
752
|
}
|
|
927
|
-
function
|
|
928
|
-
|
|
929
|
-
if ('dashed' === style) return 'dashed';
|
|
930
|
-
if ('dotted' === style) return 'dotted';
|
|
931
|
-
if ('inset' === style || 'groove' === style) return 'inset';
|
|
932
|
-
if ('outset' === style || 'ridge' === style) return 'outset';
|
|
933
|
-
return 'single';
|
|
753
|
+
function isRecordWithKeys(value, allowed) {
|
|
754
|
+
return 'object' == typeof value && null !== value && !Array.isArray(value) && Object.keys(value).every((key)=>allowed.has(key));
|
|
934
755
|
}
|
|
935
|
-
|
|
936
|
-
|
|
756
|
+
const DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE = 'data-office-proofing-languages';
|
|
757
|
+
const DOCUMENT_NO_PROOF_ATTRIBUTE = 'data-office-no-proof';
|
|
758
|
+
const PROOFING_LANGUAGE_KEYS = new Set([
|
|
759
|
+
'latin',
|
|
760
|
+
'eastAsia',
|
|
761
|
+
'bidi'
|
|
762
|
+
]);
|
|
763
|
+
const PROOFING_LANGUAGE_ORDER = [
|
|
764
|
+
'latin',
|
|
765
|
+
'eastAsia',
|
|
766
|
+
'bidi'
|
|
767
|
+
];
|
|
768
|
+
const MAX_LANGUAGE_TAG_LENGTH = 85;
|
|
769
|
+
const MAX_SERIALIZED_PROOFING_LANGUAGES_BYTES = 384;
|
|
770
|
+
function normalizeDocumentLanguageTag(source) {
|
|
771
|
+
if ('string' != typeof source) return null;
|
|
772
|
+
if (!source || source !== source.trim() || source.length > MAX_LANGUAGE_TAG_LENGTH || /[\p{Cc}\p{Cs}]/u.test(source)) return null;
|
|
773
|
+
return /^(?:x-none|[a-z0-9]{1,8}(?:-[a-z0-9]{1,8})*)$/iu.test(source) ? source : null;
|
|
937
774
|
}
|
|
938
|
-
function
|
|
939
|
-
|
|
775
|
+
function normalizeDocumentProofingLanguages(source) {
|
|
776
|
+
if (!isRecord(source)) return null;
|
|
777
|
+
const keys = Object.keys(source);
|
|
778
|
+
if (!keys.length || keys.some((key)=>!PROOFING_LANGUAGE_KEYS.has(key))) return null;
|
|
779
|
+
const normalized = {};
|
|
780
|
+
for (const key of PROOFING_LANGUAGE_ORDER){
|
|
781
|
+
if (void 0 === source[key]) continue;
|
|
782
|
+
const language = normalizeDocumentLanguageTag(source[key]);
|
|
783
|
+
if (!language) return null;
|
|
784
|
+
normalized[key] = language;
|
|
785
|
+
}
|
|
786
|
+
return Object.keys(normalized).length ? normalized : null;
|
|
940
787
|
}
|
|
941
|
-
|
|
942
|
-
const
|
|
943
|
-
|
|
944
|
-
const
|
|
945
|
-
|
|
946
|
-
const border = normalizeDocumentParagraphBorder(source);
|
|
947
|
-
return border && !isDocumentParagraphArtBorderStyle(border.style) ? border : null;
|
|
788
|
+
function serializeDocumentProofingLanguages(source) {
|
|
789
|
+
const normalized = normalizeDocumentProofingLanguages(source);
|
|
790
|
+
if (!normalized) return;
|
|
791
|
+
const serialized = JSON.stringify(normalized);
|
|
792
|
+
return serialized.length <= MAX_SERIALIZED_PROOFING_LANGUAGES_BYTES ? serialized : void 0;
|
|
948
793
|
}
|
|
949
|
-
function
|
|
950
|
-
if ('string' != typeof source) return
|
|
951
|
-
if (!source
|
|
794
|
+
function parseDocumentProofingLanguages(source) {
|
|
795
|
+
if ('string' != typeof source) return normalizeDocumentProofingLanguages(source);
|
|
796
|
+
if (!source || source.length > MAX_SERIALIZED_PROOFING_LANGUAGES_BYTES) return null;
|
|
952
797
|
try {
|
|
953
|
-
|
|
798
|
+
const normalized = normalizeDocumentProofingLanguages(JSON.parse(source));
|
|
799
|
+
return normalized && JSON.stringify(normalized) === source ? normalized : null;
|
|
954
800
|
} catch {
|
|
955
801
|
return null;
|
|
956
802
|
}
|
|
957
803
|
}
|
|
958
|
-
function
|
|
959
|
-
|
|
960
|
-
if (
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
804
|
+
function normalizeDocumentNoProof(source) {
|
|
805
|
+
if (true === source || 'true' === source || '1' === source) return true;
|
|
806
|
+
if (false === source || 'false' === source || '0' === source) return false;
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
function documentProofingLanguagesFromElement(element) {
|
|
810
|
+
return parseDocumentProofingLanguages(element.getAttribute(DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE));
|
|
811
|
+
}
|
|
812
|
+
function documentNoProofFromElement(element) {
|
|
813
|
+
return normalizeDocumentNoProof(element.getAttribute(DOCUMENT_NO_PROOF_ATTRIBUTE));
|
|
814
|
+
}
|
|
815
|
+
function documentProofingLanguageForScript(source, slot) {
|
|
816
|
+
const languages = normalizeDocumentProofingLanguages(source);
|
|
817
|
+
if (!languages) return;
|
|
818
|
+
if ('eastAsia' === slot) return languages.eastAsia ?? languages.latin;
|
|
819
|
+
if ('complexScript' === slot) return languages.bidi ?? languages.latin;
|
|
820
|
+
return languages.latin ?? languages.eastAsia ?? languages.bidi;
|
|
821
|
+
}
|
|
822
|
+
function documentProofingDomAttributes(languagesSource, noProofSource, slot) {
|
|
823
|
+
const languages = normalizeDocumentProofingLanguages(languagesSource);
|
|
824
|
+
const serialized = serializeDocumentProofingLanguages(languages);
|
|
825
|
+
const noProof = normalizeDocumentNoProof(noProofSource);
|
|
826
|
+
const language = documentProofingLanguageForScript(languages, slot);
|
|
827
|
+
return {
|
|
828
|
+
...serialized ? {
|
|
829
|
+
[DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE]: serialized
|
|
974
830
|
} : {},
|
|
975
|
-
...
|
|
976
|
-
|
|
831
|
+
...null === noProof ? {} : {
|
|
832
|
+
[DOCUMENT_NO_PROOF_ATTRIBUTE]: String(noProof)
|
|
833
|
+
},
|
|
834
|
+
...slot ? {
|
|
835
|
+
[DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE]: slot
|
|
977
836
|
} : {},
|
|
978
|
-
...
|
|
979
|
-
|
|
837
|
+
...language && 'x-none' !== language ? {
|
|
838
|
+
lang: language
|
|
980
839
|
} : {},
|
|
981
|
-
...
|
|
982
|
-
|
|
840
|
+
...true === noProof ? {
|
|
841
|
+
spellcheck: 'false'
|
|
983
842
|
} : {}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
function parseDocumentRunBorderElement(element) {
|
|
987
|
-
const semantic = parseDocumentRunBorder(element.getAttribute(DOCUMENT_RUN_BORDER_ATTRIBUTE));
|
|
988
|
-
return semantic ?? documentRunBorderFromCss(element);
|
|
843
|
+
};
|
|
989
844
|
}
|
|
990
|
-
function
|
|
991
|
-
const
|
|
992
|
-
const
|
|
993
|
-
|
|
994
|
-
const presentation = documentBorderPresentation(border);
|
|
995
|
-
const declarations = [
|
|
996
|
-
`border: ${work_document_run_border_formatPixels(presentation.width)}px ${presentation.style} ${presentation.color}`,
|
|
997
|
-
`padding: ${work_document_run_border_formatPixels((border.space ?? 0) * work_document_run_border_POINTS_TO_PIXELS)}px`,
|
|
998
|
-
'box-decoration-break: clone',
|
|
999
|
-
'-webkit-box-decoration-break: clone'
|
|
1000
|
-
];
|
|
1001
|
-
if (border.shadow && presentation.width > 0) declarations.push(`box-shadow: 2px 2px 0 ${presentation.color}`);
|
|
1002
|
-
return {
|
|
1003
|
-
[DOCUMENT_RUN_BORDER_ATTRIBUTE]: serialized,
|
|
1004
|
-
style: declarations.join('; ')
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
function documentRunBorderIsVisible(source) {
|
|
1008
|
-
const border = normalizeDocumentRunBorder(source);
|
|
1009
|
-
return Boolean(border && documentBorderPresentation(border).width > 0);
|
|
1010
|
-
}
|
|
1011
|
-
function documentRunBorderFromCss(element) {
|
|
1012
|
-
const style = element.style.borderStyle.trim();
|
|
1013
|
-
if (!style) return null;
|
|
1014
|
-
if ('none' === style || 'hidden' === style) return {
|
|
1015
|
-
style: 'none'
|
|
845
|
+
function patchDocumentProofingLanguages(source, patch) {
|
|
846
|
+
const current = normalizeDocumentProofingLanguages(source) ?? {};
|
|
847
|
+
const next = {
|
|
848
|
+
...current
|
|
1016
849
|
};
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
} : {}
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
|
-
function cssBorderStyle(style) {
|
|
1033
|
-
if ('double' === style) return 'double';
|
|
1034
|
-
if ('dashed' === style) return 'dashed';
|
|
1035
|
-
if ('dotted' === style) return 'dotted';
|
|
1036
|
-
if ('inset' === style || 'groove' === style) return 'inset';
|
|
1037
|
-
if ('outset' === style || 'ridge' === style) return 'outset';
|
|
1038
|
-
return 'single';
|
|
850
|
+
for (const slot of PROOFING_LANGUAGE_ORDER){
|
|
851
|
+
const value = patch[slot];
|
|
852
|
+
if (void 0 === value) continue;
|
|
853
|
+
if (null === value) {
|
|
854
|
+
delete next[slot];
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
const language = normalizeDocumentLanguageTag(value);
|
|
858
|
+
if (!language) return null;
|
|
859
|
+
next[slot] = language;
|
|
860
|
+
}
|
|
861
|
+
return Object.keys(next).length ? next : null;
|
|
1039
862
|
}
|
|
1040
|
-
function
|
|
1041
|
-
return
|
|
863
|
+
function isRecord(source) {
|
|
864
|
+
return 'object' == typeof source && null !== source && !Array.isArray(source);
|
|
1042
865
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
'
|
|
1047
|
-
|
|
1048
|
-
'
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
'
|
|
1055
|
-
|
|
1056
|
-
'
|
|
1057
|
-
'thinHorzCross',
|
|
1058
|
-
'thinDiagCross',
|
|
1059
|
-
'pct5',
|
|
1060
|
-
'pct10',
|
|
1061
|
-
'pct12',
|
|
1062
|
-
'pct15',
|
|
1063
|
-
'pct20',
|
|
1064
|
-
'pct25',
|
|
1065
|
-
'pct30',
|
|
1066
|
-
'pct35',
|
|
1067
|
-
'pct37',
|
|
1068
|
-
'pct40',
|
|
1069
|
-
'pct45',
|
|
1070
|
-
'pct50',
|
|
1071
|
-
'pct55',
|
|
1072
|
-
'pct60',
|
|
1073
|
-
'pct62',
|
|
1074
|
-
'pct65',
|
|
1075
|
-
'pct70',
|
|
1076
|
-
'pct75',
|
|
1077
|
-
'pct80',
|
|
1078
|
-
'pct85',
|
|
1079
|
-
'pct87',
|
|
1080
|
-
'pct90',
|
|
1081
|
-
'pct95'
|
|
1082
|
-
]);
|
|
1083
|
-
function normalizeDocumentParagraphShading(source) {
|
|
1084
|
-
if (!source || 'object' != typeof source) return null;
|
|
1085
|
-
const value = source;
|
|
1086
|
-
const pattern = value.pattern;
|
|
1087
|
-
if ('string' != typeof pattern || !work_document_paragraph_shading_DOCUMENT_PARAGRAPH_SHADING_PATTERNS.has(pattern)) return null;
|
|
1088
|
-
const color = normalizeShadingColor(value.color);
|
|
1089
|
-
const fill = normalizeShadingColor(value.fill);
|
|
1090
|
-
if (void 0 !== value.color && !color || void 0 !== value.fill && !fill) return null;
|
|
1091
|
-
return {
|
|
1092
|
-
pattern: pattern,
|
|
1093
|
-
...color ? {
|
|
1094
|
-
color
|
|
1095
|
-
} : {},
|
|
1096
|
-
...fill ? {
|
|
1097
|
-
fill
|
|
1098
|
-
} : {}
|
|
1099
|
-
};
|
|
866
|
+
function normalizeCssColor(source) {
|
|
867
|
+
const value = source?.trim().toLowerCase();
|
|
868
|
+
if (!value) return null;
|
|
869
|
+
if ('transparent' === value) return 'transparent';
|
|
870
|
+
const shortHex = /^#([0-9a-f]{3})$/i.exec(value);
|
|
871
|
+
if (shortHex?.[1]) return `#${Array.from(shortHex[1]).map((channel)=>`${channel}${channel}`).join('')}`;
|
|
872
|
+
if (/^#[0-9a-f]{6}$/i.test(value)) return value;
|
|
873
|
+
const rgb = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*([\d.]+))?\s*\)$/i.exec(value);
|
|
874
|
+
if (!rgb) return null;
|
|
875
|
+
const channels = rgb.slice(1, 4).map(Number);
|
|
876
|
+
if (channels.some((channel)=>channel < 0 || channel > 255)) return null;
|
|
877
|
+
if (void 0 !== rgb[4] && 0 === Number(rgb[4])) return 'transparent';
|
|
878
|
+
if (void 0 !== rgb[4] && 1 !== Number(rgb[4])) return null;
|
|
879
|
+
return `#${channels.map((channel)=>channel.toString(16).padStart(2, '0')).join('')}`;
|
|
1100
880
|
}
|
|
1101
|
-
function
|
|
1102
|
-
|
|
1103
|
-
|
|
881
|
+
function decodeXmlBytes(bytes, label) {
|
|
882
|
+
let encoding = 'utf-8';
|
|
883
|
+
let offset = 0;
|
|
884
|
+
if (0xef === bytes[0] && 0xbb === bytes[1] && 0xbf === bytes[2]) offset = 3;
|
|
885
|
+
else if (0xff === bytes[0] && 0xfe === bytes[1]) {
|
|
886
|
+
encoding = 'utf-16le';
|
|
887
|
+
offset = 2;
|
|
888
|
+
} else if (0xfe === bytes[0] && 0xff === bytes[1]) {
|
|
889
|
+
encoding = 'utf-16be';
|
|
890
|
+
offset = 2;
|
|
891
|
+
} else if (0x3c === bytes[0] && 0 === bytes[1] && 0x3f === bytes[2] && 0 === bytes[3]) encoding = 'utf-16le';
|
|
892
|
+
else if (0 === bytes[0] && 0x3c === bytes[1] && 0 === bytes[2] && 0x3f === bytes[3]) encoding = 'utf-16be';
|
|
1104
893
|
try {
|
|
1105
|
-
return
|
|
894
|
+
return new TextDecoder(encoding, {
|
|
895
|
+
fatal: true
|
|
896
|
+
}).decode(bytes.subarray(offset));
|
|
1106
897
|
} catch {
|
|
1107
|
-
|
|
898
|
+
throw new Error(`${label} uses an invalid ${encoding} XML encoding.`);
|
|
1108
899
|
}
|
|
1109
900
|
}
|
|
1110
|
-
function
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1113
|
-
return
|
|
1114
|
-
pattern: shading.pattern,
|
|
1115
|
-
...shading.color ? {
|
|
1116
|
-
color: serializedShadingColor(shading.color)
|
|
1117
|
-
} : {},
|
|
1118
|
-
...shading.fill ? {
|
|
1119
|
-
fill: serializedShadingColor(shading.fill)
|
|
1120
|
-
} : {}
|
|
1121
|
-
});
|
|
901
|
+
function serializeUtf8Xml(document) {
|
|
902
|
+
const serialized = new XMLSerializer().serializeToString(document);
|
|
903
|
+
const body = serialized.replace(/^\s*<\?xml[^?]*\?>\s*/i, '');
|
|
904
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>${body}`;
|
|
1122
905
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
const expected = normalizeCssColor(presentation.backgroundColor);
|
|
1129
|
-
if (background && expected && background !== expected) {
|
|
1130
|
-
if ('transparent' === background) return {
|
|
1131
|
-
pattern: 'nil'
|
|
1132
|
-
};
|
|
1133
|
-
if (paragraphShadingBackgroundUsesForeground(semantic)) return {
|
|
1134
|
-
...semantic,
|
|
1135
|
-
color: {
|
|
1136
|
-
value: background
|
|
1137
|
-
}
|
|
1138
|
-
};
|
|
1139
|
-
return {
|
|
1140
|
-
...semantic,
|
|
1141
|
-
fill: {
|
|
1142
|
-
value: background
|
|
1143
|
-
}
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
return semantic;
|
|
906
|
+
class OoxmlPackage {
|
|
907
|
+
zip;
|
|
908
|
+
textCache = new Map();
|
|
909
|
+
constructor(zip){
|
|
910
|
+
this.zip = zip;
|
|
1147
911
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
912
|
+
static async load(buffer) {
|
|
913
|
+
return new OoxmlPackage(await jszip.loadAsync(buffer));
|
|
914
|
+
}
|
|
915
|
+
has(partPath) {
|
|
916
|
+
return Boolean(this.zip.file(partPath));
|
|
917
|
+
}
|
|
918
|
+
paths(prefix) {
|
|
919
|
+
return Object.keys(this.zip.files).filter((path)=>path.startsWith(prefix) && !this.zip.files[path]?.dir);
|
|
920
|
+
}
|
|
921
|
+
async text(partPath) {
|
|
922
|
+
const cached = this.textCache.get(partPath);
|
|
923
|
+
if (cached) return cached;
|
|
924
|
+
const entry = this.zip.file(partPath);
|
|
925
|
+
if (!entry) throw new Error(`Office package part is missing: ${partPath}`);
|
|
926
|
+
const pending = entry.async('uint8array').then((bytes)=>decodeXmlBytes(bytes, partPath));
|
|
927
|
+
this.textCache.set(partPath, pending);
|
|
928
|
+
try {
|
|
929
|
+
return await pending;
|
|
930
|
+
} catch (error) {
|
|
931
|
+
this.textCache.delete(partPath);
|
|
932
|
+
throw error;
|
|
1152
933
|
}
|
|
1153
|
-
}
|
|
934
|
+
}
|
|
935
|
+
async xml(partPath) {
|
|
936
|
+
return parseXml(await this.text(partPath), partPath);
|
|
937
|
+
}
|
|
938
|
+
async bytes(partPath) {
|
|
939
|
+
const entry = this.zip.file(partPath);
|
|
940
|
+
if (!entry) throw new Error(`Office package part is missing: ${partPath}`);
|
|
941
|
+
return entry.async('uint8array');
|
|
942
|
+
}
|
|
943
|
+
async relationships(sourcePart) {
|
|
944
|
+
const partPath = relationshipsPartPath(sourcePart);
|
|
945
|
+
if (!this.has(partPath)) return new Map();
|
|
946
|
+
const document = await this.xml(partPath);
|
|
947
|
+
return new Map(descendants(document, 'Relationship').map((element)=>{
|
|
948
|
+
const relationship = {
|
|
949
|
+
id: attribute(element, 'Id') ?? '',
|
|
950
|
+
target: resolvePartTarget(sourcePart, attribute(element, 'Target') ?? ''),
|
|
951
|
+
type: attribute(element, 'Type') ?? '',
|
|
952
|
+
targetMode: attribute(element, 'TargetMode') ?? void 0
|
|
953
|
+
};
|
|
954
|
+
return [
|
|
955
|
+
relationship.id,
|
|
956
|
+
relationship
|
|
957
|
+
];
|
|
958
|
+
}));
|
|
959
|
+
}
|
|
1154
960
|
}
|
|
1155
|
-
function
|
|
1156
|
-
const
|
|
1157
|
-
const
|
|
1158
|
-
if (
|
|
1159
|
-
|
|
1160
|
-
const styles = [
|
|
1161
|
-
`background-color: ${presentation.backgroundColor}`,
|
|
1162
|
-
presentation.backgroundImage ? `background-image: ${presentation.backgroundImage}` : '',
|
|
1163
|
-
presentation.backgroundSize ? `background-size: ${presentation.backgroundSize}` : ''
|
|
1164
|
-
].filter(Boolean);
|
|
1165
|
-
return {
|
|
1166
|
-
'data-office-paragraph-shading': serialized,
|
|
1167
|
-
style: styles.join('; ')
|
|
1168
|
-
};
|
|
961
|
+
function parseXml(source, label = 'Office XML') {
|
|
962
|
+
const document = new DOMParser().parseFromString(source, 'application/xml');
|
|
963
|
+
const error = descendants(document, 'parsererror')[0];
|
|
964
|
+
if (error) throw new Error(`${label} is not valid XML: ${error.textContent?.trim() || 'parse error'}`);
|
|
965
|
+
return document;
|
|
1169
966
|
}
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
const
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
theme
|
|
1182
|
-
} : {}
|
|
1183
|
-
};
|
|
967
|
+
const xmlElementPatterns = new Map();
|
|
968
|
+
function xmlContainsAnyElement(source, localNames) {
|
|
969
|
+
if (!source || !localNames.length) return false;
|
|
970
|
+
const key = localNames.join('\u0000');
|
|
971
|
+
let pattern = xmlElementPatterns.get(key);
|
|
972
|
+
if (!pattern) {
|
|
973
|
+
const alternatives = localNames.map((name)=>name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
|
|
974
|
+
pattern = new RegExp(`<(?:[A-Za-z_][\\w.-]*:)?(?:${alternatives})(?=[\\s/>])`);
|
|
975
|
+
xmlElementPatterns.set(key, pattern);
|
|
976
|
+
}
|
|
977
|
+
return pattern.test(source);
|
|
1184
978
|
}
|
|
1185
|
-
function
|
|
1186
|
-
const
|
|
1187
|
-
return
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
};
|
|
979
|
+
function attribute(element, name) {
|
|
980
|
+
const direct = element.getAttribute(name);
|
|
981
|
+
if (null !== direct) return direct;
|
|
982
|
+
const localName = name.includes(':') ? name.slice(name.indexOf(':') + 1) : name;
|
|
983
|
+
return Array.from(element.attributes).find((item)=>{
|
|
984
|
+
const itemLocalName = item.localName.includes(':') ? item.localName.slice(item.localName.indexOf(':') + 1) : item.localName;
|
|
985
|
+
return itemLocalName === localName && (!name.includes(':') || item.name === name);
|
|
986
|
+
})?.value ?? null;
|
|
1193
987
|
}
|
|
1194
|
-
function
|
|
1195
|
-
if (
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
const background = shadingColor(shading.fill, 'transparent');
|
|
1200
|
-
if ('clear' === shading.pattern) return {
|
|
1201
|
-
backgroundColor: background
|
|
1202
|
-
};
|
|
1203
|
-
if ('solid' === shading.pattern) return {
|
|
1204
|
-
backgroundColor: foreground
|
|
1205
|
-
};
|
|
1206
|
-
const percentage = shadingPercentage(shading.pattern);
|
|
1207
|
-
if (null !== percentage) {
|
|
1208
|
-
const inverted = percentage > 50;
|
|
1209
|
-
const dotColor = inverted ? background : foreground;
|
|
1210
|
-
const baseColor = inverted ? foreground : background;
|
|
1211
|
-
const density = Math.min(50, inverted ? 100 - percentage : percentage);
|
|
1212
|
-
const spacing = Math.max(2, Math.round(9 - density / 7));
|
|
1213
|
-
return {
|
|
1214
|
-
backgroundColor: baseColor,
|
|
1215
|
-
backgroundImage: `radial-gradient(circle, ${dotColor} 0 1px, transparent 1.2px)`,
|
|
1216
|
-
backgroundSize: `${spacing}px ${spacing}px`
|
|
1217
|
-
};
|
|
988
|
+
function xmlNamespacePrefix(element, namespace) {
|
|
989
|
+
if (!namespace) return element.prefix;
|
|
990
|
+
if ('function' == typeof element.lookupPrefix) {
|
|
991
|
+
const prefix = element.lookupPrefix(namespace);
|
|
992
|
+
if (prefix) return prefix;
|
|
1218
993
|
}
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
function shadingColor(color, fallback) {
|
|
1230
|
-
return color && 'auto' !== color.value ? color.value : fallback;
|
|
994
|
+
let current = element;
|
|
995
|
+
while(current){
|
|
996
|
+
if (current.namespaceURI === namespace && current.prefix) return current.prefix;
|
|
997
|
+
const declaration = Array.from(current.attributes).find((item)=>item.value === namespace && ('xmlns' === item.name || item.name.startsWith('xmlns:')));
|
|
998
|
+
if (declaration?.name.startsWith('xmlns:')) return declaration.name.slice(6);
|
|
999
|
+
current = current.parentElement;
|
|
1000
|
+
}
|
|
1001
|
+
return null;
|
|
1231
1002
|
}
|
|
1232
|
-
function
|
|
1233
|
-
|
|
1234
|
-
return match?.[1] ? Number(match[1]) : null;
|
|
1003
|
+
function directChildren(parent, localName) {
|
|
1004
|
+
return Array.from(parent.children).filter((element)=>!localName || element.localName === localName);
|
|
1235
1005
|
}
|
|
1236
|
-
function
|
|
1237
|
-
|
|
1238
|
-
const percentage = shadingPercentage(shading.pattern);
|
|
1239
|
-
return null !== percentage && percentage > 50;
|
|
1006
|
+
function directChild(parent, localName) {
|
|
1007
|
+
return directChildren(parent, localName)[0];
|
|
1240
1008
|
}
|
|
1241
|
-
function
|
|
1242
|
-
|
|
1243
|
-
0,
|
|
1244
|
-
90
|
|
1245
|
-
];
|
|
1246
|
-
if (pattern.includes('DiagCross')) return [
|
|
1247
|
-
45,
|
|
1248
|
-
-45
|
|
1249
|
-
];
|
|
1250
|
-
if (pattern.includes('VertStripe')) return [
|
|
1251
|
-
90
|
|
1252
|
-
];
|
|
1253
|
-
if (pattern.includes('ReverseDiagStripe')) return [
|
|
1254
|
-
-45
|
|
1255
|
-
];
|
|
1256
|
-
if (pattern.includes('DiagStripe')) return [
|
|
1257
|
-
45
|
|
1258
|
-
];
|
|
1259
|
-
return [
|
|
1260
|
-
0
|
|
1261
|
-
];
|
|
1009
|
+
function descendants(parent, localName) {
|
|
1010
|
+
return Array.from(parent.querySelectorAll('*')).filter((element)=>element.localName === localName);
|
|
1262
1011
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
'blue',
|
|
1267
|
-
'cyan',
|
|
1268
|
-
'darkBlue',
|
|
1269
|
-
'darkCyan',
|
|
1270
|
-
'darkGray',
|
|
1271
|
-
'darkGreen',
|
|
1272
|
-
'darkMagenta',
|
|
1273
|
-
'darkRed',
|
|
1274
|
-
'darkYellow',
|
|
1275
|
-
'green',
|
|
1276
|
-
'lightGray',
|
|
1277
|
-
'magenta',
|
|
1278
|
-
'none',
|
|
1279
|
-
'red',
|
|
1280
|
-
'white',
|
|
1281
|
-
'yellow'
|
|
1282
|
-
]);
|
|
1283
|
-
const HIGHLIGHT_COLORS = {
|
|
1284
|
-
black: '#000000',
|
|
1285
|
-
blue: '#0000ff',
|
|
1286
|
-
cyan: '#00ffff',
|
|
1287
|
-
darkBlue: '#000080',
|
|
1288
|
-
darkCyan: '#008080',
|
|
1289
|
-
darkGray: '#808080',
|
|
1290
|
-
darkGreen: '#008000',
|
|
1291
|
-
darkMagenta: '#800080',
|
|
1292
|
-
darkRed: '#800000',
|
|
1293
|
-
darkYellow: '#808000',
|
|
1294
|
-
green: '#00ff00',
|
|
1295
|
-
lightGray: '#c0c0c0',
|
|
1296
|
-
magenta: '#ff00ff',
|
|
1297
|
-
none: 'transparent',
|
|
1298
|
-
red: '#ff0000',
|
|
1299
|
-
white: '#ffffff',
|
|
1300
|
-
yellow: '#ffff00'
|
|
1301
|
-
};
|
|
1302
|
-
function normalizeDocumentHighlight(source) {
|
|
1303
|
-
return 'string' == typeof source && DOCUMENT_HIGHLIGHT_VALUES.has(source) ? source : null;
|
|
1012
|
+
function firstDescendant(parent, localName) {
|
|
1013
|
+
if (!parent) return;
|
|
1014
|
+
return descendants(parent, localName)[0];
|
|
1304
1015
|
}
|
|
1305
|
-
function
|
|
1306
|
-
|
|
1307
|
-
const
|
|
1308
|
-
|
|
1309
|
-
|
|
1016
|
+
function childPath(parent, ...localNames) {
|
|
1017
|
+
let current = parent;
|
|
1018
|
+
for (const name of localNames){
|
|
1019
|
+
if (!current) return;
|
|
1020
|
+
current = directChild(current, name);
|
|
1021
|
+
}
|
|
1022
|
+
return current instanceof Element ? current : void 0;
|
|
1310
1023
|
}
|
|
1311
|
-
function
|
|
1312
|
-
|
|
1313
|
-
|
|
1024
|
+
function resolvePartTarget(sourcePart, target) {
|
|
1025
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(target)) return target;
|
|
1026
|
+
const segments = target.startsWith('/') ? [] : sourcePart.split('/').slice(0, -1);
|
|
1027
|
+
for (const segment of target.replace(/^\/+/, '').split('/'))if (segment && '.' !== segment) if ('..' === segment) segments.pop();
|
|
1028
|
+
else segments.push(segment);
|
|
1029
|
+
return segments.join('/');
|
|
1314
1030
|
}
|
|
1315
|
-
function
|
|
1316
|
-
const
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1031
|
+
function contentTypeForPart(partPath) {
|
|
1032
|
+
const extension = partPath.split('.').pop()?.toLowerCase();
|
|
1033
|
+
const types = {
|
|
1034
|
+
apng: 'image/apng',
|
|
1035
|
+
bmp: 'image/bmp',
|
|
1036
|
+
emf: 'image/emf',
|
|
1037
|
+
gif: 'image/gif',
|
|
1038
|
+
jpeg: 'image/jpeg',
|
|
1039
|
+
jpg: 'image/jpeg',
|
|
1040
|
+
png: 'image/png',
|
|
1041
|
+
svg: 'image/svg+xml',
|
|
1042
|
+
tif: 'image/tiff',
|
|
1043
|
+
tiff: 'image/tiff',
|
|
1044
|
+
webp: 'image/webp',
|
|
1045
|
+
wmf: 'image/wmf'
|
|
1046
|
+
};
|
|
1047
|
+
return types[extension ?? ''] ?? 'application/octet-stream';
|
|
1320
1048
|
}
|
|
1321
|
-
function
|
|
1322
|
-
|
|
1049
|
+
function bytesToDataUrl(bytes, contentType) {
|
|
1050
|
+
let binary = '';
|
|
1051
|
+
const chunkSize = 32768;
|
|
1052
|
+
for(let offset = 0; offset < bytes.length; offset += chunkSize)binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
|
|
1053
|
+
return `data:${contentType};base64,${btoa(binary)}`;
|
|
1323
1054
|
}
|
|
1324
|
-
function
|
|
1325
|
-
const
|
|
1326
|
-
const
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
style: `background-color: ${color}`
|
|
1330
|
-
} : {};
|
|
1055
|
+
function relationshipsPartPath(sourcePart) {
|
|
1056
|
+
const separator = sourcePart.lastIndexOf('/');
|
|
1057
|
+
const directory = separator >= 0 ? sourcePart.slice(0, separator + 1) : '';
|
|
1058
|
+
const fileName = separator >= 0 ? sourcePart.slice(separator + 1) : sourcePart;
|
|
1059
|
+
return `${directory}_rels/${fileName}.rels`;
|
|
1331
1060
|
}
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
]){
|
|
1355
|
-
const color = source[name];
|
|
1356
|
-
if (void 0 !== color) {
|
|
1357
|
-
if (!isRecordWithKeys(color, RUN_SHADING_COLOR_KEYS)) return null;
|
|
1358
|
-
if (void 0 !== color.theme && !isRecordWithKeys(color.theme, THEME_REFERENCE_KEYS)) return null;
|
|
1359
|
-
}
|
|
1061
|
+
const WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
|
|
1062
|
+
class DocxThemePatchCollector {
|
|
1063
|
+
patches = [];
|
|
1064
|
+
nextMarker = 1;
|
|
1065
|
+
usedColors;
|
|
1066
|
+
constructor(sourceHtml){
|
|
1067
|
+
this.usedColors = sourceColors(sourceHtml);
|
|
1068
|
+
}
|
|
1069
|
+
marker(kind, reference, currentColor) {
|
|
1070
|
+
if (!reference || normalizeColor(currentColor) !== reference.resolved) return null;
|
|
1071
|
+
let marker = '';
|
|
1072
|
+
do {
|
|
1073
|
+
marker = (0xf00000 + this.nextMarker).toString(16).padStart(6, '0').toUpperCase();
|
|
1074
|
+
this.nextMarker += 1;
|
|
1075
|
+
}while (this.usedColors.has(marker))
|
|
1076
|
+
this.usedColors.add(marker);
|
|
1077
|
+
this.patches.push({
|
|
1078
|
+
kind,
|
|
1079
|
+
marker,
|
|
1080
|
+
reference
|
|
1081
|
+
});
|
|
1082
|
+
return marker;
|
|
1360
1083
|
}
|
|
1361
|
-
return normalizeDocumentParagraphShading(source);
|
|
1362
1084
|
}
|
|
1363
|
-
function
|
|
1364
|
-
|
|
1365
|
-
|
|
1085
|
+
function serializeDocxThemeReference(reference) {
|
|
1086
|
+
return reference ? JSON.stringify(reference) : void 0;
|
|
1087
|
+
}
|
|
1088
|
+
function parseDocxThemeReference(value) {
|
|
1089
|
+
if (!value) return null;
|
|
1366
1090
|
try {
|
|
1367
|
-
|
|
1091
|
+
const parsed = JSON.parse(value);
|
|
1092
|
+
const theme = 'string' == typeof parsed.theme ? parsed.theme.trim() : '';
|
|
1093
|
+
const resolved = normalizeColor('string' == typeof parsed.resolved ? parsed.resolved : null);
|
|
1094
|
+
const tint = byteHex(parsed.tint);
|
|
1095
|
+
const shade = byteHex(parsed.shade);
|
|
1096
|
+
if (!theme || !resolved) return null;
|
|
1097
|
+
return {
|
|
1098
|
+
theme,
|
|
1099
|
+
resolved,
|
|
1100
|
+
...tint ? {
|
|
1101
|
+
tint
|
|
1102
|
+
} : {},
|
|
1103
|
+
...shade ? {
|
|
1104
|
+
shade
|
|
1105
|
+
} : {}
|
|
1106
|
+
};
|
|
1368
1107
|
} catch {
|
|
1369
1108
|
return null;
|
|
1370
1109
|
}
|
|
1371
1110
|
}
|
|
1372
|
-
function
|
|
1373
|
-
|
|
1374
|
-
const
|
|
1375
|
-
|
|
1111
|
+
async function patchDocxThemeReferences(buffer, patches) {
|
|
1112
|
+
if (!patches.length) return buffer;
|
|
1113
|
+
const archive = await jszip.loadAsync(buffer);
|
|
1114
|
+
const byMarker = new Map(patches.map((patch)=>[
|
|
1115
|
+
patch.marker,
|
|
1116
|
+
patch
|
|
1117
|
+
]));
|
|
1118
|
+
const entries = Object.values(archive.files).filter((entry)=>!entry.dir && /^word\/(?:document|header\d+|footer\d+|footnotes|endnotes|comments)\.xml$/.test(entry.name));
|
|
1119
|
+
for (const entry of entries){
|
|
1120
|
+
const document = parseXml(await entry.async('text'), entry.name);
|
|
1121
|
+
let changed = false;
|
|
1122
|
+
for (const element of Array.from(document.getElementsByTagName('*')))for (const target of themePatchTargets(element.localName)){
|
|
1123
|
+
const marker = wordAttribute(element, target.directAttribute)?.toUpperCase();
|
|
1124
|
+
const patch = marker ? byMarker.get(marker) : void 0;
|
|
1125
|
+
if (patch && patch.kind === target.kind) {
|
|
1126
|
+
setWordAttribute(document, element, target.directAttribute, patch.reference.resolved.slice(1).toUpperCase());
|
|
1127
|
+
setWordAttribute(document, element, target.themeAttribute, patch.reference.theme);
|
|
1128
|
+
setOptionalWordAttribute(document, element, target.tintAttribute, patch.reference.tint);
|
|
1129
|
+
setOptionalWordAttribute(document, element, target.shadeAttribute, patch.reference.shade);
|
|
1130
|
+
changed = true;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
if (changed) archive.file(entry.name, new XMLSerializer().serializeToString(document));
|
|
1134
|
+
}
|
|
1135
|
+
return archive.generateAsync({
|
|
1136
|
+
type: 'arraybuffer'
|
|
1137
|
+
});
|
|
1376
1138
|
}
|
|
1377
|
-
function
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
pattern: 'nil'
|
|
1386
|
-
};
|
|
1387
|
-
return paragraphShadingBackgroundUsesForeground(semantic) ? {
|
|
1388
|
-
...semantic,
|
|
1389
|
-
color: {
|
|
1390
|
-
value: background
|
|
1139
|
+
function themePatchTargets(localName) {
|
|
1140
|
+
if ('color' === localName) return [
|
|
1141
|
+
{
|
|
1142
|
+
kind: 'color',
|
|
1143
|
+
directAttribute: 'val',
|
|
1144
|
+
themeAttribute: 'themeColor',
|
|
1145
|
+
tintAttribute: 'themeTint',
|
|
1146
|
+
shadeAttribute: 'themeShade'
|
|
1391
1147
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1148
|
+
];
|
|
1149
|
+
if ('u' === localName) return [
|
|
1150
|
+
{
|
|
1151
|
+
kind: 'underline',
|
|
1152
|
+
directAttribute: 'color',
|
|
1153
|
+
themeAttribute: 'themeColor',
|
|
1154
|
+
tintAttribute: 'themeTint',
|
|
1155
|
+
shadeAttribute: 'themeShade'
|
|
1396
1156
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1157
|
+
];
|
|
1158
|
+
if ('shd' === localName) return [
|
|
1159
|
+
{
|
|
1160
|
+
kind: 'fill',
|
|
1161
|
+
directAttribute: 'fill',
|
|
1162
|
+
themeAttribute: 'themeFill',
|
|
1163
|
+
tintAttribute: 'themeFillTint',
|
|
1164
|
+
shadeAttribute: 'themeFillShade'
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
kind: 'shadingColor',
|
|
1168
|
+
directAttribute: 'color',
|
|
1169
|
+
themeAttribute: 'themeColor',
|
|
1170
|
+
tintAttribute: 'themeTint',
|
|
1171
|
+
shadeAttribute: 'themeShade'
|
|
1172
|
+
}
|
|
1173
|
+
];
|
|
1174
|
+
return [
|
|
1175
|
+
'top',
|
|
1176
|
+
'right',
|
|
1177
|
+
'bottom',
|
|
1178
|
+
'left',
|
|
1179
|
+
'start',
|
|
1180
|
+
'end'
|
|
1181
|
+
].includes(localName) ? [
|
|
1182
|
+
{
|
|
1183
|
+
kind: 'border',
|
|
1184
|
+
directAttribute: 'color',
|
|
1185
|
+
themeAttribute: 'themeColor',
|
|
1186
|
+
tintAttribute: 'themeTint',
|
|
1187
|
+
shadeAttribute: 'themeShade'
|
|
1188
|
+
}
|
|
1189
|
+
] : [];
|
|
1413
1190
|
}
|
|
1414
|
-
function
|
|
1415
|
-
|
|
1191
|
+
function normalizeColor(value) {
|
|
1192
|
+
const normalized = value?.trim().toLowerCase();
|
|
1193
|
+
if (!normalized || !/^#[0-9a-f]{6}$/.test(normalized)) return null;
|
|
1194
|
+
return normalized;
|
|
1416
1195
|
}
|
|
1417
|
-
|
|
1418
|
-
const
|
|
1419
|
-
const
|
|
1420
|
-
const
|
|
1421
|
-
const
|
|
1422
|
-
|
|
1423
|
-
'highAnsi',
|
|
1424
|
-
'eastAsia',
|
|
1425
|
-
'complexScript',
|
|
1426
|
-
'hint'
|
|
1427
|
-
]);
|
|
1428
|
-
const SCRIPT_FONT_FACE_KEYS = new Set([
|
|
1429
|
-
'name',
|
|
1430
|
-
'theme',
|
|
1431
|
-
'resolved'
|
|
1432
|
-
]);
|
|
1433
|
-
const SCRIPT_FONT_HINTS = new Set([
|
|
1434
|
-
'default',
|
|
1435
|
-
'eastAsia',
|
|
1436
|
-
'cs'
|
|
1437
|
-
]);
|
|
1438
|
-
const THEME_FONTS = new Set([
|
|
1439
|
-
'majorEastAsia',
|
|
1440
|
-
'majorBidi',
|
|
1441
|
-
'majorAscii',
|
|
1442
|
-
'majorHAnsi',
|
|
1443
|
-
'minorEastAsia',
|
|
1444
|
-
'minorBidi',
|
|
1445
|
-
'minorAscii',
|
|
1446
|
-
'minorHAnsi'
|
|
1447
|
-
]);
|
|
1448
|
-
const SLOT_FALLBACK_ORDER = {
|
|
1449
|
-
ascii: [
|
|
1450
|
-
'ascii',
|
|
1451
|
-
'highAnsi',
|
|
1452
|
-
'eastAsia',
|
|
1453
|
-
'complexScript'
|
|
1454
|
-
],
|
|
1455
|
-
highAnsi: [
|
|
1456
|
-
'highAnsi',
|
|
1457
|
-
'ascii',
|
|
1458
|
-
'eastAsia',
|
|
1459
|
-
'complexScript'
|
|
1460
|
-
],
|
|
1461
|
-
eastAsia: [
|
|
1462
|
-
'eastAsia',
|
|
1463
|
-
'highAnsi',
|
|
1464
|
-
'ascii',
|
|
1465
|
-
'complexScript'
|
|
1466
|
-
],
|
|
1467
|
-
complexScript: [
|
|
1468
|
-
'complexScript',
|
|
1469
|
-
'highAnsi',
|
|
1470
|
-
'ascii',
|
|
1471
|
-
'eastAsia'
|
|
1472
|
-
]
|
|
1473
|
-
};
|
|
1474
|
-
const NEUTRAL_SCRIPT_CHARACTER = /^[\p{Cc}\p{Cf}\p{M}\p{N}\p{P}\p{S}\p{Z}]$/u;
|
|
1475
|
-
function normalizeDocumentScriptFonts(source) {
|
|
1476
|
-
if (!work_document_script_fonts_isRecordWithKeys(source, SCRIPT_FONT_KEYS)) return null;
|
|
1477
|
-
const normalized = {};
|
|
1478
|
-
for (const slot of scriptFontSlots){
|
|
1479
|
-
if (void 0 === source[slot]) continue;
|
|
1480
|
-
const face = normalizeDocumentScriptFontFace(source[slot]);
|
|
1481
|
-
if (!face) return null;
|
|
1482
|
-
normalized[slot] = face;
|
|
1483
|
-
}
|
|
1484
|
-
if (void 0 !== source.hint) {
|
|
1485
|
-
const hint = normalizeDocumentScriptFontHint(source.hint);
|
|
1486
|
-
if (!hint) return null;
|
|
1487
|
-
normalized.hint = hint;
|
|
1196
|
+
function sourceColors(source) {
|
|
1197
|
+
const colors = new Set();
|
|
1198
|
+
for (const match of source.matchAll(/#([0-9a-f]{6})\b/gi))if (match[1]) colors.add(match[1].toUpperCase());
|
|
1199
|
+
for (const match of source.matchAll(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/gi)){
|
|
1200
|
+
const channels = match.slice(1, 4).map(Number);
|
|
1201
|
+
if (!channels.some((channel)=>channel < 0 || channel > 255)) colors.add(channels.map((channel)=>channel.toString(16).padStart(2, '0')).join('').toUpperCase());
|
|
1488
1202
|
}
|
|
1489
|
-
return
|
|
1490
|
-
}
|
|
1491
|
-
function serializeDocumentScriptFonts(source) {
|
|
1492
|
-
const fonts = normalizeDocumentScriptFonts(source);
|
|
1493
|
-
return fonts ? JSON.stringify(fonts) : null;
|
|
1494
|
-
}
|
|
1495
|
-
function parseDocumentScriptFonts(source) {
|
|
1496
|
-
if (!source || source.length > MAX_SERIALIZED_SCRIPT_FONTS_LENGTH) return null;
|
|
1497
|
-
try {
|
|
1498
|
-
return normalizeDocumentScriptFonts(JSON.parse(source));
|
|
1499
|
-
} catch {
|
|
1500
|
-
return null;
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
function documentScriptFontsFromElement(element) {
|
|
1504
|
-
return parseDocumentScriptFonts(element.getAttribute(DOCUMENT_SCRIPT_FONTS_ATTRIBUTE));
|
|
1505
|
-
}
|
|
1506
|
-
function documentScriptFontSlotFromElement(element) {
|
|
1507
|
-
return normalizeDocumentScriptFontSlot(element.getAttribute(DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE));
|
|
1508
|
-
}
|
|
1509
|
-
function documentScriptFontsDomAttributes(source, slot) {
|
|
1510
|
-
const fonts = normalizeDocumentScriptFonts(source);
|
|
1511
|
-
const normalizedSlot = normalizeDocumentScriptFontSlot(slot);
|
|
1512
|
-
if (!fonts) return {};
|
|
1513
|
-
const serialized = serializeDocumentScriptFonts(fonts);
|
|
1514
|
-
if (!serialized) return {};
|
|
1515
|
-
const family = documentScriptFontFamily(fonts, normalizedSlot ?? documentScriptFontSlotFromHint(fonts.hint));
|
|
1516
|
-
return {
|
|
1517
|
-
[DOCUMENT_SCRIPT_FONTS_ATTRIBUTE]: serialized,
|
|
1518
|
-
...normalizedSlot ? {
|
|
1519
|
-
[DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE]: normalizedSlot
|
|
1520
|
-
} : {},
|
|
1521
|
-
...family ? {
|
|
1522
|
-
style: `font-family: ${family}`
|
|
1523
|
-
} : {}
|
|
1524
|
-
};
|
|
1525
|
-
}
|
|
1526
|
-
function documentScriptFontFamily(source, slot) {
|
|
1527
|
-
const fonts = normalizeDocumentScriptFonts(source);
|
|
1528
|
-
if (!fonts) return;
|
|
1529
|
-
const families = [];
|
|
1530
|
-
const seen = new Set();
|
|
1531
|
-
for (const candidate of documentScriptFontFallbackSlots(slot)){
|
|
1532
|
-
const family = documentScriptFontFaceFamily(fonts[candidate]);
|
|
1533
|
-
const key = family?.toLocaleLowerCase();
|
|
1534
|
-
if (!(!family || !key || seen.has(key))) {
|
|
1535
|
-
seen.add(key);
|
|
1536
|
-
families.push(cssFontFamily(family));
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return families.length ? families.join(', ') : void 0;
|
|
1540
|
-
}
|
|
1541
|
-
function documentScriptFontFallbackSlots(slot) {
|
|
1542
|
-
return SLOT_FALLBACK_ORDER[slot];
|
|
1203
|
+
return colors;
|
|
1543
1204
|
}
|
|
1544
|
-
function
|
|
1545
|
-
|
|
1546
|
-
const
|
|
1547
|
-
|
|
1548
|
-
const currentPrimary = documentFontNameFromCssFamily(safeCurrent);
|
|
1549
|
-
const projectedPrimary = documentFontNameFromCssFamily(projected);
|
|
1550
|
-
return currentPrimary && projectedPrimary && currentPrimary.toLocaleLowerCase() === projectedPrimary.toLocaleLowerCase() ? safeCurrent : projected;
|
|
1205
|
+
function byteHex(value) {
|
|
1206
|
+
if ('string' != typeof value) return;
|
|
1207
|
+
const normalized = value.trim().toUpperCase();
|
|
1208
|
+
return /^[0-9A-F]{2}$/.test(normalized) ? normalized : void 0;
|
|
1551
1209
|
}
|
|
1552
|
-
function
|
|
1553
|
-
|
|
1554
|
-
return fonts ? documentScriptFontFaceFamily(fonts[slot]) ?? null : null;
|
|
1210
|
+
function wordAttribute(element, name) {
|
|
1211
|
+
return element.getAttributeNS(WORD_NAMESPACE, name) ?? element.getAttribute(`w:${name}`);
|
|
1555
1212
|
}
|
|
1556
|
-
function
|
|
1557
|
-
const
|
|
1558
|
-
|
|
1559
|
-
const face = {
|
|
1560
|
-
name,
|
|
1561
|
-
resolved: name
|
|
1562
|
-
};
|
|
1563
|
-
return {
|
|
1564
|
-
ascii: face,
|
|
1565
|
-
highAnsi: face,
|
|
1566
|
-
eastAsia: face,
|
|
1567
|
-
complexScript: face,
|
|
1568
|
-
hint: 'default'
|
|
1569
|
-
};
|
|
1213
|
+
function setWordAttribute(document, element, name, value) {
|
|
1214
|
+
const prefix = xmlNamespacePrefix(document.documentElement, WORD_NAMESPACE) ?? 'w';
|
|
1215
|
+
element.setAttributeNS(WORD_NAMESPACE, `${prefix}:${name}`, value);
|
|
1570
1216
|
}
|
|
1571
|
-
function
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
...current
|
|
1575
|
-
};
|
|
1576
|
-
if (void 0 !== patch.latin) {
|
|
1577
|
-
const face = directFontFace(patch.latin);
|
|
1578
|
-
if (face) {
|
|
1579
|
-
next.ascii = face;
|
|
1580
|
-
next.highAnsi = face;
|
|
1581
|
-
} else {
|
|
1582
|
-
delete next.ascii;
|
|
1583
|
-
delete next.highAnsi;
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
if (void 0 !== patch.eastAsia) {
|
|
1587
|
-
const face = directFontFace(patch.eastAsia);
|
|
1588
|
-
if (face) next.eastAsia = face;
|
|
1589
|
-
else delete next.eastAsia;
|
|
1590
|
-
}
|
|
1591
|
-
if (void 0 !== patch.complexScript) {
|
|
1592
|
-
const face = directFontFace(patch.complexScript);
|
|
1593
|
-
if (face) next.complexScript = face;
|
|
1594
|
-
else delete next.complexScript;
|
|
1595
|
-
}
|
|
1596
|
-
return normalizeDocumentScriptFonts(next);
|
|
1217
|
+
function setOptionalWordAttribute(document, element, name, value) {
|
|
1218
|
+
if (value) setWordAttribute(document, element, name, value);
|
|
1219
|
+
else element.removeAttributeNS(WORD_NAMESPACE, name);
|
|
1597
1220
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1221
|
+
const DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE = 'data-office-paragraph-borders';
|
|
1222
|
+
const DOCUMENT_PARAGRAPH_BORDER_EDGES = [
|
|
1223
|
+
'top',
|
|
1224
|
+
'left',
|
|
1225
|
+
'bottom',
|
|
1226
|
+
'right',
|
|
1227
|
+
'between',
|
|
1228
|
+
'bar'
|
|
1229
|
+
];
|
|
1230
|
+
const DOCUMENT_PARAGRAPH_BORDER_STYLES = [
|
|
1231
|
+
'nil',
|
|
1232
|
+
'none',
|
|
1233
|
+
'single',
|
|
1234
|
+
'thick',
|
|
1235
|
+
'double',
|
|
1236
|
+
'dotted',
|
|
1237
|
+
'dashed',
|
|
1238
|
+
'dotDash',
|
|
1239
|
+
'dotDotDash',
|
|
1240
|
+
'triple',
|
|
1241
|
+
'thinThickSmallGap',
|
|
1242
|
+
'thickThinSmallGap',
|
|
1243
|
+
'thinThickThinSmallGap',
|
|
1244
|
+
'thinThickMediumGap',
|
|
1245
|
+
'thickThinMediumGap',
|
|
1246
|
+
'thinThickThinMediumGap',
|
|
1247
|
+
'thinThickLargeGap',
|
|
1248
|
+
'thickThinLargeGap',
|
|
1249
|
+
'thinThickThinLargeGap',
|
|
1250
|
+
'wave',
|
|
1251
|
+
'doubleWave',
|
|
1252
|
+
'dashSmallGap',
|
|
1253
|
+
'dashDotStroked',
|
|
1254
|
+
'threeDEmboss',
|
|
1255
|
+
'threeDEngrave',
|
|
1256
|
+
'outset',
|
|
1257
|
+
'inset',
|
|
1258
|
+
'apples',
|
|
1259
|
+
'archedScallops',
|
|
1260
|
+
'babyPacifier',
|
|
1261
|
+
'babyRattle',
|
|
1262
|
+
'balloons3Colors',
|
|
1263
|
+
'balloonsHotAir',
|
|
1264
|
+
'basicBlackDashes',
|
|
1265
|
+
'basicBlackDots',
|
|
1266
|
+
'basicBlackSquares',
|
|
1267
|
+
'basicThinLines',
|
|
1268
|
+
'basicWhiteDashes',
|
|
1269
|
+
'basicWhiteDots',
|
|
1270
|
+
'basicWhiteSquares',
|
|
1271
|
+
'basicWideInline',
|
|
1272
|
+
'basicWideMidline',
|
|
1273
|
+
'basicWideOutline',
|
|
1274
|
+
'bats',
|
|
1275
|
+
'birds',
|
|
1276
|
+
'birdsFlight',
|
|
1277
|
+
'cabins',
|
|
1278
|
+
'cakeSlice',
|
|
1279
|
+
'candyCorn',
|
|
1280
|
+
'celticKnotwork',
|
|
1281
|
+
'certificateBanner',
|
|
1282
|
+
'chainLink',
|
|
1283
|
+
'champagneBottle',
|
|
1284
|
+
'checkedBarBlack',
|
|
1285
|
+
'checkedBarColor',
|
|
1286
|
+
'checkered',
|
|
1287
|
+
'christmasTree',
|
|
1288
|
+
'circlesLines',
|
|
1289
|
+
'circlesRectangles',
|
|
1290
|
+
'classicalWave',
|
|
1291
|
+
'clocks',
|
|
1292
|
+
'compass',
|
|
1293
|
+
'confetti',
|
|
1294
|
+
'confettiGrays',
|
|
1295
|
+
'confettiOutline',
|
|
1296
|
+
'confettiStreamers',
|
|
1297
|
+
'confettiWhite',
|
|
1298
|
+
'cornerTriangles',
|
|
1299
|
+
'couponCutoutDashes',
|
|
1300
|
+
'couponCutoutDots',
|
|
1301
|
+
'crazyMaze',
|
|
1302
|
+
'creaturesButterfly',
|
|
1303
|
+
'creaturesFish',
|
|
1304
|
+
'creaturesInsects',
|
|
1305
|
+
'creaturesLadyBug',
|
|
1306
|
+
'crossStitch',
|
|
1307
|
+
'cup',
|
|
1308
|
+
'decoArch',
|
|
1309
|
+
'decoArchColor',
|
|
1310
|
+
'decoBlocks',
|
|
1311
|
+
'diamondsGray',
|
|
1312
|
+
'doubleD',
|
|
1313
|
+
'doubleDiamonds',
|
|
1314
|
+
'earth1',
|
|
1315
|
+
'earth2',
|
|
1316
|
+
'eclipsingSquares1',
|
|
1317
|
+
'eclipsingSquares2',
|
|
1318
|
+
'eggsBlack',
|
|
1319
|
+
'fans',
|
|
1320
|
+
'film',
|
|
1321
|
+
'firecrackers',
|
|
1322
|
+
'flowersBlockPrint',
|
|
1323
|
+
'flowersDaisies',
|
|
1324
|
+
'flowersModern1',
|
|
1325
|
+
'flowersModern2',
|
|
1326
|
+
'flowersPansy',
|
|
1327
|
+
'flowersRedRose',
|
|
1328
|
+
'flowersRoses',
|
|
1329
|
+
'flowersTeacup',
|
|
1330
|
+
'flowersTiny',
|
|
1331
|
+
'gems',
|
|
1332
|
+
'gingerbreadMan',
|
|
1333
|
+
'gradient',
|
|
1334
|
+
'handmade1',
|
|
1335
|
+
'handmade2',
|
|
1336
|
+
'heartBalloon',
|
|
1337
|
+
'heartGray',
|
|
1338
|
+
'hearts',
|
|
1339
|
+
'heebieJeebies',
|
|
1340
|
+
'holly',
|
|
1341
|
+
'houseFunky',
|
|
1342
|
+
'hypnotic',
|
|
1343
|
+
'iceCreamCones',
|
|
1344
|
+
'lightBulb',
|
|
1345
|
+
'lightning1',
|
|
1346
|
+
'lightning2',
|
|
1347
|
+
'mapPins',
|
|
1348
|
+
'mapleLeaf',
|
|
1349
|
+
'mapleMuffins',
|
|
1350
|
+
'marquee',
|
|
1351
|
+
'marqueeToothed',
|
|
1352
|
+
'moons',
|
|
1353
|
+
'mosaic',
|
|
1354
|
+
'musicNotes',
|
|
1355
|
+
'northwest',
|
|
1356
|
+
'ovals',
|
|
1357
|
+
'packages',
|
|
1358
|
+
'palmsBlack',
|
|
1359
|
+
'palmsColor',
|
|
1360
|
+
'paperClips',
|
|
1361
|
+
'papyrus',
|
|
1362
|
+
'partyFavor',
|
|
1363
|
+
'partyGlass',
|
|
1364
|
+
'pencils',
|
|
1365
|
+
'people',
|
|
1366
|
+
'peopleWaving',
|
|
1367
|
+
'peopleHats',
|
|
1368
|
+
'poinsettias',
|
|
1369
|
+
'postageStamp',
|
|
1370
|
+
'pumpkin1',
|
|
1371
|
+
'pushPinNote2',
|
|
1372
|
+
'pushPinNote1',
|
|
1373
|
+
'pyramids',
|
|
1374
|
+
'pyramidsAbove',
|
|
1375
|
+
'quadrants',
|
|
1376
|
+
'rings',
|
|
1377
|
+
'safari',
|
|
1378
|
+
'sawtooth',
|
|
1379
|
+
'sawtoothGray',
|
|
1380
|
+
'scaredCat',
|
|
1381
|
+
'seattle',
|
|
1382
|
+
'shadowedSquares',
|
|
1383
|
+
'sharksTeeth',
|
|
1384
|
+
'shorebirdTracks',
|
|
1385
|
+
'skyrocket',
|
|
1386
|
+
'snowflakeFancy',
|
|
1387
|
+
'snowflakes',
|
|
1388
|
+
'sombrero',
|
|
1389
|
+
'southwest',
|
|
1390
|
+
'stars',
|
|
1391
|
+
'starsTop',
|
|
1392
|
+
'stars3d',
|
|
1393
|
+
'starsBlack',
|
|
1394
|
+
'starsShadowed',
|
|
1395
|
+
'sun',
|
|
1396
|
+
'swirligig',
|
|
1397
|
+
'tornPaper',
|
|
1398
|
+
'tornPaperBlack',
|
|
1399
|
+
'trees',
|
|
1400
|
+
'triangleParty',
|
|
1401
|
+
'triangles',
|
|
1402
|
+
'tribal1',
|
|
1403
|
+
'tribal2',
|
|
1404
|
+
'tribal3',
|
|
1405
|
+
'tribal4',
|
|
1406
|
+
'tribal5',
|
|
1407
|
+
'tribal6',
|
|
1408
|
+
'triangle1',
|
|
1409
|
+
'triangle2',
|
|
1410
|
+
'triangleCircle1',
|
|
1411
|
+
'triangleCircle2',
|
|
1412
|
+
'shapes1',
|
|
1413
|
+
'shapes2',
|
|
1414
|
+
'twistedLines1',
|
|
1415
|
+
'twistedLines2',
|
|
1416
|
+
'vine',
|
|
1417
|
+
'waveline',
|
|
1418
|
+
'weavingAngles',
|
|
1419
|
+
'weavingBraid',
|
|
1420
|
+
'weavingRibbon',
|
|
1421
|
+
'weavingStrips',
|
|
1422
|
+
'whiteFlowers',
|
|
1423
|
+
'woodwork',
|
|
1424
|
+
'xIllusions',
|
|
1425
|
+
'zanyTriangles',
|
|
1426
|
+
'zigZag',
|
|
1427
|
+
'zigZagStitch'
|
|
1428
|
+
];
|
|
1429
|
+
const BORDER_STYLE_SET = new Set(DOCUMENT_PARAGRAPH_BORDER_STYLES);
|
|
1430
|
+
const BORDER_EDGE_SET = new Set(DOCUMENT_PARAGRAPH_BORDER_EDGES);
|
|
1431
|
+
const BORDER_PROPERTY_SET = new Set([
|
|
1432
|
+
'style',
|
|
1433
|
+
'color',
|
|
1434
|
+
'size',
|
|
1435
|
+
'space',
|
|
1436
|
+
'shadow',
|
|
1437
|
+
'frame'
|
|
1438
|
+
]);
|
|
1439
|
+
const LINE_BORDER_STYLES = new Set(DOCUMENT_PARAGRAPH_BORDER_STYLES.slice(0, 27));
|
|
1440
|
+
const DASHED_BORDER_STYLES = new Set([
|
|
1441
|
+
'dashed',
|
|
1442
|
+
'dashSmallGap',
|
|
1443
|
+
'dashDotStroked',
|
|
1444
|
+
'dotDash',
|
|
1445
|
+
'dotDotDash'
|
|
1446
|
+
]);
|
|
1447
|
+
const DOUBLE_BORDER_STYLES = new Set([
|
|
1448
|
+
'double',
|
|
1449
|
+
'triple',
|
|
1450
|
+
'thinThickSmallGap',
|
|
1451
|
+
'thickThinSmallGap',
|
|
1452
|
+
'thinThickThinSmallGap',
|
|
1453
|
+
'thinThickMediumGap',
|
|
1454
|
+
'thickThinMediumGap',
|
|
1455
|
+
'thinThickThinMediumGap',
|
|
1456
|
+
'thinThickLargeGap',
|
|
1457
|
+
'thickThinLargeGap',
|
|
1458
|
+
'thinThickThinLargeGap',
|
|
1459
|
+
'doubleWave'
|
|
1460
|
+
]);
|
|
1461
|
+
const MAX_SERIALIZED_PARAGRAPH_BORDERS = 32768;
|
|
1462
|
+
const POINTS_TO_PIXELS = 96 / 72;
|
|
1463
|
+
function normalizeDocumentParagraphBorders(source) {
|
|
1464
|
+
if (!source || 'object' != typeof source || Array.isArray(source)) return null;
|
|
1465
|
+
const record = source;
|
|
1466
|
+
if (Object.keys(record).some((key)=>!BORDER_EDGE_SET.has(key))) return null;
|
|
1467
|
+
const borders = {};
|
|
1468
|
+
for (const edge of DOCUMENT_PARAGRAPH_BORDER_EDGES){
|
|
1469
|
+
if (void 0 === record[edge]) continue;
|
|
1470
|
+
const border = normalizeDocumentParagraphBorder(record[edge]);
|
|
1471
|
+
if (!border) return null;
|
|
1472
|
+
borders[edge] = border;
|
|
1647
1473
|
}
|
|
1648
|
-
return
|
|
1649
|
-
}
|
|
1650
|
-
function normalizeDocumentScriptFontSlot(value) {
|
|
1651
|
-
return scriptFontSlots.includes(value) ? value : null;
|
|
1474
|
+
return Object.keys(borders).length ? borders : null;
|
|
1652
1475
|
}
|
|
1653
|
-
function
|
|
1654
|
-
|
|
1476
|
+
function normalizeDocumentParagraphBorder(source) {
|
|
1477
|
+
if (!source || 'object' != typeof source || Array.isArray(source)) return null;
|
|
1478
|
+
const record = source;
|
|
1479
|
+
if (Object.keys(record).some((key)=>!BORDER_PROPERTY_SET.has(key))) return null;
|
|
1480
|
+
const style = record.style;
|
|
1481
|
+
if ('string' != typeof style || !BORDER_STYLE_SET.has(style)) return null;
|
|
1482
|
+
const normalizedStyle = style;
|
|
1483
|
+
const color = normalizeBorderColor(record.color);
|
|
1484
|
+
if (void 0 !== record.color && !color) return null;
|
|
1485
|
+
const size = optionalInteger(record.size);
|
|
1486
|
+
if (null === size || void 0 !== size && !validBorderSize(normalizedStyle, size)) return null;
|
|
1487
|
+
const space = optionalInteger(record.space);
|
|
1488
|
+
if (null === space || void 0 !== space && (space < 0 || space > 31)) return null;
|
|
1489
|
+
const shadow = optionalBoolean(record.shadow);
|
|
1490
|
+
const frame = optionalBoolean(record.frame);
|
|
1491
|
+
if (null === shadow || null === frame) return null;
|
|
1492
|
+
return {
|
|
1493
|
+
style: normalizedStyle,
|
|
1494
|
+
...color ? {
|
|
1495
|
+
color
|
|
1496
|
+
} : {},
|
|
1497
|
+
...void 0 !== size ? {
|
|
1498
|
+
size
|
|
1499
|
+
} : {},
|
|
1500
|
+
...void 0 !== space ? {
|
|
1501
|
+
space
|
|
1502
|
+
} : {},
|
|
1503
|
+
...void 0 !== shadow ? {
|
|
1504
|
+
shadow
|
|
1505
|
+
} : {},
|
|
1506
|
+
...void 0 !== frame ? {
|
|
1507
|
+
frame
|
|
1508
|
+
} : {}
|
|
1509
|
+
};
|
|
1655
1510
|
}
|
|
1656
|
-
function
|
|
1657
|
-
|
|
1511
|
+
function parseDocumentParagraphBorders(source) {
|
|
1512
|
+
if ('string' != typeof source) return normalizeDocumentParagraphBorders(source);
|
|
1513
|
+
if (!source.trim() || source.length > MAX_SERIALIZED_PARAGRAPH_BORDERS) return null;
|
|
1514
|
+
try {
|
|
1515
|
+
return normalizeDocumentParagraphBorders(JSON.parse(source));
|
|
1516
|
+
} catch {
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
1658
1519
|
}
|
|
1659
|
-
function
|
|
1660
|
-
|
|
1661
|
-
if (
|
|
1662
|
-
return
|
|
1520
|
+
function serializeDocumentParagraphBorders(source) {
|
|
1521
|
+
const borders = normalizeDocumentParagraphBorders(source);
|
|
1522
|
+
if (!borders) return;
|
|
1523
|
+
return JSON.stringify(Object.fromEntries(DOCUMENT_PARAGRAPH_BORDER_EDGES.flatMap((edge)=>{
|
|
1524
|
+
const border = borders[edge];
|
|
1525
|
+
return border ? [
|
|
1526
|
+
[
|
|
1527
|
+
edge,
|
|
1528
|
+
serializedBorder(border)
|
|
1529
|
+
]
|
|
1530
|
+
] : [];
|
|
1531
|
+
})));
|
|
1663
1532
|
}
|
|
1664
|
-
function
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1533
|
+
function parseDocumentParagraphBordersElement(element) {
|
|
1534
|
+
const semantic = parseDocumentParagraphBorders(element.getAttribute(DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE));
|
|
1535
|
+
if (!semantic) return paragraphBordersFromCss(element);
|
|
1536
|
+
const edited = {
|
|
1537
|
+
...semantic
|
|
1538
|
+
};
|
|
1539
|
+
for (const edge of [
|
|
1540
|
+
'top',
|
|
1541
|
+
'left',
|
|
1542
|
+
'bottom',
|
|
1543
|
+
'right'
|
|
1544
|
+
]){
|
|
1545
|
+
const border = semantic[edge];
|
|
1546
|
+
const css = cssBorder(element, edge);
|
|
1547
|
+
if (!(!css || border && sameBorderPresentation(border, css))) {
|
|
1548
|
+
if ('none' === css.style) {
|
|
1549
|
+
edited[edge] = {
|
|
1550
|
+
style: 'nil'
|
|
1551
|
+
};
|
|
1680
1552
|
continue;
|
|
1681
1553
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1554
|
+
edited[edge] = {
|
|
1555
|
+
...border ?? {
|
|
1556
|
+
style: 'single'
|
|
1557
|
+
},
|
|
1558
|
+
style: cssStyleToBorderStyle(css.style),
|
|
1559
|
+
color: {
|
|
1560
|
+
value: css.color
|
|
1561
|
+
},
|
|
1562
|
+
size: cssWidthToEighthPoints(css.width)
|
|
1563
|
+
};
|
|
1686
1564
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
return normalizeDocumentFontName(family);
|
|
1565
|
+
}
|
|
1566
|
+
return normalizeDocumentParagraphBorders(edited);
|
|
1690
1567
|
}
|
|
1691
|
-
function
|
|
1692
|
-
const
|
|
1693
|
-
|
|
1568
|
+
function documentParagraphBordersDomAttributes(source) {
|
|
1569
|
+
const borders = normalizeDocumentParagraphBorders(source);
|
|
1570
|
+
const serialized = serializeDocumentParagraphBorders(borders);
|
|
1571
|
+
if (!borders || !serialized) return {};
|
|
1572
|
+
const styles = [];
|
|
1573
|
+
const shadows = [];
|
|
1574
|
+
for (const edge of [
|
|
1575
|
+
'top',
|
|
1576
|
+
'left',
|
|
1577
|
+
'bottom',
|
|
1578
|
+
'right'
|
|
1579
|
+
]){
|
|
1580
|
+
const border = borders[edge];
|
|
1581
|
+
if (!border) continue;
|
|
1582
|
+
const presentation = documentBorderPresentation(border);
|
|
1583
|
+
styles.push(`border-${edge}: ${formatPixels(presentation.width)}px ${presentation.style} ${presentation.color}`);
|
|
1584
|
+
if (border.space) styles.push(`padding-${edge}: ${formatPixels(border.space * POINTS_TO_PIXELS)}px`);
|
|
1585
|
+
if (border.shadow && presentation.width > 0) shadows.push(`2px 2px 0 ${presentation.color}`);
|
|
1586
|
+
}
|
|
1587
|
+
const between = borders.between ? documentBorderPresentation(borders.between) : null;
|
|
1588
|
+
if (between && between.width > 0) shadows.push(`inset 0 -${formatPixels(between.width)}px 0 ${between.color}`);
|
|
1589
|
+
const bar = borders.bar ? documentBorderPresentation(borders.bar) : null;
|
|
1590
|
+
if (bar && bar.width > 0) shadows.push(`inset ${formatPixels(bar.width)}px 0 0 ${bar.color}`);
|
|
1591
|
+
if (shadows.length) styles.push(`box-shadow: ${shadows.join(', ')}`);
|
|
1592
|
+
return {
|
|
1593
|
+
[DOCUMENT_PARAGRAPH_BORDERS_ATTRIBUTE]: serialized,
|
|
1594
|
+
...styles.length ? {
|
|
1595
|
+
style: styles.join('; ')
|
|
1596
|
+
} : {}
|
|
1597
|
+
};
|
|
1694
1598
|
}
|
|
1695
|
-
function
|
|
1696
|
-
|
|
1697
|
-
const normalized = value.trim();
|
|
1698
|
-
return normalized && normalized.length <= MAX_FONT_NAME_LENGTH && !/[\p{Cc}\p{Cs}]/u.test(normalized) ? normalized : null;
|
|
1599
|
+
function isDocumentParagraphArtBorderStyle(style) {
|
|
1600
|
+
return !LINE_BORDER_STYLES.has(style);
|
|
1699
1601
|
}
|
|
1700
|
-
|
|
1701
|
-
'
|
|
1702
|
-
|
|
1703
|
-
'
|
|
1704
|
-
'
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
if (!
|
|
1708
|
-
|
|
1709
|
-
const resolved = void 0 === source.resolved ? void 0 : normalizeDocumentFontName(source.resolved);
|
|
1710
|
-
const theme = void 0 === source.theme ? void 0 : normalizeDocumentThemeFont(source.theme);
|
|
1711
|
-
if (void 0 !== source.name && !name || void 0 !== source.resolved && !resolved || null === theme || !name && !theme && !resolved) return null;
|
|
1602
|
+
function normalizeBorderColor(source) {
|
|
1603
|
+
if (!source || 'object' != typeof source || Array.isArray(source)) return null;
|
|
1604
|
+
const record = source;
|
|
1605
|
+
if (Object.keys(record).some((key)=>'value' !== key && 'theme' !== key)) return null;
|
|
1606
|
+
const theme = parseDocxThemeReference('string' == typeof record.theme ? record.theme : record.theme ? JSON.stringify(record.theme) : void 0);
|
|
1607
|
+
const direct = 'auto' === record.value ? 'auto' : 'string' == typeof record.value ? normalizeCssColor(record.value) : null;
|
|
1608
|
+
const resolved = direct ?? theme?.resolved ?? null;
|
|
1609
|
+
if (!resolved || 'transparent' === resolved) return null;
|
|
1610
|
+
if (theme && resolved !== theme.resolved && !('auto' === resolved && 'none' === theme.theme && '#000000' === theme.resolved)) return null;
|
|
1712
1611
|
return {
|
|
1713
|
-
|
|
1714
|
-
name
|
|
1715
|
-
} : {},
|
|
1612
|
+
value: resolved,
|
|
1716
1613
|
...theme ? {
|
|
1717
1614
|
theme
|
|
1718
|
-
} : {},
|
|
1719
|
-
...resolved ? {
|
|
1720
|
-
resolved
|
|
1721
1615
|
} : {}
|
|
1722
1616
|
};
|
|
1723
1617
|
}
|
|
1724
|
-
function
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1618
|
+
function serializedBorder(border) {
|
|
1619
|
+
const color = border.color ? serializedBorderColor(border.color) : void 0;
|
|
1620
|
+
return {
|
|
1621
|
+
style: border.style,
|
|
1622
|
+
...color ? {
|
|
1623
|
+
color
|
|
1624
|
+
} : {},
|
|
1625
|
+
...void 0 !== border.size ? {
|
|
1626
|
+
size: border.size
|
|
1627
|
+
} : {},
|
|
1628
|
+
...void 0 !== border.space ? {
|
|
1629
|
+
space: border.space
|
|
1630
|
+
} : {},
|
|
1631
|
+
...void 0 !== border.shadow ? {
|
|
1632
|
+
shadow: border.shadow
|
|
1633
|
+
} : {},
|
|
1634
|
+
...void 0 !== border.frame ? {
|
|
1635
|
+
frame: border.frame
|
|
1636
|
+
} : {}
|
|
1637
|
+
};
|
|
1742
1638
|
}
|
|
1743
|
-
function
|
|
1744
|
-
|
|
1639
|
+
function serializedBorderColor(color) {
|
|
1640
|
+
const theme = serializeDocxThemeReference(color.theme ?? null);
|
|
1641
|
+
return {
|
|
1642
|
+
value: color.value,
|
|
1643
|
+
...theme ? {
|
|
1644
|
+
theme: JSON.parse(theme)
|
|
1645
|
+
} : {}
|
|
1646
|
+
};
|
|
1745
1647
|
}
|
|
1746
|
-
function
|
|
1747
|
-
|
|
1648
|
+
function optionalInteger(value) {
|
|
1649
|
+
if (void 0 === value) return;
|
|
1650
|
+
return 'number' == typeof value && Number.isSafeInteger(value) ? value : null;
|
|
1748
1651
|
}
|
|
1749
|
-
function
|
|
1750
|
-
|
|
1652
|
+
function optionalBoolean(value) {
|
|
1653
|
+
if (void 0 === value) return;
|
|
1654
|
+
return 'boolean' == typeof value ? value : null;
|
|
1751
1655
|
}
|
|
1752
|
-
function
|
|
1753
|
-
|
|
1656
|
+
function validBorderSize(style, size) {
|
|
1657
|
+
if ('nil' === style || 'none' === style) return size >= 0 && size <= 96;
|
|
1658
|
+
return isDocumentParagraphArtBorderStyle(style) ? size >= 1 && size <= 31 : size >= 2 && size <= 96;
|
|
1754
1659
|
}
|
|
1755
|
-
function
|
|
1756
|
-
if ('
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
if (index >= source.length) return null;
|
|
1768
|
-
} else if (character === quote) quote = '';
|
|
1769
|
-
continue;
|
|
1770
|
-
}
|
|
1771
|
-
if ('"' === character || "'" === character) {
|
|
1772
|
-
quote = character;
|
|
1773
|
-
continue;
|
|
1774
|
-
}
|
|
1775
|
-
if (',' === character) {
|
|
1776
|
-
tokens.push(source.slice(start, index).trim());
|
|
1777
|
-
start = index + 1;
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
if (quote) return null;
|
|
1781
|
-
tokens.push(source.slice(start).trim());
|
|
1782
|
-
return tokens.length && tokens.every(validCssFontFamilyToken) ? source : null;
|
|
1660
|
+
function documentBorderPresentation(border) {
|
|
1661
|
+
if ('nil' === border.style || 'none' === border.style || !border.size) return {
|
|
1662
|
+
color: 'transparent',
|
|
1663
|
+
style: 'none',
|
|
1664
|
+
width: 0
|
|
1665
|
+
};
|
|
1666
|
+
const width = Math.min(16, isDocumentParagraphArtBorderStyle(border.style) ? border.size * POINTS_TO_PIXELS : border.size / 6);
|
|
1667
|
+
return {
|
|
1668
|
+
color: border.color && 'auto' !== border.color.value ? border.color.value : '#000000',
|
|
1669
|
+
style: borderStyleToCssStyle(border.style),
|
|
1670
|
+
width
|
|
1671
|
+
};
|
|
1783
1672
|
}
|
|
1784
|
-
function
|
|
1785
|
-
if (
|
|
1786
|
-
if (
|
|
1787
|
-
if (
|
|
1788
|
-
|
|
1673
|
+
function borderStyleToCssStyle(style) {
|
|
1674
|
+
if ('nil' === style || 'none' === style) return 'none';
|
|
1675
|
+
if ('dotted' === style) return 'dotted';
|
|
1676
|
+
if (DASHED_BORDER_STYLES.has(style)) return 'dashed';
|
|
1677
|
+
if (DOUBLE_BORDER_STYLES.has(style)) return 'double';
|
|
1678
|
+
if ('inset' === style || 'threeDEngrave' === style) return 'inset';
|
|
1679
|
+
if ('outset' === style || 'threeDEmboss' === style) return 'outset';
|
|
1680
|
+
return 'solid';
|
|
1789
1681
|
}
|
|
1790
|
-
function
|
|
1791
|
-
|
|
1792
|
-
const
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1682
|
+
function paragraphBordersFromCss(element) {
|
|
1683
|
+
const borders = {};
|
|
1684
|
+
for (const edge of [
|
|
1685
|
+
'top',
|
|
1686
|
+
'left',
|
|
1687
|
+
'bottom',
|
|
1688
|
+
'right'
|
|
1689
|
+
]){
|
|
1690
|
+
const css = cssBorder(element, edge);
|
|
1691
|
+
if (css && 'none' !== css.style) borders[edge] = {
|
|
1692
|
+
style: cssStyleToBorderStyle(css.style),
|
|
1693
|
+
color: {
|
|
1694
|
+
value: css.color
|
|
1695
|
+
},
|
|
1696
|
+
size: cssWidthToEighthPoints(css.width)
|
|
1801
1697
|
};
|
|
1802
1698
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1699
|
+
return normalizeDocumentParagraphBorders(borders);
|
|
1700
|
+
}
|
|
1701
|
+
function cssBorder(element, edge) {
|
|
1702
|
+
const style = element.style.getPropertyValue(`border-${edge}-style`).trim();
|
|
1703
|
+
const width = Number.parseFloat(element.style.getPropertyValue(`border-${edge}-width`));
|
|
1704
|
+
const color = normalizeCssColor(element.style.getPropertyValue(`border-${edge}-color`));
|
|
1705
|
+
if (!style) return null;
|
|
1706
|
+
if ('none' === style || 'hidden' === style) return {
|
|
1707
|
+
color: '#000000',
|
|
1708
|
+
style: 'none',
|
|
1709
|
+
width: 0
|
|
1807
1710
|
};
|
|
1711
|
+
return color && 'transparent' !== color && Number.isFinite(width) && width > 0 ? {
|
|
1712
|
+
color,
|
|
1713
|
+
style,
|
|
1714
|
+
width
|
|
1715
|
+
} : null;
|
|
1808
1716
|
}
|
|
1809
|
-
function
|
|
1810
|
-
|
|
1717
|
+
function sameBorderPresentation(border, css) {
|
|
1718
|
+
const expected = documentBorderPresentation(border);
|
|
1719
|
+
if ('none' === expected.style) return 'none' === css.style && 0 === css.width;
|
|
1720
|
+
return expected.color === css.color && expected.style === css.style && Math.abs(expected.width - css.width) < 0.01;
|
|
1811
1721
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
'
|
|
1816
|
-
'
|
|
1817
|
-
'
|
|
1818
|
-
|
|
1819
|
-
const PROOFING_LANGUAGE_ORDER = [
|
|
1820
|
-
'latin',
|
|
1821
|
-
'eastAsia',
|
|
1822
|
-
'bidi'
|
|
1823
|
-
];
|
|
1824
|
-
const MAX_LANGUAGE_TAG_LENGTH = 85;
|
|
1825
|
-
const MAX_SERIALIZED_PROOFING_LANGUAGES_BYTES = 384;
|
|
1826
|
-
function normalizeDocumentLanguageTag(source) {
|
|
1827
|
-
if ('string' != typeof source) return null;
|
|
1828
|
-
if (!source || source !== source.trim() || source.length > MAX_LANGUAGE_TAG_LENGTH || /[\p{Cc}\p{Cs}]/u.test(source)) return null;
|
|
1829
|
-
return /^(?:x-none|[a-z0-9]{1,8}(?:-[a-z0-9]{1,8})*)$/iu.test(source) ? source : null;
|
|
1722
|
+
function cssStyleToBorderStyle(style) {
|
|
1723
|
+
if ('double' === style) return 'double';
|
|
1724
|
+
if ('dashed' === style) return 'dashed';
|
|
1725
|
+
if ('dotted' === style) return 'dotted';
|
|
1726
|
+
if ('inset' === style || 'groove' === style) return 'inset';
|
|
1727
|
+
if ('outset' === style || 'ridge' === style) return 'outset';
|
|
1728
|
+
return 'single';
|
|
1830
1729
|
}
|
|
1831
|
-
function
|
|
1832
|
-
|
|
1833
|
-
const keys = Object.keys(source);
|
|
1834
|
-
if (!keys.length || keys.some((key)=>!PROOFING_LANGUAGE_KEYS.has(key))) return null;
|
|
1835
|
-
const normalized = {};
|
|
1836
|
-
for (const key of PROOFING_LANGUAGE_ORDER){
|
|
1837
|
-
if (void 0 === source[key]) continue;
|
|
1838
|
-
const language = normalizeDocumentLanguageTag(source[key]);
|
|
1839
|
-
if (!language) return null;
|
|
1840
|
-
normalized[key] = language;
|
|
1841
|
-
}
|
|
1842
|
-
return Object.keys(normalized).length ? normalized : null;
|
|
1730
|
+
function cssWidthToEighthPoints(width) {
|
|
1731
|
+
return Math.max(2, Math.min(96, Math.round(6 * width)));
|
|
1843
1732
|
}
|
|
1844
|
-
function
|
|
1845
|
-
|
|
1846
|
-
if (!normalized) return;
|
|
1847
|
-
const serialized = JSON.stringify(normalized);
|
|
1848
|
-
return serialized.length <= MAX_SERIALIZED_PROOFING_LANGUAGES_BYTES ? serialized : void 0;
|
|
1733
|
+
function formatPixels(value) {
|
|
1734
|
+
return Number(value.toFixed(3)).toString();
|
|
1849
1735
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1736
|
+
const DOCUMENT_RUN_BORDER_ATTRIBUTE = 'data-office-run-border';
|
|
1737
|
+
const DOCUMENT_RUN_BORDER_STYLES = DOCUMENT_PARAGRAPH_BORDER_STYLES.slice(0, 27);
|
|
1738
|
+
const MAX_SERIALIZED_RUN_BORDER_BYTES = 4096;
|
|
1739
|
+
const work_document_run_border_POINTS_TO_PIXELS = 96 / 72;
|
|
1740
|
+
function normalizeDocumentRunBorder(source) {
|
|
1741
|
+
const border = normalizeDocumentParagraphBorder(source);
|
|
1742
|
+
return border && !isDocumentParagraphArtBorderStyle(border.style) ? border : null;
|
|
1743
|
+
}
|
|
1744
|
+
function parseDocumentRunBorder(source) {
|
|
1745
|
+
if ('string' != typeof source) return normalizeDocumentRunBorder(source);
|
|
1746
|
+
if (!source.trim() || source.length > MAX_SERIALIZED_RUN_BORDER_BYTES) return null;
|
|
1853
1747
|
try {
|
|
1854
|
-
|
|
1855
|
-
return normalized && JSON.stringify(normalized) === source ? normalized : null;
|
|
1748
|
+
return normalizeDocumentRunBorder(JSON.parse(source));
|
|
1856
1749
|
} catch {
|
|
1857
1750
|
return null;
|
|
1858
1751
|
}
|
|
1859
1752
|
}
|
|
1860
|
-
function
|
|
1861
|
-
|
|
1862
|
-
if (
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
if (!languages) return;
|
|
1874
|
-
if ('eastAsia' === slot) return languages.eastAsia ?? languages.latin;
|
|
1875
|
-
if ('complexScript' === slot) return languages.bidi ?? languages.latin;
|
|
1876
|
-
return languages.latin ?? languages.eastAsia ?? languages.bidi;
|
|
1877
|
-
}
|
|
1878
|
-
function documentProofingDomAttributes(languagesSource, noProofSource, slot) {
|
|
1879
|
-
const languages = normalizeDocumentProofingLanguages(languagesSource);
|
|
1880
|
-
const serialized = serializeDocumentProofingLanguages(languages);
|
|
1881
|
-
const noProof = normalizeDocumentNoProof(noProofSource);
|
|
1882
|
-
const language = documentProofingLanguageForScript(languages, slot);
|
|
1883
|
-
return {
|
|
1884
|
-
...serialized ? {
|
|
1885
|
-
[DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE]: serialized
|
|
1886
|
-
} : {},
|
|
1887
|
-
...null === noProof ? {} : {
|
|
1888
|
-
[DOCUMENT_NO_PROOF_ATTRIBUTE]: String(noProof)
|
|
1889
|
-
},
|
|
1890
|
-
...slot ? {
|
|
1891
|
-
[DOCUMENT_SCRIPT_FONT_SLOT_ATTRIBUTE]: slot
|
|
1753
|
+
function serializeDocumentRunBorder(source) {
|
|
1754
|
+
const border = normalizeDocumentRunBorder(source);
|
|
1755
|
+
if (!border) return;
|
|
1756
|
+
const theme = serializeDocxThemeReference(border.color?.theme ?? null);
|
|
1757
|
+
return JSON.stringify({
|
|
1758
|
+
style: border.style,
|
|
1759
|
+
...border.color ? {
|
|
1760
|
+
color: {
|
|
1761
|
+
value: border.color.value,
|
|
1762
|
+
...theme ? {
|
|
1763
|
+
theme: JSON.parse(theme)
|
|
1764
|
+
} : {}
|
|
1765
|
+
}
|
|
1892
1766
|
} : {},
|
|
1893
|
-
...
|
|
1894
|
-
|
|
1767
|
+
...void 0 !== border.size ? {
|
|
1768
|
+
size: border.size
|
|
1895
1769
|
} : {},
|
|
1896
|
-
...
|
|
1897
|
-
|
|
1898
|
-
} : {}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
};
|
|
1906
|
-
for (const slot of PROOFING_LANGUAGE_ORDER){
|
|
1907
|
-
const value = patch[slot];
|
|
1908
|
-
if (void 0 === value) continue;
|
|
1909
|
-
if (null === value) {
|
|
1910
|
-
delete next[slot];
|
|
1911
|
-
continue;
|
|
1912
|
-
}
|
|
1913
|
-
const language = normalizeDocumentLanguageTag(value);
|
|
1914
|
-
if (!language) return null;
|
|
1915
|
-
next[slot] = language;
|
|
1916
|
-
}
|
|
1917
|
-
return Object.keys(next).length ? next : null;
|
|
1770
|
+
...void 0 !== border.space ? {
|
|
1771
|
+
space: border.space
|
|
1772
|
+
} : {},
|
|
1773
|
+
...void 0 !== border.shadow ? {
|
|
1774
|
+
shadow: border.shadow
|
|
1775
|
+
} : {},
|
|
1776
|
+
...void 0 !== border.frame ? {
|
|
1777
|
+
frame: border.frame
|
|
1778
|
+
} : {}
|
|
1779
|
+
});
|
|
1918
1780
|
}
|
|
1919
|
-
function
|
|
1920
|
-
|
|
1781
|
+
function parseDocumentRunBorderElement(element) {
|
|
1782
|
+
const semantic = parseDocumentRunBorder(element.getAttribute(DOCUMENT_RUN_BORDER_ATTRIBUTE));
|
|
1783
|
+
return semantic ?? documentRunBorderFromCss(element);
|
|
1921
1784
|
}
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
function normalizeDocumentIndexEntryDraft(source) {
|
|
1935
|
-
const mainEntry = normalizedIndexTerm(source?.mainEntry);
|
|
1936
|
-
if (!mainEntry) return null;
|
|
1937
|
-
const subEntry = normalizedIndexTerm(source?.subEntry);
|
|
1938
|
-
const crossReference = normalizedIndexTerm(source?.crossReference);
|
|
1785
|
+
function documentRunBorderDomAttributes(source) {
|
|
1786
|
+
const border = normalizeDocumentRunBorder(source);
|
|
1787
|
+
const serialized = serializeDocumentRunBorder(border);
|
|
1788
|
+
if (!border || !serialized) return {};
|
|
1789
|
+
const presentation = documentBorderPresentation(border);
|
|
1790
|
+
const declarations = [
|
|
1791
|
+
`border: ${work_document_run_border_formatPixels(presentation.width)}px ${presentation.style} ${presentation.color}`,
|
|
1792
|
+
`padding: ${work_document_run_border_formatPixels((border.space ?? 0) * work_document_run_border_POINTS_TO_PIXELS)}px`,
|
|
1793
|
+
'box-decoration-break: clone',
|
|
1794
|
+
'-webkit-box-decoration-break: clone'
|
|
1795
|
+
];
|
|
1796
|
+
if (border.shadow && presentation.width > 0) declarations.push(`box-shadow: 2px 2px 0 ${presentation.color}`);
|
|
1939
1797
|
return {
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
crossReference,
|
|
1943
|
-
pageBold: !crossReference && Boolean(source?.pageBold),
|
|
1944
|
-
pageItalic: !crossReference && Boolean(source?.pageItalic)
|
|
1798
|
+
[DOCUMENT_RUN_BORDER_ATTRIBUTE]: serialized,
|
|
1799
|
+
style: declarations.join('; ')
|
|
1945
1800
|
};
|
|
1946
1801
|
}
|
|
1947
|
-
function
|
|
1948
|
-
const
|
|
1949
|
-
|
|
1950
|
-
return {
|
|
1951
|
-
id: validIndexId(source?.id) ?? fallbackId,
|
|
1952
|
-
...value
|
|
1953
|
-
};
|
|
1802
|
+
function documentRunBorderIsVisible(source) {
|
|
1803
|
+
const border = normalizeDocumentRunBorder(source);
|
|
1804
|
+
return Boolean(border && documentBorderPresentation(border).width > 0);
|
|
1954
1805
|
}
|
|
1955
|
-
function
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
leader: indexLeader(source?.leader) ?? 'dot'
|
|
1806
|
+
function documentRunBorderFromCss(element) {
|
|
1807
|
+
const style = element.style.borderStyle.trim();
|
|
1808
|
+
if (!style) return null;
|
|
1809
|
+
if ('none' === style || 'hidden' === style) return {
|
|
1810
|
+
style: 'none'
|
|
1961
1811
|
};
|
|
1812
|
+
const width = Number.parseFloat(element.style.borderWidth);
|
|
1813
|
+
const color = normalizeCssColor(element.style.borderColor);
|
|
1814
|
+
if (!Number.isFinite(width) || width <= 0 || !color || 'transparent' === color) return null;
|
|
1815
|
+
const padding = Number.parseFloat(element.style.padding);
|
|
1816
|
+
return normalizeDocumentRunBorder({
|
|
1817
|
+
style: cssBorderStyle(style),
|
|
1818
|
+
color: {
|
|
1819
|
+
value: color
|
|
1820
|
+
},
|
|
1821
|
+
size: Math.max(2, Math.min(96, Math.round(6 * width))),
|
|
1822
|
+
...Number.isFinite(padding) && padding >= 0 ? {
|
|
1823
|
+
space: Math.max(0, Math.min(31, Math.round(padding / work_document_run_border_POINTS_TO_PIXELS)))
|
|
1824
|
+
} : {}
|
|
1825
|
+
});
|
|
1962
1826
|
}
|
|
1963
|
-
function
|
|
1827
|
+
function cssBorderStyle(style) {
|
|
1828
|
+
if ('double' === style) return 'double';
|
|
1829
|
+
if ('dashed' === style) return 'dashed';
|
|
1830
|
+
if ('dotted' === style) return 'dotted';
|
|
1831
|
+
if ('inset' === style || 'groove' === style) return 'inset';
|
|
1832
|
+
if ('outset' === style || 'ridge' === style) return 'outset';
|
|
1833
|
+
return 'single';
|
|
1834
|
+
}
|
|
1835
|
+
function work_document_run_border_formatPixels(value) {
|
|
1836
|
+
return Number(value.toFixed(3)).toString();
|
|
1837
|
+
}
|
|
1838
|
+
const work_document_paragraph_shading_DOCUMENT_PARAGRAPH_SHADING_PATTERNS = new Set([
|
|
1839
|
+
'nil',
|
|
1840
|
+
'clear',
|
|
1841
|
+
'solid',
|
|
1842
|
+
'horzStripe',
|
|
1843
|
+
'vertStripe',
|
|
1844
|
+
'reverseDiagStripe',
|
|
1845
|
+
'diagStripe',
|
|
1846
|
+
'horzCross',
|
|
1847
|
+
'diagCross',
|
|
1848
|
+
'thinHorzStripe',
|
|
1849
|
+
'thinVertStripe',
|
|
1850
|
+
'thinReverseDiagStripe',
|
|
1851
|
+
'thinDiagStripe',
|
|
1852
|
+
'thinHorzCross',
|
|
1853
|
+
'thinDiagCross',
|
|
1854
|
+
'pct5',
|
|
1855
|
+
'pct10',
|
|
1856
|
+
'pct12',
|
|
1857
|
+
'pct15',
|
|
1858
|
+
'pct20',
|
|
1859
|
+
'pct25',
|
|
1860
|
+
'pct30',
|
|
1861
|
+
'pct35',
|
|
1862
|
+
'pct37',
|
|
1863
|
+
'pct40',
|
|
1864
|
+
'pct45',
|
|
1865
|
+
'pct50',
|
|
1866
|
+
'pct55',
|
|
1867
|
+
'pct60',
|
|
1868
|
+
'pct62',
|
|
1869
|
+
'pct65',
|
|
1870
|
+
'pct70',
|
|
1871
|
+
'pct75',
|
|
1872
|
+
'pct80',
|
|
1873
|
+
'pct85',
|
|
1874
|
+
'pct87',
|
|
1875
|
+
'pct90',
|
|
1876
|
+
'pct95'
|
|
1877
|
+
]);
|
|
1878
|
+
function normalizeDocumentParagraphShading(source) {
|
|
1879
|
+
if (!source || 'object' != typeof source) return null;
|
|
1880
|
+
const value = source;
|
|
1881
|
+
const pattern = value.pattern;
|
|
1882
|
+
if ('string' != typeof pattern || !work_document_paragraph_shading_DOCUMENT_PARAGRAPH_SHADING_PATTERNS.has(pattern)) return null;
|
|
1883
|
+
const color = normalizeShadingColor(value.color);
|
|
1884
|
+
const fill = normalizeShadingColor(value.fill);
|
|
1885
|
+
if (void 0 !== value.color && !color || void 0 !== value.fill && !fill) return null;
|
|
1964
1886
|
return {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1887
|
+
pattern: pattern,
|
|
1888
|
+
...color ? {
|
|
1889
|
+
color
|
|
1890
|
+
} : {},
|
|
1891
|
+
...fill ? {
|
|
1892
|
+
fill
|
|
1893
|
+
} : {}
|
|
1969
1894
|
};
|
|
1970
1895
|
}
|
|
1971
|
-
function
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1896
|
+
function parseDocumentParagraphShading(source) {
|
|
1897
|
+
if ('string' != typeof source) return normalizeDocumentParagraphShading(source);
|
|
1898
|
+
if (!source.trim()) return null;
|
|
1899
|
+
try {
|
|
1900
|
+
return normalizeDocumentParagraphShading(JSON.parse(source));
|
|
1901
|
+
} catch {
|
|
1902
|
+
return null;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
function serializeDocumentParagraphShading(source) {
|
|
1906
|
+
const shading = normalizeDocumentParagraphShading(source);
|
|
1907
|
+
if (!shading) return;
|
|
1908
|
+
return JSON.stringify({
|
|
1909
|
+
pattern: shading.pattern,
|
|
1910
|
+
...shading.color ? {
|
|
1911
|
+
color: serializedShadingColor(shading.color)
|
|
1912
|
+
} : {},
|
|
1913
|
+
...shading.fill ? {
|
|
1914
|
+
fill: serializedShadingColor(shading.fill)
|
|
1915
|
+
} : {}
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
function parseDocumentParagraphShadingElement(element) {
|
|
1919
|
+
const semantic = parseDocumentParagraphShading(element.dataset.officeParagraphShading);
|
|
1920
|
+
const background = normalizeCssColor(element.style.backgroundColor);
|
|
1921
|
+
if (semantic) {
|
|
1922
|
+
const presentation = documentParagraphShadingPresentation(semantic);
|
|
1923
|
+
const expected = normalizeCssColor(presentation.backgroundColor);
|
|
1924
|
+
if (background && expected && background !== expected) {
|
|
1925
|
+
if ('transparent' === background) return {
|
|
1926
|
+
pattern: 'nil'
|
|
1927
|
+
};
|
|
1928
|
+
if (paragraphShadingBackgroundUsesForeground(semantic)) return {
|
|
1929
|
+
...semantic,
|
|
1930
|
+
color: {
|
|
1931
|
+
value: background
|
|
1932
|
+
}
|
|
1933
|
+
};
|
|
1934
|
+
return {
|
|
1935
|
+
...semantic,
|
|
1936
|
+
fill: {
|
|
1937
|
+
value: background
|
|
1938
|
+
}
|
|
1991
1939
|
};
|
|
1992
|
-
grouped.set(key, group);
|
|
1993
1940
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
if (!existing.targetIds.includes(marker.id)) existing.targetIds.push(marker.id);
|
|
2001
|
-
return;
|
|
1941
|
+
return semantic;
|
|
1942
|
+
}
|
|
1943
|
+
return background && 'transparent' !== background ? {
|
|
1944
|
+
pattern: 'clear',
|
|
1945
|
+
fill: {
|
|
1946
|
+
value: background
|
|
2002
1947
|
}
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
usage: 'sort'
|
|
2016
|
-
});
|
|
2017
|
-
const allEntries = Array.from(grouped.values()).map(({ entry, pages })=>({
|
|
2018
|
-
...entry,
|
|
2019
|
-
pages: Array.from(pages.values()).sort((left, right)=>left.pageNumber - right.pageNumber)
|
|
2020
|
-
})).sort((left, right)=>collator.compare(left.mainEntry, right.mainEntry) || collator.compare(left.subEntry, right.subEntry) || collator.compare(left.crossReference, right.crossReference));
|
|
1948
|
+
} : null;
|
|
1949
|
+
}
|
|
1950
|
+
function documentParagraphShadingDomAttributes(source) {
|
|
1951
|
+
const shading = normalizeDocumentParagraphShading(source);
|
|
1952
|
+
const serialized = serializeDocumentParagraphShading(shading);
|
|
1953
|
+
if (!shading || !serialized) return {};
|
|
1954
|
+
const presentation = documentParagraphShadingPresentation(shading);
|
|
1955
|
+
const styles = [
|
|
1956
|
+
`background-color: ${presentation.backgroundColor}`,
|
|
1957
|
+
presentation.backgroundImage ? `background-image: ${presentation.backgroundImage}` : '',
|
|
1958
|
+
presentation.backgroundSize ? `background-size: ${presentation.backgroundSize}` : ''
|
|
1959
|
+
].filter(Boolean);
|
|
2021
1960
|
return {
|
|
2022
|
-
|
|
2023
|
-
|
|
1961
|
+
'data-office-paragraph-shading': serialized,
|
|
1962
|
+
style: styles.join('; ')
|
|
2024
1963
|
};
|
|
2025
1964
|
}
|
|
2026
|
-
function
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
const
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
'<li class="work-document-index-empty">没有已标记的索引项</li>'
|
|
2041
|
-
];
|
|
2042
|
-
const status = value.truncated ? `仅显示前 ${MAX_DOCUMENT_INDEX_ENTRIES} 项` : `${value.entries.length} 项`;
|
|
2043
|
-
return [
|
|
2044
|
-
`<div data-document-index="true" data-index-id="${escapeHtmlAttribute(value.id)}" data-index-columns="${value.options.columns}" data-index-format="${value.options.format}" data-index-right-align-page-numbers="${String(value.options.rightAlignPageNumbers)}" data-index-leader="${value.options.leader}" data-index-entries="${escapeHtmlAttribute(JSON.stringify(value.entries))}" data-index-truncated="${String(value.truncated)}" class="work-document-index" contenteditable="false" aria-label="索引">`,
|
|
2045
|
-
`<div class="work-document-index-header"><strong>索引</strong><span>${status}</span></div>`,
|
|
2046
|
-
'<ol class="work-document-index-list">',
|
|
2047
|
-
...rows,
|
|
2048
|
-
'</ol></div>'
|
|
2049
|
-
].join('');
|
|
2050
|
-
}
|
|
2051
|
-
function documentIndexEntryFromElement(element, index = 1) {
|
|
2052
|
-
return normalizeDocumentIndexEntry({
|
|
2053
|
-
id: element.dataset.indexEntryId,
|
|
2054
|
-
mainEntry: element.dataset.indexMainEntry,
|
|
2055
|
-
subEntry: element.dataset.indexSubEntry,
|
|
2056
|
-
crossReference: element.dataset.indexCrossReference,
|
|
2057
|
-
pageBold: 'true' === element.dataset.indexPageBold,
|
|
2058
|
-
pageItalic: 'true' === element.dataset.indexPageItalic
|
|
2059
|
-
}, `index-entry-${index}`);
|
|
1965
|
+
function normalizeShadingColor(source) {
|
|
1966
|
+
if (!source || 'object' != typeof source) return null;
|
|
1967
|
+
const value = source;
|
|
1968
|
+
const theme = parseDocxThemeReference('string' == typeof value.theme ? value.theme : value.theme ? JSON.stringify(value.theme) : void 0);
|
|
1969
|
+
const direct = 'auto' === value.value ? 'auto' : 'string' == typeof value.value ? normalizeCssColor(value.value) : null;
|
|
1970
|
+
const resolved = direct ?? theme?.resolved ?? null;
|
|
1971
|
+
if (!resolved || 'transparent' === resolved) return null;
|
|
1972
|
+
if (theme && resolved !== theme.resolved) return null;
|
|
1973
|
+
return {
|
|
1974
|
+
value: resolved,
|
|
1975
|
+
...theme ? {
|
|
1976
|
+
theme
|
|
1977
|
+
} : {}
|
|
1978
|
+
};
|
|
2060
1979
|
}
|
|
2061
|
-
function
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
},
|
|
2070
|
-
entries: parseGeneratedEntries(element.dataset.indexEntries),
|
|
2071
|
-
truncated: 'true' === element.dataset.indexTruncated
|
|
2072
|
-
});
|
|
1980
|
+
function serializedShadingColor(color) {
|
|
1981
|
+
const theme = serializeDocxThemeReference(color.theme ?? null);
|
|
1982
|
+
return {
|
|
1983
|
+
value: color.value,
|
|
1984
|
+
...theme ? {
|
|
1985
|
+
theme: JSON.parse(theme)
|
|
1986
|
+
} : {}
|
|
1987
|
+
};
|
|
2073
1988
|
}
|
|
2074
|
-
function
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
}
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
1989
|
+
function documentParagraphShadingPresentation(shading) {
|
|
1990
|
+
if ('nil' === shading.pattern) return {
|
|
1991
|
+
backgroundColor: 'transparent'
|
|
1992
|
+
};
|
|
1993
|
+
const foreground = shadingColor(shading.color, '#000000');
|
|
1994
|
+
const background = shadingColor(shading.fill, 'transparent');
|
|
1995
|
+
if ('clear' === shading.pattern) return {
|
|
1996
|
+
backgroundColor: background
|
|
1997
|
+
};
|
|
1998
|
+
if ('solid' === shading.pattern) return {
|
|
1999
|
+
backgroundColor: foreground
|
|
2000
|
+
};
|
|
2001
|
+
const percentage = shadingPercentage(shading.pattern);
|
|
2002
|
+
if (null !== percentage) {
|
|
2003
|
+
const inverted = percentage > 50;
|
|
2004
|
+
const dotColor = inverted ? background : foreground;
|
|
2005
|
+
const baseColor = inverted ? foreground : background;
|
|
2006
|
+
const density = Math.min(50, inverted ? 100 - percentage : percentage);
|
|
2007
|
+
const spacing = Math.max(2, Math.round(9 - density / 7));
|
|
2008
|
+
return {
|
|
2009
|
+
backgroundColor: baseColor,
|
|
2010
|
+
backgroundImage: `radial-gradient(circle, ${dotColor} 0 1px, transparent 1.2px)`,
|
|
2011
|
+
backgroundSize: `${spacing}px ${spacing}px`
|
|
2012
|
+
};
|
|
2091
2013
|
}
|
|
2092
|
-
|
|
2014
|
+
const thin = shading.pattern.startsWith('thin');
|
|
2015
|
+
const width = thin ? 1 : 2;
|
|
2016
|
+
const period = thin ? 7 : 6;
|
|
2017
|
+
const stripe = (angle)=>`repeating-linear-gradient(${angle}deg, ${foreground} 0 ${width}px, transparent ${width}px ${period}px)`;
|
|
2018
|
+
const angles = shadingPatternAngles(shading.pattern);
|
|
2019
|
+
return {
|
|
2020
|
+
backgroundColor: background,
|
|
2021
|
+
backgroundImage: angles.map(stripe).join(', ')
|
|
2022
|
+
};
|
|
2093
2023
|
}
|
|
2094
|
-
function
|
|
2095
|
-
return
|
|
2024
|
+
function shadingColor(color, fallback) {
|
|
2025
|
+
return color && 'auto' !== color.value ? color.value : fallback;
|
|
2096
2026
|
}
|
|
2097
|
-
function
|
|
2098
|
-
const
|
|
2099
|
-
|
|
2100
|
-
const target = page.targetIds[0] ?? '';
|
|
2101
|
-
const classes = [
|
|
2102
|
-
page.pageBold ? 'bold' : '',
|
|
2103
|
-
page.pageItalic ? 'italic' : ''
|
|
2104
|
-
].filter(Boolean).join(' ');
|
|
2105
|
-
return `<a href="#${escapeHtmlAttribute(target)}" data-index-target="${escapeHtmlAttribute(target)}" tabindex="-1"${classes ? ` class="${classes}"` : ''}>${page.pageNumber}</a>`;
|
|
2106
|
-
}).join('<span aria-hidden="true">, </span>');
|
|
2107
|
-
return `<li data-index-main-entry="${escapeHtmlAttribute(entry.mainEntry)}" data-index-sub-entry="${escapeHtmlAttribute(entry.subEntry)}">${term}<i aria-hidden="true"></i><span class="work-document-index-pages">${pages}</span></li>`;
|
|
2027
|
+
function shadingPercentage(pattern) {
|
|
2028
|
+
const match = /^pct(\d+)$/.exec(pattern);
|
|
2029
|
+
return match?.[1] ? Number(match[1]) : null;
|
|
2108
2030
|
}
|
|
2109
|
-
function
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
const candidate = item;
|
|
2114
|
-
const draft = normalizeDocumentIndexEntryDraft({
|
|
2115
|
-
mainEntry: normalizedIndexTerm(candidate.mainEntry),
|
|
2116
|
-
subEntry: normalizedIndexTerm(candidate.subEntry),
|
|
2117
|
-
crossReference: normalizedIndexTerm(candidate.crossReference)
|
|
2118
|
-
});
|
|
2119
|
-
if (draft) entries.push({
|
|
2120
|
-
mainEntry: draft.mainEntry,
|
|
2121
|
-
subEntry: draft.subEntry,
|
|
2122
|
-
crossReference: draft.crossReference,
|
|
2123
|
-
pages: draft.crossReference ? [] : normalizeIndexPages(candidate.pages)
|
|
2124
|
-
});
|
|
2125
|
-
}
|
|
2126
|
-
return entries;
|
|
2031
|
+
function paragraphShadingBackgroundUsesForeground(shading) {
|
|
2032
|
+
if ('solid' === shading.pattern) return true;
|
|
2033
|
+
const percentage = shadingPercentage(shading.pattern);
|
|
2034
|
+
return null !== percentage && percentage > 50;
|
|
2127
2035
|
}
|
|
2128
|
-
function
|
|
2129
|
-
if (
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2036
|
+
function shadingPatternAngles(pattern) {
|
|
2037
|
+
if (pattern.includes('HorzCross')) return [
|
|
2038
|
+
0,
|
|
2039
|
+
90
|
|
2040
|
+
];
|
|
2041
|
+
if (pattern.includes('DiagCross')) return [
|
|
2042
|
+
45,
|
|
2043
|
+
-45
|
|
2044
|
+
];
|
|
2045
|
+
if (pattern.includes('VertStripe')) return [
|
|
2046
|
+
90
|
|
2047
|
+
];
|
|
2048
|
+
if (pattern.includes('ReverseDiagStripe')) return [
|
|
2049
|
+
-45
|
|
2050
|
+
];
|
|
2051
|
+
if (pattern.includes('DiagStripe')) return [
|
|
2052
|
+
45
|
|
2053
|
+
];
|
|
2054
|
+
return [
|
|
2055
|
+
0
|
|
2056
|
+
];
|
|
2057
|
+
}
|
|
2058
|
+
const DOCUMENT_HIGHLIGHT_ATTRIBUTE = 'data-office-highlight';
|
|
2059
|
+
const DOCUMENT_HIGHLIGHT_VALUES = new Set([
|
|
2060
|
+
'black',
|
|
2061
|
+
'blue',
|
|
2062
|
+
'cyan',
|
|
2063
|
+
'darkBlue',
|
|
2064
|
+
'darkCyan',
|
|
2065
|
+
'darkGray',
|
|
2066
|
+
'darkGreen',
|
|
2067
|
+
'darkMagenta',
|
|
2068
|
+
'darkRed',
|
|
2069
|
+
'darkYellow',
|
|
2070
|
+
'green',
|
|
2071
|
+
'lightGray',
|
|
2072
|
+
'magenta',
|
|
2073
|
+
'none',
|
|
2074
|
+
'red',
|
|
2075
|
+
'white',
|
|
2076
|
+
'yellow'
|
|
2077
|
+
]);
|
|
2078
|
+
const HIGHLIGHT_COLORS = {
|
|
2079
|
+
black: '#000000',
|
|
2080
|
+
blue: '#0000ff',
|
|
2081
|
+
cyan: '#00ffff',
|
|
2082
|
+
darkBlue: '#000080',
|
|
2083
|
+
darkCyan: '#008080',
|
|
2084
|
+
darkGray: '#808080',
|
|
2085
|
+
darkGreen: '#008000',
|
|
2086
|
+
darkMagenta: '#800080',
|
|
2087
|
+
darkRed: '#800000',
|
|
2088
|
+
darkYellow: '#808000',
|
|
2089
|
+
green: '#00ff00',
|
|
2090
|
+
lightGray: '#c0c0c0',
|
|
2091
|
+
magenta: '#ff00ff',
|
|
2092
|
+
none: 'transparent',
|
|
2093
|
+
red: '#ff0000',
|
|
2094
|
+
white: '#ffffff',
|
|
2095
|
+
yellow: '#ffff00'
|
|
2096
|
+
};
|
|
2097
|
+
function normalizeDocumentHighlight(source) {
|
|
2098
|
+
return 'string' == typeof source && DOCUMENT_HIGHLIGHT_VALUES.has(source) ? source : null;
|
|
2151
2099
|
}
|
|
2152
|
-
function
|
|
2153
|
-
if (
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
} catch {
|
|
2158
|
-
return [];
|
|
2159
|
-
}
|
|
2100
|
+
function documentHighlightFromDocxValue(source) {
|
|
2101
|
+
if ('string' != typeof source) return null;
|
|
2102
|
+
const normalized = source.trim().toLowerCase();
|
|
2103
|
+
for (const value of DOCUMENT_HIGHLIGHT_VALUES)if (value.toLowerCase() === normalized) return value;
|
|
2104
|
+
return null;
|
|
2160
2105
|
}
|
|
2161
|
-
function
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
if (offset >= position) return false;
|
|
2165
|
-
if ('pageBreak' === node.type.name) pageNumber += 1;
|
|
2166
|
-
return true;
|
|
2167
|
-
});
|
|
2168
|
-
return pageNumber;
|
|
2106
|
+
function documentHighlightCssColor(source) {
|
|
2107
|
+
const value = normalizeDocumentHighlight(source);
|
|
2108
|
+
return value ? HIGHLIGHT_COLORS[value] : null;
|
|
2169
2109
|
}
|
|
2170
|
-
function
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
].map((value)=>value.normalize('NFKC').toLocaleLowerCase()).join('\u0000');
|
|
2110
|
+
function documentHighlightForCssColor(source) {
|
|
2111
|
+
const color = normalizeCssColor('string' == typeof source ? source : null);
|
|
2112
|
+
if (!color) return null;
|
|
2113
|
+
for (const [value, candidate] of Object.entries(HIGHLIGHT_COLORS))if (candidate === color) return value;
|
|
2114
|
+
return null;
|
|
2176
2115
|
}
|
|
2177
|
-
function
|
|
2178
|
-
|
|
2179
|
-
return entry.crossReference ? `${term} · 参见 ${entry.crossReference}` : term;
|
|
2116
|
+
function documentHighlightFromElement(element) {
|
|
2117
|
+
return normalizeDocumentHighlight(element.getAttribute(DOCUMENT_HIGHLIGHT_ATTRIBUTE)) ?? documentHighlightForCssColor(element.style.backgroundColor);
|
|
2180
2118
|
}
|
|
2181
|
-
function
|
|
2182
|
-
|
|
2119
|
+
function documentHighlightDomAttributes(source) {
|
|
2120
|
+
const value = normalizeDocumentHighlight(source);
|
|
2121
|
+
const color = documentHighlightCssColor(value);
|
|
2122
|
+
return value && color ? {
|
|
2123
|
+
[DOCUMENT_HIGHLIGHT_ATTRIBUTE]: value,
|
|
2124
|
+
style: `background-color: ${color}`
|
|
2125
|
+
} : {};
|
|
2183
2126
|
}
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2127
|
+
const DOCUMENT_RUN_SHADING_ATTRIBUTE = 'data-office-run-shading';
|
|
2128
|
+
const MAX_SERIALIZED_RUN_SHADING_BYTES = 4096;
|
|
2129
|
+
const RUN_SHADING_KEYS = new Set([
|
|
2130
|
+
'pattern',
|
|
2131
|
+
'color',
|
|
2132
|
+
'fill'
|
|
2133
|
+
]);
|
|
2134
|
+
const RUN_SHADING_COLOR_KEYS = new Set([
|
|
2135
|
+
'value',
|
|
2136
|
+
'theme'
|
|
2137
|
+
]);
|
|
2138
|
+
const THEME_REFERENCE_KEYS = new Set([
|
|
2139
|
+
'theme',
|
|
2140
|
+
'resolved',
|
|
2141
|
+
'tint',
|
|
2142
|
+
'shade'
|
|
2143
|
+
]);
|
|
2144
|
+
function normalizeDocumentRunShading(source) {
|
|
2145
|
+
if (!work_document_run_shading_isRecordWithKeys(source, RUN_SHADING_KEYS)) return null;
|
|
2146
|
+
for (const name of [
|
|
2147
|
+
'color',
|
|
2148
|
+
'fill'
|
|
2149
|
+
]){
|
|
2150
|
+
const color = source[name];
|
|
2151
|
+
if (void 0 !== color) {
|
|
2152
|
+
if (!work_document_run_shading_isRecordWithKeys(color, RUN_SHADING_COLOR_KEYS)) return null;
|
|
2153
|
+
if (void 0 !== color.theme && !work_document_run_shading_isRecordWithKeys(color.theme, THEME_REFERENCE_KEYS)) return null;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
return normalizeDocumentParagraphShading(source);
|
|
2187
2157
|
}
|
|
2188
|
-
function
|
|
2189
|
-
|
|
2190
|
-
|
|
2158
|
+
function parseDocumentRunShading(source) {
|
|
2159
|
+
if ('string' != typeof source) return normalizeDocumentRunShading(source);
|
|
2160
|
+
if (!source.trim() || source.length > MAX_SERIALIZED_RUN_SHADING_BYTES) return null;
|
|
2161
|
+
try {
|
|
2162
|
+
return normalizeDocumentRunShading(JSON.parse(source));
|
|
2163
|
+
} catch {
|
|
2164
|
+
return null;
|
|
2165
|
+
}
|
|
2191
2166
|
}
|
|
2192
|
-
function
|
|
2193
|
-
|
|
2167
|
+
function serializeDocumentRunShading(source) {
|
|
2168
|
+
const shading = normalizeDocumentRunShading(source);
|
|
2169
|
+
const serialized = shading ? serializeDocumentParagraphShading(shading) : void 0;
|
|
2170
|
+
return serialized && serialized.length <= MAX_SERIALIZED_RUN_SHADING_BYTES ? serialized : void 0;
|
|
2194
2171
|
}
|
|
2195
|
-
function
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2172
|
+
function parseDocumentRunShadingElement(element) {
|
|
2173
|
+
const semantic = parseDocumentRunShading(element.getAttribute(DOCUMENT_RUN_SHADING_ATTRIBUTE));
|
|
2174
|
+
if (!semantic) return null;
|
|
2175
|
+
if (element.hasAttribute(DOCUMENT_HIGHLIGHT_ATTRIBUTE)) return semantic;
|
|
2176
|
+
const background = normalizeCssColor(element.style.backgroundColor);
|
|
2177
|
+
const expected = normalizeCssColor(documentParagraphShadingPresentation(semantic).backgroundColor);
|
|
2178
|
+
if (!background || !expected || background === expected) return semantic;
|
|
2179
|
+
if ('transparent' === background) return {
|
|
2180
|
+
pattern: 'nil'
|
|
2181
|
+
};
|
|
2182
|
+
return paragraphShadingBackgroundUsesForeground(semantic) ? {
|
|
2183
|
+
...semantic,
|
|
2184
|
+
color: {
|
|
2185
|
+
value: background
|
|
2186
|
+
}
|
|
2187
|
+
} : {
|
|
2188
|
+
...semantic,
|
|
2189
|
+
fill: {
|
|
2190
|
+
value: background
|
|
2191
|
+
}
|
|
2192
|
+
};
|
|
2205
2193
|
}
|
|
2206
|
-
function
|
|
2207
|
-
|
|
2194
|
+
function documentRunShadingDomAttributes(source) {
|
|
2195
|
+
const shading = normalizeDocumentRunShading(source);
|
|
2196
|
+
const serialized = serializeDocumentRunShading(shading);
|
|
2197
|
+
if (!shading || !serialized) return {};
|
|
2198
|
+
const paragraphAttributes = documentParagraphShadingDomAttributes(shading);
|
|
2199
|
+
const style = [
|
|
2200
|
+
paragraphAttributes.style,
|
|
2201
|
+
'box-decoration-break: clone',
|
|
2202
|
+
'-webkit-box-decoration-break: clone'
|
|
2203
|
+
].filter(Boolean).join('; ');
|
|
2204
|
+
return {
|
|
2205
|
+
[DOCUMENT_RUN_SHADING_ATTRIBUTE]: serialized,
|
|
2206
|
+
style
|
|
2207
|
+
};
|
|
2208
2208
|
}
|
|
2209
|
-
function
|
|
2210
|
-
return
|
|
2209
|
+
function work_document_run_shading_isRecordWithKeys(source, allowed) {
|
|
2210
|
+
return 'object' == typeof source && null !== source && !Array.isArray(source) && Object.keys(source).every((key)=>allowed.has(key));
|
|
2211
2211
|
}
|
|
2212
2212
|
const DOCUMENT_PARAGRAPH_ID_ATTRIBUTE = 'data-office-paragraph-id';
|
|
2213
2213
|
const DOCUMENT_PARAGRAPH_TEXT_ID_ATTRIBUTE = 'data-office-paragraph-text-id';
|
|
@@ -2905,6 +2905,13 @@ const WORK_TEMPLATES = [
|
|
|
2905
2905
|
description: '下拉列表、输入提示与错误警告',
|
|
2906
2906
|
accent: '#13795b'
|
|
2907
2907
|
},
|
|
2908
|
+
{
|
|
2909
|
+
id: 'structured-references',
|
|
2910
|
+
kind: 'spreadsheet',
|
|
2911
|
+
name: '结构化引用',
|
|
2912
|
+
description: '表名、当前行公式、插入行自动填充与汇总行',
|
|
2913
|
+
accent: '#0f7b61'
|
|
2914
|
+
},
|
|
2908
2915
|
{
|
|
2909
2916
|
id: 'blank-presentation',
|
|
2910
2917
|
kind: 'presentation',
|
|
@@ -2958,6 +2965,7 @@ function initialTitle(templateId, kind) {
|
|
|
2958
2965
|
'proofing-languages': '校对语言示例',
|
|
2959
2966
|
'quarterly-plan': '季度执行计划',
|
|
2960
2967
|
'data-validation': '数据验证示例',
|
|
2968
|
+
'structured-references': '结构化引用示例',
|
|
2961
2969
|
'strategy-deck': '业务策略汇报',
|
|
2962
2970
|
'animated-deck': '入场动画示例'
|
|
2963
2971
|
};
|
|
@@ -3257,6 +3265,10 @@ function contentForTemplate(templateId) {
|
|
|
3257
3265
|
type: 'spreadsheet',
|
|
3258
3266
|
sheets: dataValidationTemplateSheets()
|
|
3259
3267
|
};
|
|
3268
|
+
if ('structured-references' === templateId) return {
|
|
3269
|
+
type: 'spreadsheet',
|
|
3270
|
+
sheets: structuredReferenceTemplateSheets()
|
|
3271
|
+
};
|
|
3260
3272
|
if ('strategy-deck' === templateId) return strategyPresentation();
|
|
3261
3273
|
if ('animated-deck' === templateId) return animatedPresentation();
|
|
3262
3274
|
if ('blank-spreadsheet' === templateId) return {
|
|
@@ -3609,6 +3621,254 @@ function dataValidationTemplateSheets() {
|
|
|
3609
3621
|
}
|
|
3610
3622
|
];
|
|
3611
3623
|
}
|
|
3624
|
+
function structuredReferenceTemplateSheets() {
|
|
3625
|
+
const sales = emptyMatrix(16, 10);
|
|
3626
|
+
sales[0][0] = styledCell('Sales · 结构化引用', {
|
|
3627
|
+
bl: 1,
|
|
3628
|
+
fs: 16,
|
|
3629
|
+
fc: '#ffffff',
|
|
3630
|
+
bg: '#0f7b61'
|
|
3631
|
+
});
|
|
3632
|
+
sales[1][0] = styledCell('插入表格正文行会自动补齐 Revenue;已填写的手工值不会覆盖。', {
|
|
3633
|
+
fc: '#49645c',
|
|
3634
|
+
fs: 10
|
|
3635
|
+
});
|
|
3636
|
+
[
|
|
3637
|
+
'Item',
|
|
3638
|
+
'Units',
|
|
3639
|
+
'Unit price',
|
|
3640
|
+
'Revenue'
|
|
3641
|
+
].forEach((value, column)=>{
|
|
3642
|
+
sales[2][column] = headerCell(value);
|
|
3643
|
+
});
|
|
3644
|
+
const rows = [
|
|
3645
|
+
[
|
|
3646
|
+
'Landing page',
|
|
3647
|
+
12,
|
|
3648
|
+
48,
|
|
3649
|
+
'=[@Units]*[@[Unit price]]'
|
|
3650
|
+
],
|
|
3651
|
+
[
|
|
3652
|
+
'API integration',
|
|
3653
|
+
8,
|
|
3654
|
+
120,
|
|
3655
|
+
'=[@Units]*[@[Unit price]]'
|
|
3656
|
+
],
|
|
3657
|
+
[
|
|
3658
|
+
'QA review',
|
|
3659
|
+
16,
|
|
3660
|
+
36,
|
|
3661
|
+
'=[@Units]*[@[Unit price]]'
|
|
3662
|
+
],
|
|
3663
|
+
[
|
|
3664
|
+
'Release support',
|
|
3665
|
+
5,
|
|
3666
|
+
80,
|
|
3667
|
+
'=[@Units]*[@[Unit price]]'
|
|
3668
|
+
]
|
|
3669
|
+
];
|
|
3670
|
+
rows.forEach((row, rowIndex)=>{
|
|
3671
|
+
row.forEach((value, columnIndex)=>{
|
|
3672
|
+
sales[rowIndex + 3][columnIndex] = styledCell(value, {
|
|
3673
|
+
bg: rowIndex % 2 ? '#f3faf7' : '#ffffff',
|
|
3674
|
+
...columnIndex >= 1 ? {
|
|
3675
|
+
ct: {
|
|
3676
|
+
fa: 1 === columnIndex ? '0' : '#,##0.00',
|
|
3677
|
+
t: 'n'
|
|
3678
|
+
}
|
|
3679
|
+
} : {}
|
|
3680
|
+
});
|
|
3681
|
+
});
|
|
3682
|
+
});
|
|
3683
|
+
sales[7][0] = styledCell('Total', {
|
|
3684
|
+
bl: 1,
|
|
3685
|
+
fc: '#215446',
|
|
3686
|
+
bg: '#dff3ec'
|
|
3687
|
+
});
|
|
3688
|
+
sales[7][1] = styledCell('=SUBTOTAL(109,Sales[Units])', {
|
|
3689
|
+
bl: 1,
|
|
3690
|
+
fc: '#215446',
|
|
3691
|
+
bg: '#dff3ec',
|
|
3692
|
+
ct: {
|
|
3693
|
+
fa: '0',
|
|
3694
|
+
t: 'n'
|
|
3695
|
+
}
|
|
3696
|
+
});
|
|
3697
|
+
sales[7][3] = styledCell('=SUBTOTAL(109,Sales[Revenue])', {
|
|
3698
|
+
bl: 1,
|
|
3699
|
+
fc: '#215446',
|
|
3700
|
+
bg: '#dff3ec',
|
|
3701
|
+
ct: {
|
|
3702
|
+
fa: '#,##0.00',
|
|
3703
|
+
t: 'n'
|
|
3704
|
+
}
|
|
3705
|
+
});
|
|
3706
|
+
sales[9][0] = styledCell('Reference examples', {
|
|
3707
|
+
bl: 1,
|
|
3708
|
+
fc: '#215446'
|
|
3709
|
+
});
|
|
3710
|
+
sales[10][0] = styledCell('Headers count');
|
|
3711
|
+
sales[10][1] = styledCell('=COUNTA(Sales[#Headers])', {
|
|
3712
|
+
ct: {
|
|
3713
|
+
fa: '0',
|
|
3714
|
+
t: 'n'
|
|
3715
|
+
}
|
|
3716
|
+
});
|
|
3717
|
+
sales[11][0] = styledCell('Data revenue');
|
|
3718
|
+
sales[11][1] = styledCell('=SUM(Sales[Revenue])', {
|
|
3719
|
+
ct: {
|
|
3720
|
+
fa: '#,##0.00',
|
|
3721
|
+
t: 'n'
|
|
3722
|
+
}
|
|
3723
|
+
});
|
|
3724
|
+
sales[12][0] = styledCell('Units + prices');
|
|
3725
|
+
sales[12][1] = styledCell('=SUM(Sales[[Units]:[Unit price]])', {
|
|
3726
|
+
ct: {
|
|
3727
|
+
fa: '#,##0.00',
|
|
3728
|
+
t: 'n'
|
|
3729
|
+
}
|
|
3730
|
+
});
|
|
3731
|
+
sales[13][0] = styledCell('All table cells');
|
|
3732
|
+
sales[13][1] = styledCell('=COUNTA(Sales[#All])', {
|
|
3733
|
+
ct: {
|
|
3734
|
+
fa: '0',
|
|
3735
|
+
t: 'n'
|
|
3736
|
+
}
|
|
3737
|
+
});
|
|
3738
|
+
const table = {
|
|
3739
|
+
id: createWorkId('spreadsheet-table'),
|
|
3740
|
+
name: 'Sales',
|
|
3741
|
+
displayName: 'SalesData',
|
|
3742
|
+
range: {
|
|
3743
|
+
row: [
|
|
3744
|
+
2,
|
|
3745
|
+
7
|
|
3746
|
+
],
|
|
3747
|
+
column: [
|
|
3748
|
+
0,
|
|
3749
|
+
3
|
|
3750
|
+
]
|
|
3751
|
+
},
|
|
3752
|
+
columns: [
|
|
3753
|
+
{
|
|
3754
|
+
name: 'Item',
|
|
3755
|
+
totalsLabel: 'Total'
|
|
3756
|
+
},
|
|
3757
|
+
{
|
|
3758
|
+
name: 'Units',
|
|
3759
|
+
totalsFunction: 'sum'
|
|
3760
|
+
},
|
|
3761
|
+
{
|
|
3762
|
+
name: 'Unit price'
|
|
3763
|
+
},
|
|
3764
|
+
{
|
|
3765
|
+
name: 'Revenue',
|
|
3766
|
+
calculatedFormula: '=[@Units]*[@[Unit price]]',
|
|
3767
|
+
totalsFunction: 'sum'
|
|
3768
|
+
}
|
|
3769
|
+
],
|
|
3770
|
+
filters: [],
|
|
3771
|
+
headerRow: true,
|
|
3772
|
+
totalsRow: true,
|
|
3773
|
+
style: {
|
|
3774
|
+
family: 'medium',
|
|
3775
|
+
number: 4
|
|
3776
|
+
},
|
|
3777
|
+
showFirstColumn: false,
|
|
3778
|
+
showLastColumn: false,
|
|
3779
|
+
showRowStripes: true,
|
|
3780
|
+
showColumnStripes: false
|
|
3781
|
+
};
|
|
3782
|
+
const salesSheet = {
|
|
3783
|
+
id: createWorkId('sheet'),
|
|
3784
|
+
name: 'Sales',
|
|
3785
|
+
status: 1,
|
|
3786
|
+
order: 0,
|
|
3787
|
+
row: 16,
|
|
3788
|
+
column: 10,
|
|
3789
|
+
data: sales,
|
|
3790
|
+
tables: [
|
|
3791
|
+
table
|
|
3792
|
+
],
|
|
3793
|
+
config: {
|
|
3794
|
+
columnlen: {
|
|
3795
|
+
0: 172,
|
|
3796
|
+
1: 76,
|
|
3797
|
+
2: 102,
|
|
3798
|
+
3: 112
|
|
3799
|
+
},
|
|
3800
|
+
rowlen: {
|
|
3801
|
+
0: 32,
|
|
3802
|
+
1: 24,
|
|
3803
|
+
2: 28,
|
|
3804
|
+
7: 28
|
|
3805
|
+
},
|
|
3806
|
+
merge: {
|
|
3807
|
+
'0_0': {
|
|
3808
|
+
r: 0,
|
|
3809
|
+
c: 0,
|
|
3810
|
+
rs: 1,
|
|
3811
|
+
cs: 4
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
};
|
|
3816
|
+
const summary = emptyMatrix(10, 4);
|
|
3817
|
+
summary[0][0] = styledCell('Summary · qualified references', {
|
|
3818
|
+
bl: 1,
|
|
3819
|
+
fs: 16,
|
|
3820
|
+
fc: '#ffffff',
|
|
3821
|
+
bg: '#215446'
|
|
3822
|
+
});
|
|
3823
|
+
summary[2][0] = styledCell('Revenue from Sales table');
|
|
3824
|
+
summary[2][1] = styledCell('=SUM(Sales!Sales[Revenue])', {
|
|
3825
|
+
ct: {
|
|
3826
|
+
fa: '#,##0.00',
|
|
3827
|
+
t: 'n'
|
|
3828
|
+
}
|
|
3829
|
+
});
|
|
3830
|
+
summary[3][0] = styledCell('Headers from Sales table');
|
|
3831
|
+
summary[3][1] = styledCell('=COUNTA(Sales!Sales[#Headers])', {
|
|
3832
|
+
ct: {
|
|
3833
|
+
fa: '0',
|
|
3834
|
+
t: 'n'
|
|
3835
|
+
}
|
|
3836
|
+
});
|
|
3837
|
+
summary[5][0] = styledCell('Sales!Sales[...] demonstrates a worksheet-qualified table reference.', {
|
|
3838
|
+
fc: '#49645c',
|
|
3839
|
+
fs: 10
|
|
3840
|
+
});
|
|
3841
|
+
return [
|
|
3842
|
+
salesSheet,
|
|
3843
|
+
{
|
|
3844
|
+
id: createWorkId('sheet'),
|
|
3845
|
+
name: 'Summary',
|
|
3846
|
+
status: 0,
|
|
3847
|
+
order: 1,
|
|
3848
|
+
row: 10,
|
|
3849
|
+
column: 4,
|
|
3850
|
+
data: summary,
|
|
3851
|
+
config: {
|
|
3852
|
+
columnlen: {
|
|
3853
|
+
0: 240,
|
|
3854
|
+
1: 120
|
|
3855
|
+
},
|
|
3856
|
+
rowlen: {
|
|
3857
|
+
0: 32,
|
|
3858
|
+
5: 28
|
|
3859
|
+
},
|
|
3860
|
+
merge: {
|
|
3861
|
+
'0_0': {
|
|
3862
|
+
r: 0,
|
|
3863
|
+
c: 0,
|
|
3864
|
+
rs: 1,
|
|
3865
|
+
cs: 2
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
];
|
|
3871
|
+
}
|
|
3612
3872
|
function dataValidationTemplateItem(overrides) {
|
|
3613
3873
|
return {
|
|
3614
3874
|
type: 'dropdown',
|