@a3s-lab/office 0.47.0 → 0.48.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -309,6 +309,33 @@ and [CLI reference](./docs/latest/en/cli-reference.md).
309
309
 
310
310
  ## Current release
311
311
 
312
+ Version `0.48.1` fixes the Writer floating selection toolbar's split emphasis
313
+ controls without changing the document model or public command contract:
314
+
315
+ - Underline and strikethrough controls now reset browser-native button
316
+ appearance and use the same compact themed states as the surrounding
317
+ actions, including one-pixel grouping dividers and keyboard focus treatment.
318
+ - The fix is covered by a browser visual contract in both desktop and compact
319
+ viewports; formatting commands, accessibility names, and split menus remain
320
+ unchanged.
321
+
322
+ Version `0.48.0` adds bounded native Writer content controls without
323
+ introducing a second document model or a remote service:
324
+
325
+ - The Insert ribbon and responsive dialog author inline plain-text or rich-text
326
+ controls with aliases, program tags, multiline text, border/tag/hidden
327
+ appearance, and an optional theme color. Each accepted change is one typed
328
+ update and one Undo step, and the control exposes an accessible textbox name.
329
+ - Content and shell locks are enforced at the transaction boundary, including
330
+ direct editor transactions. Locked content cannot be changed accidentally;
331
+ typed unlock/delete commands remain explicit and auditable.
332
+ - Direct paragraph DOCX `w:sdt` controls round-trip through strict and
333
+ transitional WordprocessingML with collision-free native IDs, rich run
334
+ formatting, lock/multiline metadata, and Word 2012 appearance/color. Active
335
+ bindings, placeholders, repeating regions, form controls, nested or
336
+ relationship-bound structures remain safe editable text with compatibility
337
+ diagnostics instead of an inexact editable promise.
338
+
312
339
  Version `0.47.0` adds a bounded common-field workflow for Writer without
313
340
  introducing a second document model or a remote service:
314
341
 
@@ -1,6 +1,6 @@
1
1
  import { documentRunBorderDomAttributes, normalizeDocumentRunBorder, normalizeDocumentParagraphBorder, serializeDocumentRunBorder, serializeDocumentRunShading, documentIndexEntryHtml, attribute as work_ooxml_package_attribute, documentScriptFontFallbackSlots, documentScriptFontsDomAttributes, documentHighlightFromDocxValue, serializeDocxThemeReference, directChildren as work_ooxml_package_directChildren, documentParagraphShadingDomAttributes, documentTableOfContentsHtml, documentParagraphBordersDomAttributes, normalizeDocumentScriptFontHint, normalizeDocumentScriptFonts, DOCUMENT_PARAGRAPH_BORDER_STYLES, normalizeDocumentParagraphBorders as work_document_paragraph_borders_normalizeDocumentParagraphBorders, DOCUMENT_PARAGRAPH_BORDER_EDGES, documentScriptFontFamily, normalizeDocumentThemeFont, normalizeDocumentParagraphId, documentHighlightDomAttributes, descendants, documentIndexHtml, documentHighlightCssColor, xmlNamespacePrefix, normalizeDocumentFontName, documentProofingDomAttributes, work_document_paragraph_shading_DOCUMENT_PARAGRAPH_SHADING_PATTERNS, directChild, documentScriptFontSegments, childPath, parseDocumentTableOfContentsInstruction, documentRunShadingDomAttributes } from "./6968.js";
2
- import { docxThemeFont, XML_NAMESPACE as work_docx_settings_xml_XML_NAMESPACE, parseDocumentIndexEntryInstruction, resolveDocxProofing, parseDocumentIndexInstruction, DOCX_WORD_2010_NAMESPACE, XMLNS_NAMESPACE as work_docx_settings_xml_XMLNS_NAMESPACE, STRICT_WORDPROCESSING_NAMESPACE, docxCharacterSpacingTwipsFromProperties, parseBoundedDocxInteger, xmlAttributeLocalName, resolveDocxOpenTypeFeatures, docxCharacterScalePercentFromProperties, resolveDocxEmphasisMark, docxCharacterPositionHalfPointsFromProperties, docxThemeColor, parseDocxParagraphDefaultCollapsed, resolveDocxKerningThresholdHalfPoints, resolveDocxThemeResolver as work_docx_theme_resolveDocxThemeResolver, DOCX_WORDPROCESSING_NAMESPACES, xmlAttributeNamespace, parseDocxTwipsMeasure } from "./0~4042.js";
3
- import { normalizeDocumentEmphasisMark, normalizeDocumentPageBorders, textBoxCss, documentCharacterPositionDomAttributes, DOCUMENT_PAGE_BORDER_EDGES, normalizeDocumentTabStops, documentTextCaseFromWordFlags, documentStrikeDomAttributes, serializeDocumentNumberingChange, numberingTypeFromFormat, normalizeDocumentUnderlineColor, documentAutoLineHeight, documentKerningDomAttributes, documentCharacterScaleDomAttributes, textBoxDomAttributes, serializeDocumentParagraphFormatting, normalizeDocumentPaperSource, documentEquationText, documentLegacyTextEffectsDomAttributes, documentLegacyTextEffectsConflict, documentCharacterSpacingDomAttributes, normalizeDocumentParagraphIndent, normalizeDocumentEquation, documentOpenTypeDomAttributes, normalizeDocumentTextBoxProperties, renderDocumentAutoLineHeight, documentUnderlineDomAttributes, MAX_DOCUMENT_NUMBERING_START, normalizeDocumentUnderlineStyle, applyDocumentTextCaseStyle, documentHiddenTextDomAttributes, importedDocumentCharacterFormatting, DOCUMENT_LEGACY_TEXT_EFFECT_NAMES, createDocumentEquationElement, normalizeDocumentPageGeometry, serializeDocumentTabStops, documentWordLineHeightFactor, documentEmphasisMarkDomAttributes, normalizeDocumentPageMargins } from "./4174.js";
2
+ import { normalizeDocumentEmphasisMark, normalizeDocumentPageBorders, textBoxCss, documentCharacterPositionDomAttributes, DOCUMENT_PAGE_BORDER_EDGES, normalizeDocumentTabStops, documentTextCaseFromWordFlags, documentStrikeDomAttributes, serializeDocumentNumberingChange, numberingTypeFromFormat, normalizeDocumentUnderlineColor, documentAutoLineHeight, documentKerningDomAttributes, documentCharacterScaleDomAttributes, textBoxDomAttributes, serializeDocumentParagraphFormatting, normalizeDocumentContentControlProperties, documentEquationText, normalizeDocumentPaperSource, documentLegacyTextEffectsDomAttributes, documentLegacyTextEffectsConflict, documentCharacterSpacingDomAttributes, normalizeDocumentParagraphIndent, normalizeDocumentEquation, documentOpenTypeDomAttributes, normalizeDocumentTextBoxProperties, renderDocumentAutoLineHeight, contentControlDomAttributes, documentUnderlineDomAttributes, MAX_DOCUMENT_NUMBERING_START, normalizeDocumentUnderlineStyle, applyDocumentTextCaseStyle, documentHiddenTextDomAttributes, importedDocumentCharacterFormatting, DOCUMENT_LEGACY_TEXT_EFFECT_NAMES, normalizeDocumentPageGeometry, createDocumentEquationElement, serializeDocumentTabStops, documentEmphasisMarkDomAttributes, normalizeDocumentPageMargins, documentWordLineHeightFactor } from "./4174.js";
3
+ import { hasUnsupportedContentControlSemanticChild, docxThemeFont, hasContentControlRelationshipReference, XML_NAMESPACE as work_docx_settings_xml_XML_NAMESPACE, parseDocumentIndexEntryInstruction, resolveDocxProofing, parseDocumentIndexInstruction, CONTENT_CONTROL_WORD_2012_NAMESPACE, DOCX_WORD_2010_NAMESPACE, XMLNS_NAMESPACE as work_docx_settings_xml_XMLNS_NAMESPACE, STRICT_WORDPROCESSING_NAMESPACE, docxCharacterSpacingTwipsFromProperties, parseBoundedDocxInteger, isDocxWordElement, xmlAttributeLocalName, resolveDocxOpenTypeFeatures, resolveDocxEmphasisMark, docxCharacterScalePercentFromProperties, docxCharacterPositionHalfPointsFromProperties, docxThemeColor, parseDocxParagraphDefaultCollapsed, resolveDocxKerningThresholdHalfPoints, resolveDocxThemeResolver as work_docx_theme_resolveDocxThemeResolver, DOCX_WORDPROCESSING_NAMESPACES, xmlAttributeNamespace, parseDocxTwipsMeasure } from "./0~3534.js";
4
4
  function docxCaptionSequenceKind(instruction) {
5
5
  const identifier = /^\s*SEQ\s+([^\s\\]+)/i.exec(instruction)?.[1]?.toLowerCase();
6
6
  if (!identifier) return null;
@@ -158,6 +158,428 @@ function fieldRange(field, indexes) {
158
158
  function containsRange(outer, inner) {
159
159
  return outer.start >= 0 && inner.start >= 0 && outer.start < inner.start && outer.end >= inner.end;
160
160
  }
161
+ const WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
162
+ const STRICT_WORD_NAMESPACE = 'http://purl.oclc.org/ooxml/wordprocessingml/main';
163
+ const WORD_NAMESPACES = new Set([
164
+ WORD_NAMESPACE,
165
+ STRICT_WORD_NAMESPACE
166
+ ]);
167
+ const MARKUP_COMPATIBILITY_NAMESPACE = 'http://schemas.openxmlformats.org/markup-compatibility/2006';
168
+ const MAX_IMPORTED_CONTENT_CONTROLS = 4096;
169
+ const MAX_CONTENT_CONTROL_TEXT = 1000000;
170
+ const RUN_CONTENT_NAMES = new Set([
171
+ 'br',
172
+ 'cr',
173
+ 'noBreakHyphen',
174
+ 'softHyphen',
175
+ 'tab',
176
+ 't'
177
+ ]);
178
+ const PROPERTY_NAMES = new Set([
179
+ 'alias',
180
+ 'id',
181
+ 'lock',
182
+ 'richText',
183
+ 'tag',
184
+ 'text'
185
+ ]);
186
+ const LOCK_VALUES = new Set([
187
+ 'contentLocked',
188
+ 'sdtContentLocked',
189
+ 'sdtLocked',
190
+ 'unlocked'
191
+ ]);
192
+ const APPEARANCE_VALUES = new Set([
193
+ 'boundingBox',
194
+ 'hidden',
195
+ 'tags'
196
+ ]);
197
+ function inspectDocxContentControls(document) {
198
+ let supported = 0;
199
+ let unsupported = 0;
200
+ for (const control of descendants(document, 'sdt'))if (!hasContentControlAncestor(control)) {
201
+ if (!isStructuralContentControl(control)) if (isDirectParagraphControl(control) && readContentControl(control, 0)) supported += 1;
202
+ else unsupported += 1;
203
+ }
204
+ return {
205
+ supported,
206
+ unsupported
207
+ };
208
+ }
209
+ function markDocxContentControls(document) {
210
+ const controls = [];
211
+ let unsupported = 0;
212
+ const candidates = descendants(document, 'sdt').filter((control)=>!hasContentControlAncestor(control) && !isStructuralContentControl(control));
213
+ for (const [index, control] of candidates.entries()){
214
+ if (!isDirectParagraphControl(control)) {
215
+ unsupported += 1;
216
+ continue;
217
+ }
218
+ const properties = readContentControl(control, index + 1);
219
+ if (!properties) {
220
+ unsupported += 1;
221
+ continue;
222
+ }
223
+ if (controls.length >= MAX_IMPORTED_CONTENT_CONTROLS) throw new Error('Imported DOCX exceeds the content-control limit.');
224
+ const [start, end] = contentControlMarkers(document, controls.length + 1);
225
+ if (!replaceControlWithMarkers(document, control, start, end)) {
226
+ unsupported += 1;
227
+ continue;
228
+ }
229
+ controls.push({
230
+ start,
231
+ end,
232
+ properties
233
+ });
234
+ }
235
+ return {
236
+ controls,
237
+ unsupported
238
+ };
239
+ }
240
+ function contentControlMarkers(document, initialIndex) {
241
+ const occupiedText = document.documentElement.textContent ?? '';
242
+ let index = initialIndex;
243
+ for(let attempts = 0; attempts < 1000000; attempts += 1){
244
+ const start = `__A3S_WORK_CONTENT_CONTROL_START_${index}__`;
245
+ const end = `__A3S_WORK_CONTENT_CONTROL_END_${index}__`;
246
+ if (!occupiedText.includes(start) && !occupiedText.includes(end)) return [
247
+ start,
248
+ end
249
+ ];
250
+ index += 1;
251
+ }
252
+ throw new Error('Imported DOCX content-control marker space is exhausted.');
253
+ }
254
+ function applyImportedDocxContentControlMarkers(document, markers) {
255
+ if (!markers.controls.length) return;
256
+ for (const marker of markers.controls){
257
+ const wrapper = document.createElement('span');
258
+ for (const [name, value] of Object.entries(contentControlDomAttributes(marker.properties)))if (void 0 !== value) wrapper.setAttribute(name, value);
259
+ wrapper.className = 'work-document-content-control';
260
+ wrapper.setAttribute('role', 'textbox');
261
+ wrapper.setAttribute('aria-label', marker.properties.alias || marker.properties.tag || '内容控件');
262
+ if ('contentLocked' === marker.properties.lock || 'sdtContentLocked' === marker.properties.lock) wrapper.setAttribute('contenteditable', 'false');
263
+ replaceMarkerRange(document.body, marker.start, marker.end, wrapper);
264
+ }
265
+ document.body.normalize();
266
+ }
267
+ function hasImportedDocxContentControlMarkers(markers) {
268
+ return markers.controls.length > 0;
269
+ }
270
+ function readContentControl(control, sequence) {
271
+ const definition = readDefinition(control);
272
+ if (!definition) return null;
273
+ const metadata = readProperties(definition.properties);
274
+ if (!metadata) return null;
275
+ const runs = Array.from(definition.content.children);
276
+ if (!runs.length || runs.some((run)=>!isDocxWordElement(run) || 'r' !== run.localName) || hasUnsupportedContentControlSemanticChild(definition.content) || runs.some((run)=>!validRun(run))) {
277
+ if (runs.length) return null;
278
+ }
279
+ const text = runs.map(work_docx_content_control_import_runText).join('');
280
+ if (text.length > MAX_CONTENT_CONTROL_TEXT) throw new Error('Imported DOCX content control text is too large.');
281
+ if ('text' === metadata.type && !metadata.multiLine && text.includes('\n')) return null;
282
+ const nativeId = metadata.nativeId;
283
+ return normalizeDocumentContentControlProperties({
284
+ id: `docx-content-control-${sequence}`,
285
+ nativeId,
286
+ type: metadata.type,
287
+ alias: metadata.alias,
288
+ tag: metadata.tag,
289
+ lock: metadata.lock,
290
+ multiLine: metadata.multiLine,
291
+ appearance: metadata.appearance,
292
+ color: metadata.color
293
+ });
294
+ }
295
+ function readDefinition(control) {
296
+ if (!isDocxWordElement(control) || 'sdt' !== control.localName || !isDirectParagraphControl(control) || hasContentControlRelationshipReference(control) || hasUnsupportedContentControlSemanticChild(control) || !validControlAttributes(control)) return null;
297
+ const children = Array.from(control.children);
298
+ const properties = children.filter((child)=>isDocxWordElement(child) && 'sdtPr' === child.localName);
299
+ const content = children.filter((child)=>isDocxWordElement(child) && 'sdtContent' === child.localName);
300
+ const endProperties = children.filter((child)=>isDocxWordElement(child) && 'sdtEndPr' === child.localName);
301
+ if (1 !== properties.length || 1 !== content.length || endProperties.length > 1 || children.some((child)=>!isDocxWordElement(child) || 'sdtPr' !== child.localName && 'sdtContent' !== child.localName && 'sdtEndPr' !== child.localName)) return null;
302
+ if ([
303
+ control,
304
+ properties[0],
305
+ content[0],
306
+ endProperties[0]
307
+ ].some((element)=>element && hasNonWhitespaceText(element))) return null;
308
+ if (endProperties[0] && (endProperties[0].children.length > 0 || !validContainerAttributes(endProperties[0]))) return null;
309
+ if (!validContainerAttributes(properties[0]) || !validContainerAttributes(content[0])) return null;
310
+ return {
311
+ control,
312
+ content: content[0],
313
+ properties: properties[0]
314
+ };
315
+ }
316
+ function readProperties(properties) {
317
+ if (hasContentControlRelationshipReference(properties)) return null;
318
+ const children = Array.from(properties.children);
319
+ const groups = new Map();
320
+ let appearance = 'boundingBox';
321
+ let color = null;
322
+ for (const child of children){
323
+ const namespace = child.namespaceURI ?? '';
324
+ if (isDocxWordElement(child)) {
325
+ if (!PROPERTY_NAMES.has(child.localName)) return null;
326
+ const matches = groups.get(child.localName) ?? [];
327
+ matches.push(child);
328
+ groups.set(child.localName, matches);
329
+ continue;
330
+ }
331
+ if (namespace === CONTENT_CONTROL_WORD_2012_NAMESPACE) {
332
+ if ('appearance' !== child.localName && 'color' !== child.localName) return null;
333
+ const matches = groups.get(`w15:${child.localName}`) ?? [];
334
+ matches.push(child);
335
+ groups.set(`w15:${child.localName}`, matches);
336
+ continue;
337
+ }
338
+ if (namespace !== MARKUP_COMPATIBILITY_NAMESPACE) return null;
339
+ }
340
+ if (Array.from(groups.values()).some((items)=>items.length > 1)) return null;
341
+ const id = groups.get('id')?.[0];
342
+ if (id && !validLeaf(id, new Set([
343
+ 'val'
344
+ ]))) return null;
345
+ const nativeId = id ? parseNativeId(wordAttribute(id, 'val')) : null;
346
+ if (id && null === nativeId) return null;
347
+ const alias = readStringProperty(groups.get('alias')?.[0]);
348
+ const tag = readStringProperty(groups.get('tag')?.[0]);
349
+ if (null === alias || null === tag) return null;
350
+ const lockElement = groups.get('lock')?.[0];
351
+ if (lockElement && !validLeaf(lockElement, new Set([
352
+ 'val'
353
+ ]))) return null;
354
+ const lockValue = lockElement ? wordAttribute(lockElement, 'val') : null;
355
+ const lock = lockValue ?? 'unlocked';
356
+ if (!LOCK_VALUES.has(lock)) return null;
357
+ const textElement = groups.get('text')?.[0];
358
+ const richTextElement = groups.get('richText')?.[0];
359
+ if (textElement && richTextElement) return null;
360
+ if (textElement && !validLeaf(textElement, new Set([
361
+ 'multiLine'
362
+ ]))) return null;
363
+ if (richTextElement && !validLeaf(richTextElement, new Set())) return null;
364
+ const multilineValue = textElement ? wordAttribute(textElement, 'multiLine') : null;
365
+ if (null !== multilineValue && ![
366
+ '0',
367
+ '1',
368
+ 'false',
369
+ 'off',
370
+ 'on',
371
+ 'true'
372
+ ].includes(multilineValue)) return null;
373
+ const appearanceElement = groups.get('w15:appearance')?.[0];
374
+ if (appearanceElement) {
375
+ if (!validNamespacedLeaf(appearanceElement, new Set([
376
+ 'val'
377
+ ]), CONTENT_CONTROL_WORD_2012_NAMESPACE)) return null;
378
+ const value = namespacedAttribute(appearanceElement, 'val', CONTENT_CONTROL_WORD_2012_NAMESPACE);
379
+ if (!value || !APPEARANCE_VALUES.has(value)) return null;
380
+ appearance = value;
381
+ }
382
+ const colorElement = groups.get('w15:color')?.[0];
383
+ if (colorElement) {
384
+ if (!validNamespacedLeaf(colorElement, new Set([
385
+ 'val'
386
+ ]), CONTENT_CONTROL_WORD_2012_NAMESPACE)) return null;
387
+ const value = namespacedAttribute(colorElement, 'val', CONTENT_CONTROL_WORD_2012_NAMESPACE);
388
+ if (!value || !/^[0-9a-f]{6}$/i.test(value)) return null;
389
+ color = `#${value.toLowerCase()}`;
390
+ }
391
+ return {
392
+ alias: alias ?? '',
393
+ appearance,
394
+ color,
395
+ lock: lock,
396
+ multiLine: null !== multilineValue && [
397
+ '1',
398
+ 'on',
399
+ 'true'
400
+ ].includes(multilineValue),
401
+ nativeId,
402
+ tag: tag ?? '',
403
+ type: textElement ? 'text' : 'richText'
404
+ };
405
+ }
406
+ function replaceControlWithMarkers(document, control, start, end) {
407
+ const paragraph = control.parentElement;
408
+ const content = Array.from(control.children).find((child)=>isDocxWordElement(child) && 'sdtContent' === child.localName);
409
+ if (!paragraph || !content) return false;
410
+ const startRun = work_docx_content_control_import_markerRun(document, paragraph, start);
411
+ const endRun = work_docx_content_control_import_markerRun(document, paragraph, end);
412
+ paragraph.insertBefore(startRun, control);
413
+ const runs = Array.from(content.children);
414
+ for (const run of runs)paragraph.insertBefore(run, control);
415
+ paragraph.insertBefore(endRun, control);
416
+ control.remove();
417
+ return true;
418
+ }
419
+ function work_docx_content_control_import_markerRun(document, context, value) {
420
+ const namespace = context.namespaceURI ?? WORD_NAMESPACE;
421
+ const prefix = xmlNamespacePrefix(context, namespace) ?? 'w';
422
+ const run = document.createElementNS(namespace, `${prefix}:r`);
423
+ const text = document.createElementNS(namespace, `${prefix}:t`);
424
+ text.setAttributeNS(work_docx_settings_xml_XML_NAMESPACE, 'xml:space', 'preserve');
425
+ text.textContent = value;
426
+ run.append(text);
427
+ return run;
428
+ }
429
+ function replaceMarkerRange(root, start, end, replacement) {
430
+ const startElement = replaceMarkerWithBoundary(root, start, 'start');
431
+ const endElement = replaceMarkerWithBoundary(root, end, 'end');
432
+ if (!startElement || !endElement) return;
433
+ if (startElement.parentNode === endElement.parentNode) {
434
+ const parent = startElement.parentNode;
435
+ let current = startElement.nextSibling;
436
+ while(current && current !== endElement){
437
+ const next = current.nextSibling;
438
+ replacement.append(current);
439
+ current = next;
440
+ }
441
+ if (current === endElement) {
442
+ startElement.replaceWith(replacement);
443
+ endElement.remove();
444
+ return;
445
+ }
446
+ parent?.append(replacement);
447
+ startElement.remove();
448
+ endElement.remove();
449
+ return;
450
+ }
451
+ const range = root.ownerDocument.createRange();
452
+ try {
453
+ range.setStartAfter(startElement);
454
+ range.setEndBefore(endElement);
455
+ const content = range.extractContents();
456
+ replacement.append(content);
457
+ startElement.replaceWith(replacement);
458
+ endElement.remove();
459
+ } catch {
460
+ startElement.remove();
461
+ endElement.remove();
462
+ }
463
+ }
464
+ function replaceMarkerWithBoundary(root, marker, kind) {
465
+ const node = textNodes(root).find((candidate)=>candidate.data.includes(marker));
466
+ if (!node?.parentNode) return null;
467
+ const ownerDocument = root.ownerDocument;
468
+ if (!ownerDocument) return null;
469
+ const offset = node.data.indexOf(marker);
470
+ const before = node.data.slice(0, offset);
471
+ const after = node.data.slice(offset + marker.length);
472
+ const boundary = ownerDocument.createElement('span');
473
+ boundary.dataset.contentControlBoundary = kind;
474
+ node.parentNode.insertBefore(ownerDocument.createTextNode(before), node);
475
+ node.parentNode.insertBefore(boundary, node);
476
+ if (after) node.parentNode.insertBefore(ownerDocument.createTextNode(after), node);
477
+ node.remove();
478
+ return boundary;
479
+ }
480
+ function textNodes(root) {
481
+ const walker = root.ownerDocument?.createTreeWalker(root, NodeFilter.SHOW_TEXT);
482
+ const nodes = [];
483
+ if (!walker) return nodes;
484
+ while(walker.nextNode())nodes.push(walker.currentNode);
485
+ return nodes;
486
+ }
487
+ function validControlAttributes(element) {
488
+ return validContainerAttributes(element);
489
+ }
490
+ function validRun(run) {
491
+ if (hasNonWhitespaceText(run)) return false;
492
+ let sawContent = false;
493
+ let properties = 0;
494
+ for (const child of Array.from(run.children)){
495
+ if (!isDocxWordElement(child)) return false;
496
+ if ('rPr' === child.localName) {
497
+ properties += 1;
498
+ if (properties > 1 || sawContent) return false;
499
+ if (hasContentControlRelationshipReference(child)) return false;
500
+ continue;
501
+ }
502
+ if (!RUN_CONTENT_NAMES.has(child.localName)) return false;
503
+ sawContent = true;
504
+ if ('t' === child.localName && (child.textContent?.length ?? 0) > MAX_CONTENT_CONTROL_TEXT) return false;
505
+ }
506
+ return true;
507
+ }
508
+ function work_docx_content_control_import_runText(run) {
509
+ let value = '';
510
+ for (const child of Array.from(run.children))if (isDocxWordElement(child)) {
511
+ if ('t' === child.localName) value += child.textContent ?? '';
512
+ else if ('tab' === child.localName) value += '\t';
513
+ else if ('br' === child.localName || 'cr' === child.localName) value += '\n';
514
+ else if ('noBreakHyphen' === child.localName) value += '\u2011';
515
+ else if ('softHyphen' === child.localName) value += '\u00ad';
516
+ }
517
+ return value;
518
+ }
519
+ function isDirectParagraphControl(control) {
520
+ return isDocxWordElement(control) && Boolean(control.parentElement && isDocxWordElement(control.parentElement) && 'p' === control.parentElement.localName);
521
+ }
522
+ function hasContentControlAncestor(control) {
523
+ let current = control.parentElement;
524
+ while(current){
525
+ if (isDocxWordElement(current) && 'sdt' === current.localName) return true;
526
+ current = current.parentElement;
527
+ }
528
+ return false;
529
+ }
530
+ function isStructuralContentControl(control) {
531
+ const properties = Array.from(control.children).find((child)=>isDocxWordElement(child) && 'sdtPr' === child.localName);
532
+ if (!properties) return false;
533
+ return Array.from(properties.children).some((child)=>isDocxWordElement(child) && [
534
+ 'docPartObj',
535
+ 'docPartList'
536
+ ].includes(child.localName));
537
+ }
538
+ function wordAttribute(element, localName) {
539
+ const matches = Array.from(element.attributes).filter((item)=>xmlAttributeLocalName(item) === localName && xmlAttributeNamespace(element, item) === element.namespaceURI && WORD_NAMESPACES.has(element.namespaceURI ?? ''));
540
+ return 1 === matches.length ? matches[0].value : null;
541
+ }
542
+ function namespacedAttribute(element, localName, namespace) {
543
+ const matches = Array.from(element.attributes).filter((item)=>xmlAttributeLocalName(item) === localName && xmlAttributeNamespace(element, item) === namespace);
544
+ return 1 === matches.length ? matches[0].value : null;
545
+ }
546
+ function readStringProperty(element) {
547
+ if (!element) return '';
548
+ if (!validLeaf(element, new Set([
549
+ 'val'
550
+ ]))) return null;
551
+ const value = wordAttribute(element, 'val');
552
+ if (null === value || /[\u0000-\u001f\u007f]/u.test(value)) return null;
553
+ return value.slice(0, 255);
554
+ }
555
+ function validContainerAttributes(element) {
556
+ return Array.from(element.attributes).every((item)=>{
557
+ const namespace = xmlAttributeNamespace(element, item);
558
+ return 'http://www.w3.org/2000/xmlns/' === namespace || namespace === MARKUP_COMPATIBILITY_NAMESPACE;
559
+ });
560
+ }
561
+ function hasNonWhitespaceText(element) {
562
+ return Array.from(element.childNodes).some((child)=>child.nodeType === Node.TEXT_NODE && Boolean(child.textContent?.trim()));
563
+ }
564
+ function validLeaf(element, allowedAttributes) {
565
+ return 0 === element.children.length && Array.from(element.childNodes).every((child)=>child.nodeType !== Node.TEXT_NODE || !(child.textContent ?? '').trim()) && Array.from(element.attributes).every((item)=>{
566
+ const namespace = xmlAttributeNamespace(element, item);
567
+ if ('http://www.w3.org/2000/xmlns/' === namespace) return true;
568
+ if (namespace === MARKUP_COMPATIBILITY_NAMESPACE) return true;
569
+ return namespace === element.namespaceURI && WORD_NAMESPACES.has(element.namespaceURI ?? '') && allowedAttributes.has(xmlAttributeLocalName(item));
570
+ });
571
+ }
572
+ function validNamespacedLeaf(element, allowedAttributes, namespace) {
573
+ return 0 === element.children.length && Array.from(element.childNodes).every((child)=>child.nodeType !== Node.TEXT_NODE || !(child.textContent ?? '').trim()) && Array.from(element.attributes).every((item)=>{
574
+ const attributeNamespace = xmlAttributeNamespace(element, item);
575
+ return 'http://www.w3.org/2000/xmlns/' === attributeNamespace || attributeNamespace === namespace && allowedAttributes.has(xmlAttributeLocalName(item));
576
+ });
577
+ }
578
+ function parseNativeId(value) {
579
+ if (null === value || !/^-?(0|[1-9][0-9]{0,9})$/u.test(value)) return null;
580
+ const parsed = Number(value);
581
+ return Number.isSafeInteger(parsed) && parsed >= -2147483648 && parsed <= 2147483647 ? parsed : null;
582
+ }
161
583
  function isDocxEquationLikeRoot(element) {
162
584
  return 'oMath' === element.localName || 'oMathPara' === element.localName;
163
585
  }
@@ -276,7 +698,7 @@ function wordPrefix(element, namespace) {
276
698
  }
277
699
  const TRANSITIONAL_MATH_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/math';
278
700
  const STRICT_MATH_NAMESPACE = 'http://purl.oclc.org/ooxml/officeDocument/math';
279
- const STRICT_WORD_NAMESPACE = 'http://purl.oclc.org/ooxml/wordprocessingml/main';
701
+ const work_docx_equation_import_STRICT_WORD_NAMESPACE = 'http://purl.oclc.org/ooxml/wordprocessingml/main';
280
702
  const DOCX_MATH_NAMESPACES = new Set([
281
703
  TRANSITIONAL_MATH_NAMESPACE,
282
704
  STRICT_MATH_NAMESPACE
@@ -1982,7 +2404,7 @@ function parseWordPosition(element) {
1982
2404
  const source = attributes.get('val')?.trim() ?? '';
1983
2405
  const integer = wordIntegerValue(source, MIN_WORD_POSITION_HALF_POINTS, MAX_WORD_POSITION_HALF_POINTS);
1984
2406
  if (null !== integer) return integer;
1985
- return element.namespaceURI === STRICT_WORD_NAMESPACE ? strictUniversalHalfPoints(source, MIN_WORD_POSITION_HALF_POINTS, MAX_WORD_POSITION_HALF_POINTS) : null;
2407
+ return element.namespaceURI === work_docx_equation_import_STRICT_WORD_NAMESPACE ? strictUniversalHalfPoints(source, MIN_WORD_POSITION_HALF_POINTS, MAX_WORD_POSITION_HALF_POINTS) : null;
1986
2408
  }
1987
2409
  function parseWordHalfPointSize(element) {
1988
2410
  if (!element) return;
@@ -1992,7 +2414,7 @@ function parseWordHalfPointSize(element) {
1992
2414
  if (!attributes || 1 !== attributes.size) return null;
1993
2415
  const source = attributes.get('val')?.trim() ?? '';
1994
2416
  const integer = wordIntegerValue(source, 1, MAX_WORD_HALF_POINT_SIZE);
1995
- const halfPoints = integer ?? (element.namespaceURI === STRICT_WORD_NAMESPACE ? strictUniversalHalfPoints(source, 1, MAX_WORD_HALF_POINT_SIZE) : null);
2417
+ const halfPoints = integer ?? (element.namespaceURI === work_docx_equation_import_STRICT_WORD_NAMESPACE ? strictUniversalHalfPoints(source, 1, MAX_WORD_HALF_POINT_SIZE) : null);
1996
2418
  return null === halfPoints ? null : halfPoints / 2;
1997
2419
  }
1998
2420
  function wordIntegerValue(source, minimum, maximum) {
@@ -2138,7 +2560,7 @@ function parseWordFitText(element) {
2138
2560
  if (!attributes?.has('val')) return null;
2139
2561
  const source = attributes.get('val')?.trim() ?? '';
2140
2562
  const integer = wordIntegerValue(source, 0, MAX_WORD_FIT_TEXT_WIDTH_TWIPS);
2141
- const widthTwips = integer ?? (element.namespaceURI === STRICT_WORD_NAMESPACE ? strictUniversalTwips(source, 0, MAX_WORD_FIT_TEXT_WIDTH_TWIPS) : null);
2563
+ const widthTwips = integer ?? (element.namespaceURI === work_docx_equation_import_STRICT_WORD_NAMESPACE ? strictUniversalTwips(source, 0, MAX_WORD_FIT_TEXT_WIDTH_TWIPS) : null);
2142
2564
  const id = attributes.has('id') ? wordIntegerValue(attributes.get('id')?.trim() ?? '', MIN_WORD_FIT_TEXT_ID, MAX_WORD_FIT_TEXT_ID) : void 0;
2143
2565
  return null === widthTwips || null === id ? null : {
2144
2566
  widthTwips,
@@ -4073,7 +4495,7 @@ function nextEquationMarker(state) {
4073
4495
  state.occupiedText += marker;
4074
4496
  return marker;
4075
4497
  }
4076
- const WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
4498
+ const work_docx_index_import_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
4077
4499
  const work_docx_index_import_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
4078
4500
  function markDocxIndexes(document) {
4079
4501
  const fields = docxFieldOccurrences(document);
@@ -4124,11 +4546,11 @@ function applyImportedDocxIndexMarkers(document, markers) {
4124
4546
  });
4125
4547
  const parsed = document.createRange().createContextualFragment(html);
4126
4548
  const replacement = parsed.firstElementChild;
4127
- if (replacement instanceof HTMLElement) replaceMarkerRange(document.body, entry.start, entry.end, replacement);
4549
+ if (replacement instanceof HTMLElement) work_docx_index_import_replaceMarkerRange(document.body, entry.start, entry.end, replacement);
4128
4550
  }
4129
4551
  const importedEntries = Array.from(document.body.querySelectorAll('[data-document-index-entry]'));
4130
4552
  for (const index of markers.indexes){
4131
- const text = textNodes(document.body).find((node)=>node.data.includes(index.marker));
4553
+ const text = work_docx_index_import_textNodes(document.body).find((node)=>node.data.includes(index.marker));
4132
4554
  if (!text) continue;
4133
4555
  const entries = index.entries.map((entry, entryIndex)=>{
4134
4556
  const targets = importedEntries.filter((candidate)=>normalizedTerm(candidate.dataset.indexMainEntry) === normalizedTerm(entry.mainEntry) && normalizedTerm(candidate.dataset.indexSubEntry) === normalizedTerm(entry.subEntry));
@@ -4250,23 +4672,23 @@ function insertFieldBoundaryMarkers(document, field, start, end) {
4250
4672
  field.end.parentNode?.insertBefore(work_docx_index_import_markerRun(document, end), field.end.nextSibling);
4251
4673
  }
4252
4674
  function markerParagraph(document, marker) {
4253
- const paragraph = document.createElementNS(WORD_NAMESPACE, 'w:p');
4675
+ const paragraph = document.createElementNS(work_docx_index_import_WORD_NAMESPACE, 'w:p');
4254
4676
  paragraph.append(work_docx_index_import_markerRun(document, marker));
4255
4677
  return paragraph;
4256
4678
  }
4257
4679
  function work_docx_index_import_markerRun(document, value) {
4258
- const run = document.createElementNS(WORD_NAMESPACE, 'w:r');
4680
+ const run = document.createElementNS(work_docx_index_import_WORD_NAMESPACE, 'w:r');
4259
4681
  run.append(markerText(document, value));
4260
4682
  return run;
4261
4683
  }
4262
4684
  function markerText(document, value) {
4263
- const text = document.createElementNS(WORD_NAMESPACE, 'w:t');
4685
+ const text = document.createElementNS(work_docx_index_import_WORD_NAMESPACE, 'w:t');
4264
4686
  text.setAttributeNS(work_docx_index_import_XML_NAMESPACE, 'xml:space', 'preserve');
4265
4687
  text.textContent = value;
4266
4688
  return text;
4267
4689
  }
4268
- function replaceMarkerRange(root, start, end, replacement) {
4269
- const nodes = textNodes(root);
4690
+ function work_docx_index_import_replaceMarkerRange(root, start, end, replacement) {
4691
+ const nodes = work_docx_index_import_textNodes(root);
4270
4692
  const startNode = nodes.find((node)=>node.data.includes(start));
4271
4693
  const endNode = nodes.find((node)=>node.data.includes(end));
4272
4694
  if (!startNode || !endNode) return;
@@ -4276,7 +4698,7 @@ function replaceMarkerRange(root, start, end, replacement) {
4276
4698
  range.deleteContents();
4277
4699
  range.insertNode(replacement);
4278
4700
  }
4279
- function textNodes(root) {
4701
+ function work_docx_index_import_textNodes(root) {
4280
4702
  const walker = root.ownerDocument?.createTreeWalker(root, NodeFilter.SHOW_TEXT);
4281
4703
  const nodes = [];
4282
4704
  if (!walker) return nodes;
@@ -4394,10 +4816,10 @@ function supportedNumberingChange(change) {
4394
4816
  if (1 !== levels.length || 1 !== numberingIds.length) return null;
4395
4817
  const level = boundedWordInteger(levels[0], 'val', 0, 8);
4396
4818
  const numberingId = boundedWordInteger(numberingIds[0], 'val', 0, MAX_DOCUMENT_NUMBERING_START);
4397
- const id = wordAttribute(change, 'id')?.trim() ?? '';
4398
- const author = wordAttribute(change, 'author')?.trim() ?? '';
4399
- const rawDate = wordAttribute(change, 'date');
4400
- const original = wordAttribute(change, 'original') ?? '';
4819
+ const id = work_docx_numbering_change_import_wordAttribute(change, 'id')?.trim() ?? '';
4820
+ const author = work_docx_numbering_change_import_wordAttribute(change, 'author')?.trim() ?? '';
4821
+ const rawDate = work_docx_numbering_change_import_wordAttribute(change, 'date');
4822
+ const original = work_docx_numbering_change_import_wordAttribute(change, 'original') ?? '';
4401
4823
  if (null === level || null === numberingId || !/^\+?\d{1,10}$/.test(id) || !author || author.length > 255 || /[\u0000-\u001f\u007f]/.test(author) || null !== rawDate && !Number.isFinite(Date.parse(rawDate))) return null;
4402
4824
  const definitions = parseOriginalNumbering(original);
4403
4825
  const definition = definitions?.get(level + 1);
@@ -4507,12 +4929,12 @@ function directWordChildren(parent, localName) {
4507
4929
  }
4508
4930
  function boundedWordInteger(element, name, minimum, maximum) {
4509
4931
  if (!element) return null;
4510
- const raw = wordAttribute(element, name);
4932
+ const raw = work_docx_numbering_change_import_wordAttribute(element, name);
4511
4933
  if (!raw || !/^\d{1,10}$/.test(raw)) return null;
4512
4934
  const value = Number(raw);
4513
4935
  return Number.isSafeInteger(value) && value >= minimum && value <= maximum ? value : null;
4514
4936
  }
4515
- function wordAttribute(element, localName) {
4937
+ function work_docx_numbering_change_import_wordAttribute(element, localName) {
4516
4938
  const namespace = element.namespaceURI;
4517
4939
  if (!namespace) return null;
4518
4940
  const attributes = Array.from(element.attributes).filter((attribute)=>xmlAttributeLocalName(attribute) === localName && xmlAttributeNamespace(element, attribute) === namespace);
@@ -5314,7 +5736,7 @@ function inspectDocxPageBorders(sectionProperties, themeSource) {
5314
5736
  spoofedCount: spoofedContainers
5315
5737
  };
5316
5738
  const attributes = wordContainerAttributes(container);
5317
- if (!attributes || hasNonWhitespaceText(container)) return {
5739
+ if (!attributes || work_docx_page_borders_import_hasNonWhitespaceText(container)) return {
5318
5740
  status: 'invalid',
5319
5741
  invalidCount: 1,
5320
5742
  spoofedCount: spoofedContainers
@@ -5410,7 +5832,7 @@ function pageBorderZOrder(value) {
5410
5832
  const normalized = value.trim();
5411
5833
  return PAGE_BORDER_Z_ORDERS.has(normalized) ? normalized : null;
5412
5834
  }
5413
- function hasNonWhitespaceText(element) {
5835
+ function work_docx_page_borders_import_hasNonWhitespaceText(element) {
5414
5836
  return Array.from(element.childNodes).some((node)=>node.nodeType === Node.TEXT_NODE && node.textContent?.trim());
5415
5837
  }
5416
5838
  function work_docx_page_borders_import_isNamespaceDeclaration(attribute) {
@@ -8511,4 +8933,4 @@ function work_docx_text_box_import_textNodes(root) {
8511
8933
  while(walker.nextNode())nodes.push(walker.currentNode);
8512
8934
  return nodes;
8513
8935
  }
8514
- export { applyImportedDocxEquationMarkers, applyImportedDocxIndexMarkers, applyImportedDocxNumberingChangeMarkers, applyImportedDocxParagraphAlignmentMarkers, applyImportedDocxParagraphBorderMarkers, applyImportedDocxParagraphDirectionMarkers, applyImportedDocxParagraphFormattingChangeMarkers, applyImportedDocxParagraphIndentMarkers, applyImportedDocxParagraphPaginationMarkers, applyImportedDocxParagraphShadingMarkers, applyImportedDocxParagraphSpacingMarkers, applyImportedDocxParagraphTabStopMarkers, applyImportedDocxRunFormattingMarkers, applyImportedDocxTableOfContentsMarkers, applyImportedDocxTextBoxMarkers, createDocxParagraphStyleResolver, createDocxTableStyleResolver, createImportedDocxRunFormattingMarkerState, docxCaptionBookmark, docxCaptionReferenceTarget, docxCaptionSequenceKind, docxEquationHtml, docxFieldInstructions, docxFieldOccurrenceIsInlineEditable, docxFieldOccurrences, docxFieldResultText, docxRunPropertySources, docxTableCellStyleLayers, docxTablePropertySources, docxTableRunPropertySources, hasImportedDocxIndexMarkers, hasImportedDocxNumberingChangeMarkers, hasImportedDocxParagraphAlignmentMarkers, hasImportedDocxParagraphBorderMarkers, hasImportedDocxParagraphDirectionMarkers, hasImportedDocxParagraphFormattingChangeMarkers, hasImportedDocxParagraphIndentMarkers, hasImportedDocxParagraphPaginationMarkers, hasImportedDocxParagraphShadingMarkers, hasImportedDocxParagraphSpacingMarkers, hasImportedDocxParagraphTabStopMarkers, hasImportedDocxRunFormattingMarkers, hasImportedDocxTableOfContentsMarkers, hasImportedDocxTextBoxMarkers, hasInvalidDocxFieldStructure, importedDocxStrike, importedDocxUnderline, inspectDocxEquation, inspectDocxHiddenText, inspectDocxLegacyTextEffects, inspectDocxPageBorders, inspectDocxPageMarginSettings, inspectDocxPageMargins, inspectDocxPageSize, inspectDocxPaperSource, inspectDocxRunBorder, inspectDocxRunFonts, inspectDocxRunShading, inspectDocxTextBoxes, isDocxEquationLikeRoot, isSupportedDocxEquationPlacement, isSupportedDocxNumberingChange, isSupportedDocxParagraphFormattingChange, isSupportedDocxRunFormattingChange, markDocxIndexes, markDocxNumberingChanges, markDocxPackageEquations, markDocxParagraphAlignments, markDocxParagraphBorders, markDocxParagraphDirections, markDocxParagraphFormattingChanges, markDocxParagraphIndents, markDocxParagraphPagination, markDocxParagraphShading, markDocxParagraphSpacing, markDocxParagraphTabStops, markDocxRunFormattingIntoState, markDocxTablesOfContents, markDocxTextBoxes, parseDirectDocxParagraphShading, parseDocxPageBorders, parseDocxPageGeometry, parseDocxPageMargins, parseDocxPaperSource, resolveDocxHiddenText, resolveDocxLegacyTextEffects, resolveDocxParagraphBordersForParagraph, resolveDocxRunBorder, resolveDocxRunShading, resolveDocxTableStyleResolver, supportedDocxIndexEntryField, supportedDocxIndexField, supportedDocxTableOfContentsField };
8936
+ export { applyImportedDocxContentControlMarkers, applyImportedDocxEquationMarkers, applyImportedDocxIndexMarkers, applyImportedDocxNumberingChangeMarkers, applyImportedDocxParagraphAlignmentMarkers, applyImportedDocxParagraphBorderMarkers, applyImportedDocxParagraphDirectionMarkers, applyImportedDocxParagraphFormattingChangeMarkers, applyImportedDocxParagraphIndentMarkers, applyImportedDocxParagraphPaginationMarkers, applyImportedDocxParagraphShadingMarkers, applyImportedDocxParagraphSpacingMarkers, applyImportedDocxParagraphTabStopMarkers, applyImportedDocxRunFormattingMarkers, applyImportedDocxTableOfContentsMarkers, applyImportedDocxTextBoxMarkers, createDocxParagraphStyleResolver, createDocxTableStyleResolver, createImportedDocxRunFormattingMarkerState, docxCaptionBookmark, docxCaptionReferenceTarget, docxCaptionSequenceKind, docxEquationHtml, docxFieldInstructions, docxFieldOccurrenceIsInlineEditable, docxFieldOccurrences, docxFieldResultText, docxRunPropertySources, docxTableCellStyleLayers, docxTablePropertySources, docxTableRunPropertySources, hasImportedDocxContentControlMarkers, hasImportedDocxIndexMarkers, hasImportedDocxNumberingChangeMarkers, hasImportedDocxParagraphAlignmentMarkers, hasImportedDocxParagraphBorderMarkers, hasImportedDocxParagraphDirectionMarkers, hasImportedDocxParagraphFormattingChangeMarkers, hasImportedDocxParagraphIndentMarkers, hasImportedDocxParagraphPaginationMarkers, hasImportedDocxParagraphShadingMarkers, hasImportedDocxParagraphSpacingMarkers, hasImportedDocxParagraphTabStopMarkers, hasImportedDocxRunFormattingMarkers, hasImportedDocxTableOfContentsMarkers, hasImportedDocxTextBoxMarkers, hasInvalidDocxFieldStructure, importedDocxStrike, importedDocxUnderline, inspectDocxContentControls, inspectDocxEquation, inspectDocxHiddenText, inspectDocxLegacyTextEffects, inspectDocxPageBorders, inspectDocxPageMarginSettings, inspectDocxPageMargins, inspectDocxPageSize, inspectDocxPaperSource, inspectDocxRunBorder, inspectDocxRunFonts, inspectDocxRunShading, inspectDocxTextBoxes, isDocxEquationLikeRoot, isSupportedDocxEquationPlacement, isSupportedDocxNumberingChange, isSupportedDocxParagraphFormattingChange, isSupportedDocxRunFormattingChange, markDocxContentControls, markDocxIndexes, markDocxNumberingChanges, markDocxPackageEquations, markDocxParagraphAlignments, markDocxParagraphBorders, markDocxParagraphDirections, markDocxParagraphFormattingChanges, markDocxParagraphIndents, markDocxParagraphPagination, markDocxParagraphShading, markDocxParagraphSpacing, markDocxParagraphTabStops, markDocxRunFormattingIntoState, markDocxTablesOfContents, markDocxTextBoxes, parseDirectDocxParagraphShading, parseDocxPageBorders, parseDocxPageGeometry, parseDocxPageMargins, parseDocxPaperSource, resolveDocxHiddenText, resolveDocxLegacyTextEffects, resolveDocxParagraphBordersForParagraph, resolveDocxRunBorder, resolveDocxRunShading, resolveDocxTableStyleResolver, supportedDocxIndexEntryField, supportedDocxIndexField, supportedDocxTableOfContentsField };