@a3s-lab/office 0.39.0 → 0.41.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.
@@ -1,6 +1,6 @@
1
1
  import jszip from "jszip";
2
- import { normalizeDocumentBookmarkName, normalizeDocumentEmphasisMark, DOCUMENT_STRIKE_STYLE_ATTRIBUTE, applyDocumentImageIdentityToElement, documentEmphasisMarkFromElement, DOCUMENT_PAGE_BORDER_EDGES, DOCUMENT_UNDERLINE_STYLE_ATTRIBUTE, createDocumentImageIdentityRegistry, normalizeDocumentColumns, normalizeDocumentTableVerticalAlign, normalizeDocumentTextCase, documentCharacterSpacingTwipsFromElement, documentCharacterPositionHalfPointsFromElement, documentTableColumnPercentagesFromElement, documentTableCellMarginOverridesFromElement, applyDocumentTableRowIdentityToElement, normalizeDocumentBookmarksHtml, documentImageIdentityFromElement, documentTableGeometryFromElement, normalizeDocumentTableRowHeightRule, documentNoteKey, DOCUMENT_LEGACY_TEXT_EMBOSS_ATTRIBUTE, normalizeDocumentHiddenText, documentLegacyTextEffectsConflict, normalizeDocumentTableBorderWidth, normalizeDocumentCharacterSpacingTwips, documentStrikeFormattingFromElement, normalizeDocumentTableBorderStyle, normalizeDocumentBookmarkReferencesHtml, documentCitationInstruction, documentImageWrapContourFromElement, documentImagePositionFromElement, normalizeDocumentCharacterScalePercent, normalizeDocumentUnderlineStyle, normalizeDocumentPageChrome, documentPageMarginsForLayout, normalizeDocumentImageLayer, documentKerningThresholdHalfPointsFromElement, documentImageCropFromElement, documentBookmarkReferenceInstruction, normalizeDocumentPageBorders, documentImageLayerFromElement, normalizeDocumentLegacyTextEffects, documentCaptionLabel, documentCaptionKind, DOCUMENT_LEGACY_TEXT_IMPRINT_ATTRIBUTE, isContourImageLayout, serializeDocumentOpenTypeFeatures, normalizeDocumentImageIdentity, normalizeDocumentStrikeStyle, normalizeTableColor, normalizeDocumentOpenTypeFeatures, documentCitationTags, docxDocumentFieldKind, documentTableBordersFromElement, documentSections, wrapsBesideImage, normalizeDocumentCharacterPositionHalfPoints, normalizeDocumentKerningThresholdHalfPoints, defaultDocumentImageWrapContour, parseDocumentOpenTypeFeatures, normalizeDocumentPaperSource, DOCUMENT_LEGACY_TEXT_OUTLINE_ATTRIBUTE, documentEquationFromElement, documentLegacyTextEffectsFromElement, parseDocumentCharacterFormatting, documentUnderlineFormattingFromElement, DOCUMENT_LEGACY_TEXT_SHADOW_ATTRIBUTE, normalizeDocumentEquation, parseDocumentParagraphFormatting, documentOpenTypeFeaturesFromElement, normalizeDocumentBookmarkNativeId, documentTableRowIdentityFromElement, DOCUMENT_PAGE_MARGIN_KEYS, uniqueDocumentImageIdentity, documentHiddenTextFromElement, collectDocumentNotes, documentImageLayoutFromElement, documentPageHorizontalMarginTwips, DOCUMENT_OPEN_TYPE_ATTRIBUTE, documentPageGeometryForLayout, DOCUMENT_LEGACY_TEXT_EFFECT_NAMES, documentCharacterScalePercentFromElement, normalizeDocumentPageMargins, normalizeDocumentLegacyTextEffect, documentNoteKind } from "./4174.js";
3
- import { normalizeDocumentRunBorder, parseDocumentScriptFonts, normalizeDocumentIndexesHtml, parseXml, parseDocumentRunShading, applyDocumentParagraphIdentityToElement, DOCUMENT_RUN_SHADING_ATTRIBUTE, documentScriptFontSlotFromElement, DOCUMENT_HIGHLIGHT_ATTRIBUTE, parseDocxThemeReference, serializeDocumentScriptFonts, attribute as work_ooxml_package_attribute, createDocumentParagraphIdentityRegistry, uniqueDocumentParagraphIdentity, DOCUMENT_NO_PROOF_ATTRIBUTE, documentHighlightForCssColor, directChildren, documentTableOfContentsValueFromElement, DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE, DocxThemePatchCollector, normalizeDocumentScriptFonts, normalizeDocumentParagraphIdentity, normalizeDocumentHighlight, normalizeDocumentParagraphBorders, serializeUtf8Xml, DOCUMENT_PARAGRAPH_BORDER_EDGES, parseDocumentRunBorder, normalizeDocumentNoProof, decodeXmlBytes, documentParagraphIdentityFromElement, normalizeDocumentParagraphId, documentIndexValueFromElement, descendants, resolvePartTarget, documentProofingLanguagesFromElement, documentScriptFontFamily, parseDocumentRunShadingElement, parseDocumentProofingLanguages, documentHighlightFromElement, parseDocumentParagraphBordersElement, DOCUMENT_RUN_BORDER_ATTRIBUTE, parseDocumentRunBorderElement, xmlNamespacePrefix, documentScriptFontSlotFromHint, documentNoProofFromElement, documentScriptFontsFromElement, normalizeDocumentRunShading, documentIndexEntryFromElement, directChild, normalizeDocumentScriptFontSlot, patchDocxThemeReferences, parseDocumentParagraphShadingElement, documentFontNameFromCssFamily } from "./4121.js";
2
+ import { normalizeDocumentBookmarkName, normalizeDocumentEmphasisMark, DOCUMENT_STRIKE_STYLE_ATTRIBUTE, applyDocumentImageIdentityToElement, documentEmphasisMarkFromElement, DOCUMENT_PAGE_BORDER_EDGES, DOCUMENT_UNDERLINE_STYLE_ATTRIBUTE, createDocumentImageIdentityRegistry, normalizeDocumentColumns, normalizeDocumentTableVerticalAlign, normalizeDocumentTextCase, documentCharacterSpacingTwipsFromElement, documentCharacterPositionHalfPointsFromElement, documentTableColumnPercentagesFromElement, documentTableCellMarginOverridesFromElement, applyDocumentTableRowIdentityToElement, normalizeDocumentBookmarksHtml, documentImageIdentityFromElement, documentTableGeometryFromElement, normalizeDocumentTableRowHeightRule, documentNoteKey, DOCUMENT_LEGACY_TEXT_EMBOSS_ATTRIBUTE, normalizeDocumentHiddenText, documentLegacyTextEffectsConflict, normalizeDocumentTableBorderWidth, normalizeDocumentCharacterSpacingTwips, documentStrikeFormattingFromElement, normalizeDocumentTableBorderStyle, parseDocumentNumberingChange, normalizeDocumentBookmarkReferencesHtml, documentCitationInstruction, documentImageWrapContourFromElement, documentImagePositionFromElement, normalizeDocumentCharacterScalePercent, normalizeDocumentUnderlineStyle, normalizeDocumentPageChrome, documentPageMarginsForLayout, normalizeDocumentImageLayer, documentKerningThresholdHalfPointsFromElement, documentImageCropFromElement, documentBookmarkReferenceInstruction, normalizeDocumentPageBorders, documentImageLayerFromElement, serializeDocumentOpenTypeFeatures, documentCaptionLabel, documentCaptionKind, isContourImageLayout, normalizeDocumentLegacyTextEffects, DOCUMENT_LEGACY_TEXT_IMPRINT_ATTRIBUTE, normalizeDocumentImageIdentity, normalizeDocumentStrikeStyle, normalizeTableColor, normalizeDocumentOpenTypeFeatures, documentCitationTags, docxDocumentFieldKind, documentTableBordersFromElement, documentSections, wrapsBesideImage, normalizeDocumentCharacterPositionHalfPoints, normalizeDocumentKerningThresholdHalfPoints, defaultDocumentImageWrapContour, parseDocumentOpenTypeFeatures, normalizeDocumentPaperSource, DOCUMENT_LEGACY_TEXT_OUTLINE_ATTRIBUTE, documentEquationFromElement, parseDocumentCharacterFormatting, documentLegacyTextEffectsFromElement, documentUnderlineFormattingFromElement, DOCUMENT_LEGACY_TEXT_SHADOW_ATTRIBUTE, normalizeDocumentEquation, parseDocumentParagraphFormatting, documentOpenTypeFeaturesFromElement, normalizeDocumentBookmarkNativeId, documentTableRowIdentityFromElement, DOCUMENT_PAGE_MARGIN_KEYS, uniqueDocumentImageIdentity, documentHiddenTextFromElement, collectDocumentNotes, documentImageLayoutFromElement, documentPageHorizontalMarginTwips, DOCUMENT_OPEN_TYPE_ATTRIBUTE, MAX_DOCUMENT_NUMBERING_START, documentPageGeometryForLayout, DOCUMENT_LEGACY_TEXT_EFFECT_NAMES, documentCharacterScalePercentFromElement, normalizeDocumentPageMargins, normalizeDocumentLegacyTextEffect, documentNoteKind } from "./4174.js";
3
+ import { normalizeDocumentRunBorder, parseDocumentScriptFonts, parseDocxThemeReference, parseXml, parseDocumentRunShading, applyDocumentParagraphIdentityToElement, DOCUMENT_RUN_SHADING_ATTRIBUTE, documentScriptFontSlotFromElement, DOCUMENT_HIGHLIGHT_ATTRIBUTE, normalizeDocumentIndexesHtml, serializeDocumentScriptFonts, attribute as work_ooxml_package_attribute, createDocumentParagraphIdentityRegistry, uniqueDocumentParagraphIdentity, DOCUMENT_NO_PROOF_ATTRIBUTE, documentHighlightForCssColor, directChildren, documentTableOfContentsValueFromElement, DOCUMENT_PROOFING_LANGUAGES_ATTRIBUTE, DocxThemePatchCollector, normalizeDocumentScriptFonts, normalizeDocumentParagraphIdentity, normalizeDocumentHighlight, normalizeDocumentParagraphBorders, serializeUtf8Xml, DOCUMENT_PARAGRAPH_BORDER_EDGES, parseDocumentRunBorder, normalizeDocumentNoProof, decodeXmlBytes, documentParagraphIdentityFromElement, normalizeDocumentParagraphId, documentScriptFontFamily, descendants, resolvePartTarget, documentProofingLanguagesFromElement, documentHighlightFromElement, parseDocumentRunShadingElement, parseDocumentProofingLanguages, patchDocxThemeReferences, parseDocumentParagraphBordersElement, DOCUMENT_RUN_BORDER_ATTRIBUTE, parseDocumentRunBorderElement, xmlNamespacePrefix, documentScriptFontSlotFromHint, documentNoProofFromElement, documentScriptFontsFromElement, normalizeDocumentRunShading, normalizeDocumentScriptFontSlot, directChild, documentIndexEntryFromElement, documentIndexValueFromElement, parseDocumentParagraphShadingElement, documentFontNameFromCssFamily } from "./4121.js";
4
4
  import { assignDocxNoteIds, patchDocxPageColor, normalizeDocxCommentId, assignDocxCommentThreads } from "./0~4980.js";
5
5
  import { paragraphTabStops, mergeDocxIgnorableExtensionsAtPairs, DOCX_WORD_2010_NAMESPACE, docxKerningThresholdValue, patchDocxBibliography, documentIndexInstruction, docxCharacterScaleValue, XMLNS_NAMESPACE as work_docx_settings_xml_XMLNS_NAMESPACE, paragraphAlignment, xmlNamespaceUri, paragraphSpacingOptions, docxCharacterPositionValue, documentIndexEntryInstruction, docxEmphasisMarkRunOptions, hasNonWhitespaceXmlText, patchDocxExplicitZeroCharacterSpacing, cloneXmlElement, cssColorToHex, xmlDeclaredPrefix, assertXmlRoot, DOCX_WORDPROCESSING_NAMESPACES, paragraphIndent, domDirection, xmlAttributeNamespace, paragraphBidirectional, XML_NAMESPACE as work_docx_settings_xml_XML_NAMESPACE, mergeDocxIgnorableExtensions, DocxParagraphDefaultCollapsedPatchCollector, dataBoolean, patchDocxParagraphDefaultCollapsed, patchDocxExplicitZeroKerningThresholds, parseDocxColorSchemeMappingElement, paragraphPaginationOptions, DOCX_COLOR_SCHEME_MAPPING_ATTRIBUTES, documentProofingLanguageDocxOptions, xmlAttributeLocalName, cssFontSize, paragraphDirectionOptions, cssFontFamily, docxCharacterSpacingValue } from "./0~5024.js";
6
6
  import { normalizeDocumentHref } from "./0~work-document-links.js";
@@ -757,285 +757,6 @@ function hasAncestor(element, localName) {
757
757
  }
758
758
  return false;
759
759
  }
760
- const DOCX_RUN_PROPERTY_ORDER = [
761
- 'rStyle',
762
- 'rFonts',
763
- 'b',
764
- 'bCs',
765
- 'i',
766
- 'iCs',
767
- 'caps',
768
- 'smallCaps',
769
- 'strike',
770
- 'dstrike',
771
- 'outline',
772
- 'shadow',
773
- 'emboss',
774
- 'imprint',
775
- 'noProof',
776
- 'snapToGrid',
777
- 'vanish',
778
- 'webHidden',
779
- 'color',
780
- 'spacing',
781
- 'w',
782
- 'kern',
783
- 'position',
784
- 'sz',
785
- 'szCs',
786
- 'highlight',
787
- 'u',
788
- 'effect',
789
- 'bdr',
790
- 'shd',
791
- 'fitText',
792
- 'vertAlign',
793
- 'rtl',
794
- 'cs',
795
- 'em',
796
- 'lang',
797
- 'eastAsianLayout',
798
- 'specVanish',
799
- 'rPrChange'
800
- ];
801
- const DOCX_RUN_PROPERTY_RANK = new Map(DOCX_RUN_PROPERTY_ORDER.map((name, index)=>[
802
- name,
803
- index
804
- ]));
805
- const HIDDEN_TEXT_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
806
- const VANISH_RANK = DOCX_RUN_PROPERTY_RANK.get('vanish') ?? 0;
807
- class DocxHiddenTextPatchCollector {
808
- patches = [];
809
- nextMarker = 1;
810
- occupied;
811
- patchesByMarker = new Map();
812
- markersByValueAndStyle = new Map();
813
- constructor(source){
814
- this.occupied = source;
815
- }
816
- marker(value, inheritedStyle) {
817
- const style = this.originalStyle(inheritedStyle);
818
- const key = JSON.stringify([
819
- value,
820
- style ?? null
821
- ]);
822
- const existing = this.markersByValueAndStyle.get(key);
823
- if (existing) return existing;
824
- let marker = '';
825
- do {
826
- marker = `A3SOfficeHiddenText${value ? 'On' : 'Off'}${String(this.nextMarker).padStart(8, '0')}`;
827
- this.nextMarker += 1;
828
- }while (this.occupied.includes(marker) || this.patches.some((patch)=>patch.marker === marker))
829
- const patch = {
830
- marker,
831
- value,
832
- ...style ? {
833
- style
834
- } : {}
835
- };
836
- this.patches.push(patch);
837
- this.patchesByMarker.set(marker, patch);
838
- this.markersByValueAndStyle.set(key, marker);
839
- return marker;
840
- }
841
- lookup(marker) {
842
- return marker ? this.patchesByMarker.get(marker) : void 0;
843
- }
844
- originalStyle(style) {
845
- let current = style;
846
- const seen = new Set();
847
- while(current && !seen.has(current)){
848
- seen.add(current);
849
- const patch = this.patchesByMarker.get(current);
850
- if (!patch) break;
851
- current = patch.style;
852
- }
853
- return current;
854
- }
855
- }
856
- async function patchDocxHiddenText(buffer, patches) {
857
- if (!patches.length) return buffer;
858
- const archive = await jszip.loadAsync(buffer);
859
- const byMarker = new Map(patches.map((patch)=>[
860
- patch.marker,
861
- patch
862
- ]));
863
- const applied = new Set();
864
- for (const entry of Object.values(archive.files)){
865
- if (entry.dir || !HIDDEN_TEXT_PART_PATTERN.test(entry.name)) continue;
866
- const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
867
- let changed = false;
868
- for (const properties of descendants(document, 'rPr')){
869
- if (!DOCX_WORDPROCESSING_NAMESPACES.has(properties.namespaceURI ?? '')) continue;
870
- const markerStyles = directChildren(properties, 'rStyle').filter((style)=>{
871
- if (!DOCX_WORDPROCESSING_NAMESPACES.has(style.namespaceURI ?? '')) return false;
872
- const marker = wordValue(style);
873
- return null !== marker && byMarker.has(marker);
874
- });
875
- if (!markerStyles.length) continue;
876
- if (1 !== markerStyles.length) throw new Error('Generated DOCX hidden-text marker is duplicated in one run.');
877
- const marker = wordValue(markerStyles[0]);
878
- const patch = marker ? byMarker.get(marker) : void 0;
879
- if (!marker || !patch) continue;
880
- const existing = directChildren(properties, 'vanish').filter((element)=>DOCX_WORDPROCESSING_NAMESPACES.has(element.namespaceURI ?? ''));
881
- if (existing.length) throw new Error('Generated DOCX hidden-text marker unexpectedly has a native value.');
882
- if (patch.style) setWordValue(markerStyles[0], patch.style);
883
- else markerStyles[0].remove();
884
- insertHiddenTextProperty(document, properties, patch.value);
885
- applied.add(marker);
886
- changed = true;
887
- }
888
- if (changed) archive.file(entry.name, serializeUtf8Xml(document));
889
- }
890
- const missing = patches.filter((patch)=>!applied.has(patch.marker));
891
- if (missing.length) throw new Error(`DOCX hidden-text markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
892
- return archive.generateAsync({
893
- type: 'arraybuffer'
894
- });
895
- }
896
- function insertHiddenTextProperty(document, properties, value) {
897
- const namespace = properties.namespaceURI ?? '';
898
- const prefix = properties.prefix || 'w';
899
- const vanish = document.createElementNS(namespace, `${prefix}:vanish`);
900
- if (!value) vanish.setAttributeNS(namespace, `${prefix}:val`, '0');
901
- const next = directChildren(properties).find((child)=>{
902
- if (!DOCX_WORDPROCESSING_NAMESPACES.has(child.namespaceURI ?? '')) return true;
903
- return (DOCX_RUN_PROPERTY_RANK.get(child.localName) ?? 1 / 0) > VANISH_RANK;
904
- });
905
- properties.insertBefore(vanish, next ?? null);
906
- }
907
- function wordValue(element) {
908
- const values = Array.from(element.attributes).filter((attribute)=>'val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? ''));
909
- return 1 === values.length ? values[0].value : null;
910
- }
911
- function setWordValue(element, value) {
912
- const namespace = element.namespaceURI ?? '';
913
- const prefix = element.prefix || 'w';
914
- for (const attribute of Array.from(element.attributes))if ('val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? '')) element.removeAttributeNode(attribute);
915
- element.setAttributeNS(namespace, `${prefix}:val`, value);
916
- }
917
- const LEGACY_TEXT_EFFECTS_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
918
- class DocxLegacyTextEffectsPatchCollector {
919
- patches = [];
920
- nextMarker = 1;
921
- occupied;
922
- patchesByMarker = new Map();
923
- markersByEffectsAndStyle = new Map();
924
- constructor(source){
925
- this.occupied = source;
926
- }
927
- marker(value, inheritedStyle) {
928
- const effects = normalizeDocumentLegacyTextEffects(value);
929
- if (!effects || !Object.keys(effects).length) throw new Error('Document contains invalid legacy text effects.');
930
- const style = this.originalStyle(inheritedStyle);
931
- const key = JSON.stringify([
932
- DOCUMENT_LEGACY_TEXT_EFFECT_NAMES.map((name)=>effects[name] ?? null),
933
- style ?? null
934
- ]);
935
- const existing = this.markersByEffectsAndStyle.get(key);
936
- if (existing) return existing;
937
- let marker = '';
938
- do {
939
- marker = `A3SOfficeLegacyTextEffects${String(this.nextMarker).padStart(8, '0')}`;
940
- this.nextMarker += 1;
941
- }while (this.occupied.includes(marker) || this.patches.some((patch)=>patch.marker === marker))
942
- const patch = {
943
- marker,
944
- effects,
945
- ...style ? {
946
- style
947
- } : {}
948
- };
949
- this.patches.push(patch);
950
- this.patchesByMarker.set(marker, patch);
951
- this.markersByEffectsAndStyle.set(key, marker);
952
- return marker;
953
- }
954
- lookup(marker) {
955
- return marker ? this.patchesByMarker.get(marker) : void 0;
956
- }
957
- originalStyle(style) {
958
- let current = style;
959
- const seen = new Set();
960
- while(current && !seen.has(current)){
961
- seen.add(current);
962
- const patch = this.patchesByMarker.get(current);
963
- if (!patch) break;
964
- current = patch.style;
965
- }
966
- return current;
967
- }
968
- }
969
- async function patchDocxLegacyTextEffects(buffer, patches) {
970
- if (!patches.length) return buffer;
971
- const byMarker = new Map();
972
- for (const patch of patches){
973
- const effects = normalizeDocumentLegacyTextEffects(patch.effects);
974
- if (!patch.marker || !effects || !Object.keys(effects).length || byMarker.has(patch.marker)) throw new Error('Document contains invalid legacy text-effect patches.');
975
- byMarker.set(patch.marker, {
976
- ...patch,
977
- effects
978
- });
979
- }
980
- const archive = await jszip.loadAsync(buffer);
981
- const applied = new Set();
982
- for (const entry of Object.values(archive.files)){
983
- if (entry.dir || !LEGACY_TEXT_EFFECTS_PART_PATTERN.test(entry.name)) continue;
984
- const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
985
- let changed = false;
986
- for (const properties of descendants(document, 'rPr')){
987
- if (!DOCX_WORDPROCESSING_NAMESPACES.has(properties.namespaceURI ?? '')) continue;
988
- const markerStyles = directChildren(properties, 'rStyle').filter((style)=>{
989
- if (!DOCX_WORDPROCESSING_NAMESPACES.has(style.namespaceURI ?? '')) return false;
990
- const marker = work_docx_legacy_text_effects_export_wordValue(style);
991
- return null !== marker && byMarker.has(marker);
992
- });
993
- if (!markerStyles.length) continue;
994
- if (1 !== markerStyles.length) throw new Error('Generated DOCX legacy text-effect marker is duplicated in one run.');
995
- const marker = work_docx_legacy_text_effects_export_wordValue(markerStyles[0]);
996
- const patch = marker ? byMarker.get(marker) : void 0;
997
- if (marker && patch) {
998
- if (DOCUMENT_LEGACY_TEXT_EFFECT_NAMES.some((name)=>directChildren(properties, name).length > 0)) throw new Error('Generated DOCX legacy text-effect marker unexpectedly has a native value.');
999
- if (patch.style) work_docx_legacy_text_effects_export_setWordValue(markerStyles[0], patch.style);
1000
- else markerStyles[0].remove();
1001
- for (const name of DOCUMENT_LEGACY_TEXT_EFFECT_NAMES){
1002
- const value = patch.effects[name];
1003
- if (void 0 !== value) insertLegacyTextEffectProperty(document, properties, name, value);
1004
- }
1005
- applied.add(marker);
1006
- changed = true;
1007
- }
1008
- }
1009
- if (changed) archive.file(entry.name, serializeUtf8Xml(document));
1010
- }
1011
- const missing = patches.filter((patch)=>!applied.has(patch.marker));
1012
- if (missing.length) throw new Error(`DOCX legacy text-effect markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
1013
- return archive.generateAsync({
1014
- type: 'arraybuffer'
1015
- });
1016
- }
1017
- function insertLegacyTextEffectProperty(document, properties, name, value) {
1018
- const namespace = properties.namespaceURI ?? '';
1019
- const prefix = properties.prefix || 'w';
1020
- const effect = document.createElementNS(namespace, `${prefix}:${name}`);
1021
- if (!value) effect.setAttributeNS(namespace, `${prefix}:val`, '0');
1022
- const rank = DOCX_RUN_PROPERTY_RANK.get(name) ?? 0;
1023
- const next = directChildren(properties).find((child)=>{
1024
- if (!DOCX_WORDPROCESSING_NAMESPACES.has(child.namespaceURI ?? '')) return true;
1025
- return (DOCX_RUN_PROPERTY_RANK.get(child.localName) ?? 1 / 0) > rank;
1026
- });
1027
- properties.insertBefore(effect, next ?? null);
1028
- }
1029
- function work_docx_legacy_text_effects_export_wordValue(element) {
1030
- const values = Array.from(element.attributes).filter((attribute)=>'val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? ''));
1031
- return 1 === values.length ? values[0].value : null;
1032
- }
1033
- function work_docx_legacy_text_effects_export_setWordValue(element, value) {
1034
- const namespace = element.namespaceURI ?? '';
1035
- const prefix = element.prefix || 'w';
1036
- for (const attribute of Array.from(element.attributes))if ('val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? '')) element.removeAttributeNode(attribute);
1037
- element.setAttributeNS(namespace, `${prefix}:val`, value);
1038
- }
1039
760
  const MATH_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/math';
1040
761
  const work_docx_equation_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
1041
762
  const WORD_DATE_UTC_NAMESPACE = 'http://schemas.microsoft.com/office/word/2023/wordml/word16du';
@@ -2280,278 +2001,99 @@ async function imageToDocx(element, docx, cropPatches, wrapPatches, layerPatches
2280
2001
  return new docx.TextRun(`[${alt}]`);
2281
2002
  }
2282
2003
  }
2283
- function documentImageFloatingOptions(element, docx) {
2284
- const image = documentImageLayoutFromElement(element);
2285
- const position = documentImagePositionFromElement(element);
2286
- if ('inline' === image.layout) return;
2287
- const layer = documentImageLayerFromElement(element);
2288
- const distance = Math.round(36000 * image.wrapDistance);
2289
- const align = 'left' === image.alignment ? docx.HorizontalPositionAlign.LEFT : 'right' === image.alignment ? docx.HorizontalPositionAlign.RIGHT : docx.HorizontalPositionAlign.CENTER;
2290
- return {
2291
- horizontalPosition: {
2292
- relative: horizontalPositionReference(position?.horizontalReference ?? 'column', docx),
2293
- ...position?.horizontalOffset !== null && position?.horizontalOffset !== void 0 ? {
2294
- offset: millimetersToEmus(position.horizontalOffset)
2295
- } : {
2296
- align
2297
- }
2298
- },
2299
- verticalPosition: {
2300
- relative: verticalPositionReference(position?.verticalReference ?? 'paragraph', docx),
2301
- offset: millimetersToEmus(position?.verticalOffset ?? 0)
2302
- },
2303
- allowOverlap: layer.allowOverlap,
2304
- behindDocument: layer.behindDocument,
2305
- layoutInCell: layer.layoutInCell,
2306
- lockAnchor: layer.lockAnchor,
2307
- zIndex: layer.relativeHeight,
2308
- margins: {
2309
- top: distance,
2310
- right: distance,
2311
- bottom: distance,
2312
- left: distance
2313
- },
2314
- wrap: {
2315
- type: 'none' === image.layout ? docx.TextWrappingType.NONE : wrapsBesideImage(image.layout) ? 'square' === image.layout ? docx.TextWrappingType.SQUARE : docx.TextWrappingType.TIGHT : docx.TextWrappingType.TOP_AND_BOTTOM,
2316
- ...wrapsBesideImage(image.layout) ? {
2317
- side: textWrappingSide(image.wrapSide, docx)
2318
- } : {}
2319
- }
2320
- };
2321
- }
2322
- function textWrappingSide(value, docx) {
2323
- if ('left' === value) return docx.TextWrappingSide.LEFT;
2324
- if ('right' === value) return docx.TextWrappingSide.RIGHT;
2325
- if ('largest' === value) return docx.TextWrappingSide.LARGEST;
2326
- return docx.TextWrappingSide.BOTH_SIDES;
2327
- }
2328
- function horizontalPositionReference(value, docx) {
2329
- if ('margin' === value) return docx.HorizontalPositionRelativeFrom.MARGIN;
2330
- if ('page' === value) return docx.HorizontalPositionRelativeFrom.PAGE;
2331
- return docx.HorizontalPositionRelativeFrom.COLUMN;
2332
- }
2333
- function verticalPositionReference(value, docx) {
2334
- if ('margin' === value) return docx.VerticalPositionRelativeFrom.MARGIN;
2335
- if ('page' === value) return docx.VerticalPositionRelativeFrom.PAGE;
2336
- return docx.VerticalPositionRelativeFrom.PARAGRAPH;
2337
- }
2338
- function millimetersToEmus(value) {
2339
- return Math.round(36000 * value);
2340
- }
2341
- function docxImageType(contentType, source) {
2342
- const value = `${contentType} ${source}`.toLowerCase();
2343
- if (value.includes('png')) return 'png';
2344
- if (value.includes('jpeg') || value.includes('jpg')) return 'jpg';
2345
- if (value.includes('gif')) return 'gif';
2346
- if (value.includes('bmp')) return 'bmp';
2347
- return null;
2348
- }
2349
- function imageDimensions(blob) {
2350
- return new Promise((resolve, reject)=>{
2351
- const url = URL.createObjectURL(blob);
2352
- const image = new window.Image();
2353
- image.addEventListener('load', ()=>{
2354
- URL.revokeObjectURL(url);
2355
- resolve({
2356
- width: image.naturalWidth || 640,
2357
- height: image.naturalHeight || 360
2358
- });
2359
- }, {
2360
- once: true
2361
- });
2362
- image.addEventListener('error', ()=>{
2363
- URL.revokeObjectURL(url);
2364
- reject(new Error('Image dimensions could not be read'));
2365
- }, {
2366
- once: true
2367
- });
2368
- image.src = url;
2369
- });
2370
- }
2371
- function docxDocumentFieldRun(element, docx) {
2372
- const instruction = element.dataset.fieldInstruction?.trim() ?? '';
2373
- const display = element.dataset.fieldDisplay?.trim() || element.textContent?.trim() || '';
2374
- if (!docxDocumentFieldKind(instruction)) return new docx.TextRun(display);
2375
- return new docx.SimpleField(instruction, display);
2376
- }
2377
- const work_docx_index_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
2378
- const work_docx_index_export_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
2379
- class DocxIndexPatchCollector {
2380
- patches = [];
2381
- add(element) {
2382
- const value = documentIndexValueFromElement(element);
2383
- const patch = {
2384
- marker: `__A3S_WORK_DOCUMENT_INDEX_${this.patches.length + 1}__`,
2385
- options: value.options,
2386
- entries: value.entries
2387
- };
2388
- this.patches.push(patch);
2389
- return patch;
2390
- }
2391
- }
2392
- function docxDocumentIndex(element, docx, collector) {
2393
- const patch = collector.add(element);
2394
- return [
2395
- new docx.Paragraph({
2396
- children: [
2397
- new docx.TextRun(patch.marker)
2398
- ]
2399
- })
2400
- ];
2401
- }
2402
- function docxIndexEntryRun(element, docx) {
2403
- const value = documentIndexEntryFromElement(element);
2404
- return value ? new docx.SimpleField(documentIndexEntryInstruction(value)) : null;
2405
- }
2406
- async function patchDocxIndexes(buffer, patches) {
2407
- if (!patches.length) return buffer;
2408
- const archive = await jszip.loadAsync(buffer);
2409
- const entry = archive.file('word/document.xml');
2410
- if (!entry) return buffer;
2411
- const document = parseXml(await entry.async('text'), 'generated DOCX word/document.xml');
2412
- const paragraphs = descendants(document, 'p');
2413
- for (const patch of patches){
2414
- const paragraph = paragraphs.find((candidate)=>descendants(candidate, 't').some((text)=>(text.textContent ?? '').includes(patch.marker)));
2415
- if (!paragraph) throw new Error(`Generated DOCX did not emit the marker for document index ${patch.marker}.`);
2416
- paragraph.replaceWith(createIndexContentControl(document, patch));
2417
- }
2418
- archive.file('word/document.xml', new XMLSerializer().serializeToString(document));
2419
- return archive.generateAsync({
2420
- type: 'arraybuffer'
2421
- });
2422
- }
2423
- function createIndexContentControl(document, patch) {
2424
- const control = wordElement(document, 'sdt');
2425
- const properties = wordElement(document, 'sdtPr');
2426
- const documentPart = wordElement(document, 'docPartObj');
2427
- const gallery = wordElement(document, 'docPartGallery');
2428
- work_docx_index_export_setWordAttribute(document, gallery, 'val', 'Indexes');
2429
- documentPart.append(gallery, wordElement(document, 'docPartUnique'));
2430
- properties.append(documentPart);
2431
- const content = wordElement(document, 'sdtContent');
2432
- const fieldStart = indexFieldStartParagraph(document, patch.options);
2433
- content.append(fieldStart);
2434
- const cached = cachedIndexParagraphs(document, patch);
2435
- if (!cached.length) cached.push(emptyIndexParagraph(document));
2436
- const last = cached.at(-1);
2437
- last?.append(fieldCharacterRun(document, 'end'));
2438
- content.append(...cached);
2439
- control.append(properties, content);
2440
- return control;
2441
- }
2442
- function indexFieldStartParagraph(document, options) {
2443
- const paragraph = wordElement(document, 'p');
2444
- paragraph.append(fieldCharacterRun(document, 'begin'));
2445
- const instructionRun = wordElement(document, 'r');
2446
- const instruction = wordElement(document, 'instrText');
2447
- instruction.setAttributeNS(work_docx_index_export_XML_NAMESPACE, 'xml:space', 'preserve');
2448
- instruction.textContent = ` ${documentIndexInstruction(options)} `;
2449
- instructionRun.append(instruction);
2450
- paragraph.append(instructionRun, fieldCharacterRun(document, 'separate'));
2451
- return paragraph;
2452
- }
2453
- function cachedIndexParagraphs(document, patch) {
2454
- const paragraphs = [];
2455
- let currentMain = '';
2456
- for (const entry of patch.entries){
2457
- if (entry.subEntry && entry.mainEntry !== currentMain) paragraphs.push(cachedIndexParagraph(document, patch.options, {
2458
- style: 'Index1',
2459
- term: entry.mainEntry,
2460
- crossReference: '',
2461
- pages: []
2462
- }));
2463
- paragraphs.push(cachedIndexParagraph(document, patch.options, {
2464
- style: entry.subEntry ? 'Index2' : 'Index1',
2465
- term: entry.subEntry || entry.mainEntry,
2466
- crossReference: entry.crossReference,
2467
- pages: entry.pages
2468
- }));
2469
- currentMain = entry.mainEntry;
2470
- }
2471
- return paragraphs;
2472
- }
2473
- function cachedIndexParagraph(document, options, source) {
2474
- const paragraph = wordElement(document, 'p');
2475
- const properties = wordElement(document, 'pPr');
2476
- const style = wordElement(document, 'pStyle');
2477
- work_docx_index_export_setWordAttribute(document, style, 'val', source.style);
2478
- properties.append(style);
2479
- if (source.pages.length && options.rightAlignPageNumbers) {
2480
- const tabs = wordElement(document, 'tabs');
2481
- const tab = wordElement(document, 'tab');
2482
- work_docx_index_export_setWordAttribute(document, tab, 'val', 'right');
2483
- const leader = docxIndexLeader(options.leader);
2484
- if (leader) work_docx_index_export_setWordAttribute(document, tab, 'leader', leader);
2485
- work_docx_index_export_setWordAttribute(document, tab, 'pos', '9360');
2486
- tabs.append(tab);
2487
- properties.append(tabs);
2488
- }
2489
- paragraph.append(properties, textRun(document, source.term));
2490
- if (source.crossReference) {
2491
- paragraph.append(textRun(document, `, See ${source.crossReference}`));
2492
- return paragraph;
2493
- }
2494
- if (!source.pages.length) return paragraph;
2495
- paragraph.append(options.rightAlignPageNumbers ? tabRun(document) : textRun(document, ', '));
2496
- for (const [index, page] of source.pages.entries()){
2497
- if (index > 0) paragraph.append(textRun(document, ', '));
2498
- paragraph.append(textRun(document, String(page.pageNumber), {
2499
- bold: page.pageBold,
2500
- italic: page.pageItalic
2501
- }));
2502
- }
2503
- return paragraph;
2004
+ function documentImageFloatingOptions(element, docx) {
2005
+ const image = documentImageLayoutFromElement(element);
2006
+ const position = documentImagePositionFromElement(element);
2007
+ if ('inline' === image.layout) return;
2008
+ const layer = documentImageLayerFromElement(element);
2009
+ const distance = Math.round(36000 * image.wrapDistance);
2010
+ const align = 'left' === image.alignment ? docx.HorizontalPositionAlign.LEFT : 'right' === image.alignment ? docx.HorizontalPositionAlign.RIGHT : docx.HorizontalPositionAlign.CENTER;
2011
+ return {
2012
+ horizontalPosition: {
2013
+ relative: horizontalPositionReference(position?.horizontalReference ?? 'column', docx),
2014
+ ...position?.horizontalOffset !== null && position?.horizontalOffset !== void 0 ? {
2015
+ offset: millimetersToEmus(position.horizontalOffset)
2016
+ } : {
2017
+ align
2018
+ }
2019
+ },
2020
+ verticalPosition: {
2021
+ relative: verticalPositionReference(position?.verticalReference ?? 'paragraph', docx),
2022
+ offset: millimetersToEmus(position?.verticalOffset ?? 0)
2023
+ },
2024
+ allowOverlap: layer.allowOverlap,
2025
+ behindDocument: layer.behindDocument,
2026
+ layoutInCell: layer.layoutInCell,
2027
+ lockAnchor: layer.lockAnchor,
2028
+ zIndex: layer.relativeHeight,
2029
+ margins: {
2030
+ top: distance,
2031
+ right: distance,
2032
+ bottom: distance,
2033
+ left: distance
2034
+ },
2035
+ wrap: {
2036
+ type: 'none' === image.layout ? docx.TextWrappingType.NONE : wrapsBesideImage(image.layout) ? 'square' === image.layout ? docx.TextWrappingType.SQUARE : docx.TextWrappingType.TIGHT : docx.TextWrappingType.TOP_AND_BOTTOM,
2037
+ ...wrapsBesideImage(image.layout) ? {
2038
+ side: textWrappingSide(image.wrapSide, docx)
2039
+ } : {}
2040
+ }
2041
+ };
2504
2042
  }
2505
- function emptyIndexParagraph(document) {
2506
- const paragraph = wordElement(document, 'p');
2507
- const properties = wordElement(document, 'pPr');
2508
- const style = wordElement(document, 'pStyle');
2509
- work_docx_index_export_setWordAttribute(document, style, 'val', 'Index1');
2510
- properties.append(style);
2511
- paragraph.append(properties);
2512
- return paragraph;
2043
+ function textWrappingSide(value, docx) {
2044
+ if ('left' === value) return docx.TextWrappingSide.LEFT;
2045
+ if ('right' === value) return docx.TextWrappingSide.RIGHT;
2046
+ if ('largest' === value) return docx.TextWrappingSide.LARGEST;
2047
+ return docx.TextWrappingSide.BOTH_SIDES;
2513
2048
  }
2514
- function textRun(document, value, style = {}) {
2515
- const run = wordElement(document, 'r');
2516
- if (style.bold || style.italic) {
2517
- const properties = wordElement(document, 'rPr');
2518
- if (style.bold) properties.append(wordElement(document, 'b'));
2519
- if (style.italic) properties.append(wordElement(document, 'i'));
2520
- run.append(properties);
2521
- }
2522
- const text = wordElement(document, 't');
2523
- if (/^\s|\s$/.test(value)) text.setAttributeNS(work_docx_index_export_XML_NAMESPACE, 'xml:space', 'preserve');
2524
- text.textContent = value;
2525
- run.append(text);
2526
- return run;
2049
+ function horizontalPositionReference(value, docx) {
2050
+ if ('margin' === value) return docx.HorizontalPositionRelativeFrom.MARGIN;
2051
+ if ('page' === value) return docx.HorizontalPositionRelativeFrom.PAGE;
2052
+ return docx.HorizontalPositionRelativeFrom.COLUMN;
2527
2053
  }
2528
- function tabRun(document) {
2529
- const run = wordElement(document, 'r');
2530
- run.append(wordElement(document, 'tab'));
2531
- return run;
2054
+ function verticalPositionReference(value, docx) {
2055
+ if ('margin' === value) return docx.VerticalPositionRelativeFrom.MARGIN;
2056
+ if ('page' === value) return docx.VerticalPositionRelativeFrom.PAGE;
2057
+ return docx.VerticalPositionRelativeFrom.PARAGRAPH;
2532
2058
  }
2533
- function fieldCharacterRun(document, type) {
2534
- const run = wordElement(document, 'r');
2535
- const character = wordElement(document, 'fldChar');
2536
- work_docx_index_export_setWordAttribute(document, character, 'fldCharType', type);
2537
- run.append(character);
2538
- return run;
2059
+ function millimetersToEmus(value) {
2060
+ return Math.round(36000 * value);
2539
2061
  }
2540
- function docxIndexLeader(value) {
2541
- if ('dash' === value) return 'hyphen';
2542
- if ('underline' === value) return 'underscore';
2543
- if ('dot' === value) return 'dot';
2062
+ function docxImageType(contentType, source) {
2063
+ const value = `${contentType} ${source}`.toLowerCase();
2064
+ if (value.includes('png')) return 'png';
2065
+ if (value.includes('jpeg') || value.includes('jpg')) return 'jpg';
2066
+ if (value.includes('gif')) return 'gif';
2067
+ if (value.includes('bmp')) return 'bmp';
2544
2068
  return null;
2545
2069
  }
2546
- function wordElement(document, localName) {
2547
- return document.createElementNS(work_docx_index_export_WORD_NAMESPACE, wordQualifiedName(document.documentElement, localName));
2548
- }
2549
- function work_docx_index_export_setWordAttribute(document, element, localName, value) {
2550
- for (const candidate of Array.from(element.attributes))if (candidate.localName === localName && (candidate.namespaceURI === work_docx_index_export_WORD_NAMESPACE || !candidate.namespaceURI)) element.removeAttributeNode(candidate);
2551
- element.setAttributeNS(work_docx_index_export_WORD_NAMESPACE, wordQualifiedName(document.documentElement, localName), value);
2070
+ function imageDimensions(blob) {
2071
+ return new Promise((resolve, reject)=>{
2072
+ const url = URL.createObjectURL(blob);
2073
+ const image = new window.Image();
2074
+ image.addEventListener('load', ()=>{
2075
+ URL.revokeObjectURL(url);
2076
+ resolve({
2077
+ width: image.naturalWidth || 640,
2078
+ height: image.naturalHeight || 360
2079
+ });
2080
+ }, {
2081
+ once: true
2082
+ });
2083
+ image.addEventListener('error', ()=>{
2084
+ URL.revokeObjectURL(url);
2085
+ reject(new Error('Image dimensions could not be read'));
2086
+ }, {
2087
+ once: true
2088
+ });
2089
+ image.src = url;
2090
+ });
2552
2091
  }
2553
- function wordQualifiedName(root, localName) {
2554
- return `${xmlNamespacePrefix(root, work_docx_index_export_WORD_NAMESPACE) ?? 'w'}:${localName}`;
2092
+ function docxDocumentFieldRun(element, docx) {
2093
+ const instruction = element.dataset.fieldInstruction?.trim() ?? '';
2094
+ const display = element.dataset.fieldDisplay?.trim() || element.textContent?.trim() || '';
2095
+ if (!docxDocumentFieldKind(instruction)) return new docx.TextRun(display);
2096
+ return new docx.SimpleField(instruction, display);
2555
2097
  }
2556
2098
  const work_docx_run_shading_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
2557
2099
  class DocxRunShadingPatchCollector {
@@ -2735,16 +2277,16 @@ async function patchDocxOpenTypeFeatures(buffer, patches) {
2735
2277
  if (!DOCX_WORDPROCESSING_NAMESPACES.has(properties.namespaceURI ?? '')) continue;
2736
2278
  const markerStyles = directChildren(properties, 'rStyle').filter((style)=>{
2737
2279
  if (!DOCX_WORDPROCESSING_NAMESPACES.has(style.namespaceURI ?? '')) return false;
2738
- const marker = work_docx_opentype_export_wordValue(style);
2280
+ const marker = wordValue(style);
2739
2281
  return null !== marker && byMarker.has(marker);
2740
2282
  });
2741
2283
  if (!markerStyles.length) continue;
2742
2284
  if (1 !== markerStyles.length) throw new Error('Generated DOCX OpenType marker is duplicated in one run.');
2743
- const marker = work_docx_opentype_export_wordValue(markerStyles[0]);
2285
+ const marker = wordValue(markerStyles[0]);
2744
2286
  const patch = marker ? byMarker.get(marker) : void 0;
2745
2287
  if (marker && patch) {
2746
2288
  if (directChildren(properties).some((child)=>child.namespaceURI === DOCX_WORD_2010_NAMESPACE && OPEN_TYPE_PROPERTY_ORDER.includes(child.localName))) throw new Error('Generated DOCX OpenType marker unexpectedly has a native value.');
2747
- if (patch.style) work_docx_opentype_export_setWordValue(markerStyles[0], patch.style);
2289
+ if (patch.style) setWordValue(markerStyles[0], patch.style);
2748
2290
  else markerStyles[0].remove();
2749
2291
  appendDocxOpenTypeProperties(document, properties, patch.features);
2750
2292
  applied.add(marker);
@@ -2818,11 +2360,11 @@ function ensureIgnorable(root, word2010Prefix) {
2818
2360
  if (!tokens.includes(word2010Prefix)) tokens.push(word2010Prefix);
2819
2361
  root.setAttributeNS(work_docx_opentype_export_MARKUP_COMPATIBILITY_NAMESPACE, `${prefix}:Ignorable`, tokens.join(' '));
2820
2362
  }
2821
- function work_docx_opentype_export_wordValue(element) {
2363
+ function wordValue(element) {
2822
2364
  const values = Array.from(element.attributes).filter((attribute)=>'val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? ''));
2823
2365
  return 1 === values.length ? values[0].value : null;
2824
2366
  }
2825
- function work_docx_opentype_export_setWordValue(element, value) {
2367
+ function setWordValue(element, value) {
2826
2368
  const namespace = element.namespaceURI ?? '';
2827
2369
  const prefix = element.prefix || 'w';
2828
2370
  for (const attribute of Array.from(element.attributes))if ('val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? '')) element.removeAttributeNode(attribute);
@@ -2928,17 +2470,17 @@ function setRunFormattingChange(document, run, patch) {
2928
2470
  if (existing.length > 1) throw new Error('Generated DOCX run contains duplicate properties.');
2929
2471
  const properties = existing[0] ?? insertRunProperties(document, run, wordNamespace, prefix);
2930
2472
  if (directChildren(properties, 'rPrChange').some((element)=>element.namespaceURI === wordNamespace)) throw new Error('Generated DOCX run already contains a formatting revision.');
2931
- const change = work_docx_format_change_export_wordElement(document, wordNamespace, prefix, 'rPrChange');
2473
+ const change = wordElement(document, wordNamespace, prefix, 'rPrChange');
2932
2474
  work_docx_format_change_export_setWordAttribute(change, wordNamespace, prefix, 'id', String(patch.id));
2933
2475
  work_docx_format_change_export_setWordAttribute(change, wordNamespace, prefix, 'author', patch.author);
2934
2476
  work_docx_format_change_export_setWordAttribute(change, wordNamespace, prefix, 'date', patch.date);
2935
- const before = work_docx_format_change_export_wordElement(document, wordNamespace, prefix, 'rPr');
2477
+ const before = wordElement(document, wordNamespace, prefix, 'rPr');
2936
2478
  appendFormattingProperties(document, before, wordNamespace, prefix, patch.before);
2937
2479
  change.append(before);
2938
2480
  properties.append(change);
2939
2481
  }
2940
2482
  function insertRunProperties(document, run, namespace, prefix) {
2941
- const properties = work_docx_format_change_export_wordElement(document, namespace, prefix, 'rPr');
2483
+ const properties = wordElement(document, namespace, prefix, 'rPr');
2942
2484
  run.insertBefore(properties, run.firstChild);
2943
2485
  return properties;
2944
2486
  }
@@ -2982,13 +2524,13 @@ function appendOpenTypeProperties(document, properties, mark) {
2982
2524
  function appendNoProofProperty(document, properties, namespace, prefix, mark) {
2983
2525
  const noProof = normalizeDocumentNoProof(mark?.attrs?.noProof);
2984
2526
  if (null === noProof) return;
2985
- if (noProof) properties.append(work_docx_format_change_export_wordElement(document, namespace, prefix, 'noProof'));
2527
+ if (noProof) properties.append(wordElement(document, namespace, prefix, 'noProof'));
2986
2528
  else appendValuedProperty(document, properties, namespace, prefix, 'noProof', '0');
2987
2529
  }
2988
2530
  function appendProofingLanguagesProperty(document, properties, namespace, prefix, mark) {
2989
2531
  const languages = parseDocumentProofingLanguages(mark?.attrs?.proofingLanguages);
2990
2532
  if (!languages) return;
2991
- const element = work_docx_format_change_export_wordElement(document, namespace, prefix, 'lang');
2533
+ const element = wordElement(document, namespace, prefix, 'lang');
2992
2534
  if (languages.latin) work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'val', languages.latin);
2993
2535
  if (languages.eastAsia) work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'eastAsia', languages.eastAsia);
2994
2536
  if (languages.bidi) work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'bidi', languages.bidi);
@@ -2997,7 +2539,7 @@ function appendProofingLanguagesProperty(document, properties, namespace, prefix
2997
2539
  function appendRunBorderProperty(document, properties, namespace, prefix, mark) {
2998
2540
  const border = parseDocumentRunBorder(mark?.attrs?.runBorder);
2999
2541
  if (!border) return;
3000
- const element = work_docx_format_change_export_wordElement(document, namespace, prefix, 'bdr');
2542
+ const element = wordElement(document, namespace, prefix, 'bdr');
3001
2543
  work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'val', border.style);
3002
2544
  if (border.color) {
3003
2545
  work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'color', 'auto' === border.color.value ? 'auto' : border.color.value.slice(1).toUpperCase());
@@ -3016,7 +2558,7 @@ function appendRunBorderProperty(document, properties, namespace, prefix, mark)
3016
2558
  function appendRunShadingProperty(document, properties, namespace, prefix, mark) {
3017
2559
  const shading = parseDocumentRunShading(mark?.attrs?.runShading);
3018
2560
  if (!shading) return;
3019
- const element = work_docx_format_change_export_wordElement(document, namespace, prefix, 'shd');
2561
+ const element = wordElement(document, namespace, prefix, 'shd');
3020
2562
  if (!setDocxRunShadingAttributes(document, element, shading)) return;
3021
2563
  properties.append(element);
3022
2564
  }
@@ -3047,14 +2589,14 @@ function appendLegacyTextEffectsProperties(document, properties, namespace, pref
3047
2589
  if (documentLegacyTextEffectsConflict(effects)) throw new Error('Document contains conflicting legacy text effects.');
3048
2590
  for (const name of DOCUMENT_LEGACY_TEXT_EFFECT_NAMES){
3049
2591
  const value = effects[name];
3050
- if (void 0 !== value) if (value) properties.append(work_docx_format_change_export_wordElement(document, namespace, prefix, name));
2592
+ if (void 0 !== value) if (value) properties.append(wordElement(document, namespace, prefix, name));
3051
2593
  else appendValuedProperty(document, properties, namespace, prefix, name, '0');
3052
2594
  }
3053
2595
  }
3054
2596
  function appendHiddenTextProperty(document, properties, namespace, prefix, mark) {
3055
2597
  const hiddenText = normalizeDocumentHiddenText(mark?.attrs?.hiddenText);
3056
2598
  if (null === hiddenText) return;
3057
- if (hiddenText) properties.append(work_docx_format_change_export_wordElement(document, namespace, prefix, 'vanish'));
2599
+ if (hiddenText) properties.append(wordElement(document, namespace, prefix, 'vanish'));
3058
2600
  else appendValuedProperty(document, properties, namespace, prefix, 'vanish', '0');
3059
2601
  }
3060
2602
  function appendEmphasisMarkProperty(document, properties, namespace, prefix, mark) {
@@ -3085,7 +2627,7 @@ function appendCharacterSpacingProperty(document, properties, namespace, prefix,
3085
2627
  function appendFontProperties(document, properties, namespace, prefix, mark) {
3086
2628
  const scriptFonts = parseDocumentScriptFonts('string' == typeof mark?.attrs?.scriptFonts ? mark.attrs.scriptFonts : void 0);
3087
2629
  if (scriptFonts) {
3088
- const fonts = work_docx_format_change_export_wordElement(document, namespace, prefix, 'rFonts');
2630
+ const fonts = wordElement(document, namespace, prefix, 'rFonts');
3089
2631
  for (const [name, value] of [
3090
2632
  [
3091
2633
  'ascii',
@@ -3129,7 +2671,7 @@ function appendFontProperties(document, properties, namespace, prefix, mark) {
3129
2671
  }
3130
2672
  const family = firstFontFamily(mark?.attrs?.fontFamily);
3131
2673
  if (!family) return;
3132
- const fonts = work_docx_format_change_export_wordElement(document, namespace, prefix, 'rFonts');
2674
+ const fonts = wordElement(document, namespace, prefix, 'rFonts');
3133
2675
  for (const name of [
3134
2676
  'ascii',
3135
2677
  'hAnsi',
@@ -3143,7 +2685,7 @@ function appendUnderlineProperty(document, properties, namespace, prefix, mark)
3143
2685
  const style = normalizeDocumentUnderlineStyle(mark.attrs?.underlineStyle) ?? 'single';
3144
2686
  const color = normalizedHex(mark.attrs?.underlineColor);
3145
2687
  const theme = themeReference(mark.attrs?.underlineThemeColor);
3146
- const underline = work_docx_format_change_export_wordElement(document, namespace, prefix, 'u');
2688
+ const underline = wordElement(document, namespace, prefix, 'u');
3147
2689
  work_docx_format_change_export_setWordAttribute(underline, namespace, prefix, 'val', style);
3148
2690
  const resolved = color ?? normalizedHex(theme?.resolved);
3149
2691
  if (resolved) work_docx_format_change_export_setWordAttribute(underline, namespace, prefix, 'color', resolved);
@@ -3151,7 +2693,7 @@ function appendUnderlineProperty(document, properties, namespace, prefix, mark)
3151
2693
  properties.append(underline);
3152
2694
  }
3153
2695
  function appendBooleanProperty(document, properties, namespace, prefix, marks, mark, property) {
3154
- if (marks.has(mark)) properties.append(work_docx_format_change_export_wordElement(document, namespace, prefix, property));
2696
+ if (marks.has(mark)) properties.append(wordElement(document, namespace, prefix, property));
3155
2697
  }
3156
2698
  function appendStrikeProperties(document, properties, namespace, prefix, mark) {
3157
2699
  if (!mark) return;
@@ -3160,14 +2702,14 @@ function appendStrikeProperties(document, properties, namespace, prefix, mark) {
3160
2702
  appendValuedProperty(document, properties, namespace, prefix, 'dstrike', 'double' === style ? '1' : '0');
3161
2703
  }
3162
2704
  function appendValuedProperty(document, properties, namespace, prefix, property, value) {
3163
- const element = work_docx_format_change_export_wordElement(document, namespace, prefix, property);
2705
+ const element = wordElement(document, namespace, prefix, property);
3164
2706
  work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'val', value);
3165
2707
  properties.append(element);
3166
2708
  }
3167
2709
  function appendTextCaseProperties(document, properties, namespace, prefix, mark) {
3168
2710
  const textCase = mark?.attrs?.textCase;
3169
- if ('all-caps' === textCase) properties.append(work_docx_format_change_export_wordElement(document, namespace, prefix, 'caps'));
3170
- else if ('small-caps' === textCase) properties.append(work_docx_format_change_export_wordElement(document, namespace, prefix, 'smallCaps'));
2711
+ if ('all-caps' === textCase) properties.append(wordElement(document, namespace, prefix, 'caps'));
2712
+ else if ('small-caps' === textCase) properties.append(wordElement(document, namespace, prefix, 'smallCaps'));
3171
2713
  else if ('none' === textCase) {
3172
2714
  appendValuedProperty(document, properties, namespace, prefix, 'caps', '0');
3173
2715
  appendValuedProperty(document, properties, namespace, prefix, 'smallCaps', '0');
@@ -3177,7 +2719,7 @@ function appendColorProperty(document, properties, namespace, prefix, mark) {
3177
2719
  const color = normalizedHex(mark?.attrs?.color);
3178
2720
  const theme = themeReference(mark?.attrs?.themeColor);
3179
2721
  if (!color && !theme) return;
3180
- const element = work_docx_format_change_export_wordElement(document, namespace, prefix, 'color');
2722
+ const element = wordElement(document, namespace, prefix, 'color');
3181
2723
  work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'val', color ?? normalizedHex(theme?.resolved) ?? '000000');
3182
2724
  if (theme) setThemeAttributes(element, namespace, prefix, theme, 'themeColor');
3183
2725
  properties.append(element);
@@ -3195,7 +2737,7 @@ function appendHighlightProperty(document, properties, namespace, prefix, mark,
3195
2737
  const fill = normalizedHex(mark?.attrs?.color);
3196
2738
  const theme = themeReference(mark?.attrs?.themeFill);
3197
2739
  if (!fill && !theme) return;
3198
- const shading = work_docx_format_change_export_wordElement(document, namespace, prefix, 'shd');
2740
+ const shading = wordElement(document, namespace, prefix, 'shd');
3199
2741
  work_docx_format_change_export_setWordAttribute(shading, namespace, prefix, 'val', 'clear');
3200
2742
  work_docx_format_change_export_setWordAttribute(shading, namespace, prefix, 'color', 'auto');
3201
2743
  work_docx_format_change_export_setWordAttribute(shading, namespace, prefix, 'fill', fill ?? normalizedHex(theme?.resolved) ?? 'FFFFFF');
@@ -3243,18 +2785,175 @@ function normalizedHex(value) {
3243
2785
  const match = /^#?([0-9a-f]{6})$/i.exec(value.trim());
3244
2786
  return match?.[1]?.toUpperCase() ?? null;
3245
2787
  }
3246
- function work_docx_format_change_export_wordElement(document, namespace, prefix, name) {
2788
+ function wordElement(document, namespace, prefix, name) {
3247
2789
  return document.createElementNS(namespace, `${prefix}:${name}`);
3248
2790
  }
3249
- function work_docx_format_change_export_setWordAttribute(element, namespace, prefix, name, value) {
3250
- element.setAttributeNS(namespace, `${prefix}:${name}`, value);
2791
+ function work_docx_format_change_export_setWordAttribute(element, namespace, prefix, name, value) {
2792
+ element.setAttributeNS(namespace, `${prefix}:${name}`, value);
2793
+ }
2794
+ function normalizedRevisionDate(value) {
2795
+ const time = Date.parse(value ?? '');
2796
+ return Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString();
2797
+ }
2798
+ function duplicateMarker(marker) {
2799
+ return new Error(`Generated DOCX marker '${marker}' is duplicated.`);
2800
+ }
2801
+ const DOCX_RUN_PROPERTY_ORDER = [
2802
+ 'rStyle',
2803
+ 'rFonts',
2804
+ 'b',
2805
+ 'bCs',
2806
+ 'i',
2807
+ 'iCs',
2808
+ 'caps',
2809
+ 'smallCaps',
2810
+ 'strike',
2811
+ 'dstrike',
2812
+ 'outline',
2813
+ 'shadow',
2814
+ 'emboss',
2815
+ 'imprint',
2816
+ 'noProof',
2817
+ 'snapToGrid',
2818
+ 'vanish',
2819
+ 'webHidden',
2820
+ 'color',
2821
+ 'spacing',
2822
+ 'w',
2823
+ 'kern',
2824
+ 'position',
2825
+ 'sz',
2826
+ 'szCs',
2827
+ 'highlight',
2828
+ 'u',
2829
+ 'effect',
2830
+ 'bdr',
2831
+ 'shd',
2832
+ 'fitText',
2833
+ 'vertAlign',
2834
+ 'rtl',
2835
+ 'cs',
2836
+ 'em',
2837
+ 'lang',
2838
+ 'eastAsianLayout',
2839
+ 'specVanish',
2840
+ 'rPrChange'
2841
+ ];
2842
+ const DOCX_RUN_PROPERTY_RANK = new Map(DOCX_RUN_PROPERTY_ORDER.map((name, index)=>[
2843
+ name,
2844
+ index
2845
+ ]));
2846
+ const HIDDEN_TEXT_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
2847
+ const VANISH_RANK = DOCX_RUN_PROPERTY_RANK.get('vanish') ?? 0;
2848
+ class DocxHiddenTextPatchCollector {
2849
+ patches = [];
2850
+ nextMarker = 1;
2851
+ occupied;
2852
+ patchesByMarker = new Map();
2853
+ markersByValueAndStyle = new Map();
2854
+ constructor(source){
2855
+ this.occupied = source;
2856
+ }
2857
+ marker(value, inheritedStyle) {
2858
+ const style = this.originalStyle(inheritedStyle);
2859
+ const key = JSON.stringify([
2860
+ value,
2861
+ style ?? null
2862
+ ]);
2863
+ const existing = this.markersByValueAndStyle.get(key);
2864
+ if (existing) return existing;
2865
+ let marker = '';
2866
+ do {
2867
+ marker = `A3SOfficeHiddenText${value ? 'On' : 'Off'}${String(this.nextMarker).padStart(8, '0')}`;
2868
+ this.nextMarker += 1;
2869
+ }while (this.occupied.includes(marker) || this.patches.some((patch)=>patch.marker === marker))
2870
+ const patch = {
2871
+ marker,
2872
+ value,
2873
+ ...style ? {
2874
+ style
2875
+ } : {}
2876
+ };
2877
+ this.patches.push(patch);
2878
+ this.patchesByMarker.set(marker, patch);
2879
+ this.markersByValueAndStyle.set(key, marker);
2880
+ return marker;
2881
+ }
2882
+ lookup(marker) {
2883
+ return marker ? this.patchesByMarker.get(marker) : void 0;
2884
+ }
2885
+ originalStyle(style) {
2886
+ let current = style;
2887
+ const seen = new Set();
2888
+ while(current && !seen.has(current)){
2889
+ seen.add(current);
2890
+ const patch = this.patchesByMarker.get(current);
2891
+ if (!patch) break;
2892
+ current = patch.style;
2893
+ }
2894
+ return current;
2895
+ }
2896
+ }
2897
+ async function patchDocxHiddenText(buffer, patches) {
2898
+ if (!patches.length) return buffer;
2899
+ const archive = await jszip.loadAsync(buffer);
2900
+ const byMarker = new Map(patches.map((patch)=>[
2901
+ patch.marker,
2902
+ patch
2903
+ ]));
2904
+ const applied = new Set();
2905
+ for (const entry of Object.values(archive.files)){
2906
+ if (entry.dir || !HIDDEN_TEXT_PART_PATTERN.test(entry.name)) continue;
2907
+ const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
2908
+ let changed = false;
2909
+ for (const properties of descendants(document, 'rPr')){
2910
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(properties.namespaceURI ?? '')) continue;
2911
+ const markerStyles = directChildren(properties, 'rStyle').filter((style)=>{
2912
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(style.namespaceURI ?? '')) return false;
2913
+ const marker = work_docx_hidden_text_export_wordValue(style);
2914
+ return null !== marker && byMarker.has(marker);
2915
+ });
2916
+ if (!markerStyles.length) continue;
2917
+ if (1 !== markerStyles.length) throw new Error('Generated DOCX hidden-text marker is duplicated in one run.');
2918
+ const marker = work_docx_hidden_text_export_wordValue(markerStyles[0]);
2919
+ const patch = marker ? byMarker.get(marker) : void 0;
2920
+ if (!marker || !patch) continue;
2921
+ const existing = directChildren(properties, 'vanish').filter((element)=>DOCX_WORDPROCESSING_NAMESPACES.has(element.namespaceURI ?? ''));
2922
+ if (existing.length) throw new Error('Generated DOCX hidden-text marker unexpectedly has a native value.');
2923
+ if (patch.style) work_docx_hidden_text_export_setWordValue(markerStyles[0], patch.style);
2924
+ else markerStyles[0].remove();
2925
+ insertHiddenTextProperty(document, properties, patch.value);
2926
+ applied.add(marker);
2927
+ changed = true;
2928
+ }
2929
+ if (changed) archive.file(entry.name, serializeUtf8Xml(document));
2930
+ }
2931
+ const missing = patches.filter((patch)=>!applied.has(patch.marker));
2932
+ if (missing.length) throw new Error(`DOCX hidden-text markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
2933
+ return archive.generateAsync({
2934
+ type: 'arraybuffer'
2935
+ });
2936
+ }
2937
+ function insertHiddenTextProperty(document, properties, value) {
2938
+ const namespace = properties.namespaceURI ?? '';
2939
+ const prefix = properties.prefix || 'w';
2940
+ const vanish = document.createElementNS(namespace, `${prefix}:vanish`);
2941
+ if (!value) vanish.setAttributeNS(namespace, `${prefix}:val`, '0');
2942
+ const next = directChildren(properties).find((child)=>{
2943
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(child.namespaceURI ?? '')) return true;
2944
+ return (DOCX_RUN_PROPERTY_RANK.get(child.localName) ?? 1 / 0) > VANISH_RANK;
2945
+ });
2946
+ properties.insertBefore(vanish, next ?? null);
3251
2947
  }
3252
- function normalizedRevisionDate(value) {
3253
- const time = Date.parse(value ?? '');
3254
- return Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString();
2948
+ function work_docx_hidden_text_export_wordValue(element) {
2949
+ const values = Array.from(element.attributes).filter((attribute)=>'val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? ''));
2950
+ return 1 === values.length ? values[0].value : null;
3255
2951
  }
3256
- function duplicateMarker(marker) {
3257
- return new Error(`Generated DOCX marker '${marker}' is duplicated.`);
2952
+ function work_docx_hidden_text_export_setWordValue(element, value) {
2953
+ const namespace = element.namespaceURI ?? '';
2954
+ const prefix = element.prefix || 'w';
2955
+ for (const attribute of Array.from(element.attributes))if ('val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? '')) element.removeAttributeNode(attribute);
2956
+ element.setAttributeNS(namespace, `${prefix}:val`, value);
3258
2957
  }
3259
2958
  class DocxImageCropPatchCollector {
3260
2959
  patches = [];
@@ -3438,119 +3137,420 @@ async function patchDocxImageLayers(buffer, patches) {
3438
3137
  }
3439
3138
  if (changed) archive.file(entry.name, new XMLSerializer().serializeToString(document));
3440
3139
  }
3441
- return archive.generateAsync({
3442
- type: 'arraybuffer'
3443
- });
3140
+ return archive.generateAsync({
3141
+ type: 'arraybuffer'
3142
+ });
3143
+ }
3144
+ function imageLayerPatch(properties, patches) {
3145
+ for (const property of Array.from(properties.attributes))for (const [marker, patch] of patches)if (property.value.includes(marker)) return patch;
3146
+ return null;
3147
+ }
3148
+ function removeLayerMarker(properties, marker) {
3149
+ for (const property of Array.from(properties.attributes))if (property.value.includes(marker)) property.value = property.value.replace(marker, '');
3150
+ }
3151
+ function closestAnchor(element) {
3152
+ let current = element;
3153
+ while(current){
3154
+ if ('anchor' === current.localName) return current;
3155
+ current = current.parentElement;
3156
+ }
3157
+ return null;
3158
+ }
3159
+ function applyImageLayer(anchor, source) {
3160
+ const layer = normalizeDocumentImageLayer(source);
3161
+ anchor.setAttribute('relativeHeight', String(layer.relativeHeight));
3162
+ anchor.setAttribute('behindDoc', layer.behindDocument ? '1' : '0');
3163
+ anchor.setAttribute('allowOverlap', layer.allowOverlap ? '1' : '0');
3164
+ anchor.setAttribute('layoutInCell', layer.layoutInCell ? '1' : '0');
3165
+ anchor.setAttribute('locked', layer.lockAnchor ? '1' : '0');
3166
+ }
3167
+ const WORDPROCESSING_DRAWING_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing';
3168
+ class DocxImageWrapPatchCollector {
3169
+ patches = [];
3170
+ nextMarker = 1;
3171
+ marker(options, contour) {
3172
+ if (!isContourImageLayout(options.layout)) return '';
3173
+ const marker = `__A3S_IMAGE_WRAP_${this.nextMarker}__`;
3174
+ this.nextMarker += 1;
3175
+ this.patches.push({
3176
+ marker,
3177
+ layout: options.layout,
3178
+ options,
3179
+ contour: contour ?? defaultDocumentImageWrapContour()
3180
+ });
3181
+ return marker;
3182
+ }
3183
+ }
3184
+ async function patchDocxImageWraps(buffer, patches) {
3185
+ if (!patches.length) return buffer;
3186
+ const archive = await jszip.loadAsync(buffer);
3187
+ const byMarker = new Map(patches.map((patch)=>[
3188
+ patch.marker,
3189
+ patch
3190
+ ]));
3191
+ const entries = Object.values(archive.files).filter((entry)=>!entry.dir && /^word\/(?:document|header\d+|footer\d+|footnotes|endnotes|comments)\.xml$/.test(entry.name));
3192
+ for (const entry of entries){
3193
+ const document = parseXml(await entry.async('text'), entry.name);
3194
+ let changed = false;
3195
+ for (const properties of descendants(document, 'docPr')){
3196
+ const patch = imageWrapPatch(properties, byMarker);
3197
+ if (!patch) continue;
3198
+ removeWrapMarker(properties, patch.marker);
3199
+ changed = true;
3200
+ const anchor = work_docx_image_wrap_closestAnchor(properties);
3201
+ const source = anchor ? wrappingElement(anchor) : null;
3202
+ if (anchor && source) source.parentNode?.replaceChild(createWrappingElement(anchor, patch), source);
3203
+ }
3204
+ if (changed) archive.file(entry.name, new XMLSerializer().serializeToString(document));
3205
+ }
3206
+ return archive.generateAsync({
3207
+ type: 'arraybuffer'
3208
+ });
3209
+ }
3210
+ function imageWrapPatch(properties, patches) {
3211
+ for (const attribute of Array.from(properties.attributes))for (const [marker, patch] of patches)if (attribute.value.includes(marker)) return patch;
3212
+ return null;
3213
+ }
3214
+ function removeWrapMarker(properties, marker) {
3215
+ for (const attribute of Array.from(properties.attributes))if (attribute.value.includes(marker)) attribute.value = attribute.value.replace(marker, '');
3216
+ }
3217
+ function work_docx_image_wrap_closestAnchor(element) {
3218
+ let current = element;
3219
+ while(current){
3220
+ if ('anchor' === current.localName) return current;
3221
+ current = current.parentElement;
3222
+ }
3223
+ return null;
3224
+ }
3225
+ function wrappingElement(anchor) {
3226
+ return directChildren(anchor).find((element)=>[
3227
+ 'wrapNone',
3228
+ 'wrapSquare',
3229
+ 'wrapTight',
3230
+ 'wrapThrough',
3231
+ 'wrapTopAndBottom'
3232
+ ].includes(element.localName)) ?? null;
3233
+ }
3234
+ function createWrappingElement(anchor, patch) {
3235
+ const document = anchor.ownerDocument;
3236
+ const namespace = anchor.namespaceURI ?? WORDPROCESSING_DRAWING_NAMESPACE;
3237
+ const prefix = anchor.prefix ? `${anchor.prefix}:` : '';
3238
+ const wrap = document.createElementNS(namespace, `${prefix}${'tight' === patch.layout ? 'wrapTight' : 'wrapThrough'}`);
3239
+ wrap.setAttribute('wrapText', patch.options.wrapSide);
3240
+ const distance = String(Math.round(36000 * patch.options.wrapDistance));
3241
+ wrap.setAttribute('distL', distance);
3242
+ wrap.setAttribute('distR', distance);
3243
+ const polygon = document.createElementNS(namespace, `${prefix}wrapPolygon`);
3244
+ polygon.setAttribute('edited', patch.contour.edited ? '1' : '0');
3245
+ patch.contour.points.forEach((point, index)=>{
3246
+ const child = document.createElementNS(namespace, `${prefix}${0 === index ? 'start' : 'lineTo'}`);
3247
+ child.setAttribute('x', String(point.x));
3248
+ child.setAttribute('y', String(point.y));
3249
+ polygon.append(child);
3250
+ });
3251
+ wrap.append(polygon);
3252
+ return wrap;
3253
+ }
3254
+ const work_docx_index_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
3255
+ const work_docx_index_export_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
3256
+ class DocxIndexPatchCollector {
3257
+ patches = [];
3258
+ add(element) {
3259
+ const value = documentIndexValueFromElement(element);
3260
+ const patch = {
3261
+ marker: `__A3S_WORK_DOCUMENT_INDEX_${this.patches.length + 1}__`,
3262
+ options: value.options,
3263
+ entries: value.entries
3264
+ };
3265
+ this.patches.push(patch);
3266
+ return patch;
3267
+ }
3268
+ }
3269
+ function docxDocumentIndex(element, docx, collector) {
3270
+ const patch = collector.add(element);
3271
+ return [
3272
+ new docx.Paragraph({
3273
+ children: [
3274
+ new docx.TextRun(patch.marker)
3275
+ ]
3276
+ })
3277
+ ];
3278
+ }
3279
+ function docxIndexEntryRun(element, docx) {
3280
+ const value = documentIndexEntryFromElement(element);
3281
+ return value ? new docx.SimpleField(documentIndexEntryInstruction(value)) : null;
3282
+ }
3283
+ async function patchDocxIndexes(buffer, patches) {
3284
+ if (!patches.length) return buffer;
3285
+ const archive = await jszip.loadAsync(buffer);
3286
+ const entry = archive.file('word/document.xml');
3287
+ if (!entry) return buffer;
3288
+ const document = parseXml(await entry.async('text'), 'generated DOCX word/document.xml');
3289
+ const paragraphs = descendants(document, 'p');
3290
+ for (const patch of patches){
3291
+ const paragraph = paragraphs.find((candidate)=>descendants(candidate, 't').some((text)=>(text.textContent ?? '').includes(patch.marker)));
3292
+ if (!paragraph) throw new Error(`Generated DOCX did not emit the marker for document index ${patch.marker}.`);
3293
+ paragraph.replaceWith(createIndexContentControl(document, patch));
3294
+ }
3295
+ archive.file('word/document.xml', new XMLSerializer().serializeToString(document));
3296
+ return archive.generateAsync({
3297
+ type: 'arraybuffer'
3298
+ });
3299
+ }
3300
+ function createIndexContentControl(document, patch) {
3301
+ const control = work_docx_index_export_wordElement(document, 'sdt');
3302
+ const properties = work_docx_index_export_wordElement(document, 'sdtPr');
3303
+ const documentPart = work_docx_index_export_wordElement(document, 'docPartObj');
3304
+ const gallery = work_docx_index_export_wordElement(document, 'docPartGallery');
3305
+ work_docx_index_export_setWordAttribute(document, gallery, 'val', 'Indexes');
3306
+ documentPart.append(gallery, work_docx_index_export_wordElement(document, 'docPartUnique'));
3307
+ properties.append(documentPart);
3308
+ const content = work_docx_index_export_wordElement(document, 'sdtContent');
3309
+ const fieldStart = indexFieldStartParagraph(document, patch.options);
3310
+ content.append(fieldStart);
3311
+ const cached = cachedIndexParagraphs(document, patch);
3312
+ if (!cached.length) cached.push(emptyIndexParagraph(document));
3313
+ const last = cached.at(-1);
3314
+ last?.append(fieldCharacterRun(document, 'end'));
3315
+ content.append(...cached);
3316
+ control.append(properties, content);
3317
+ return control;
3318
+ }
3319
+ function indexFieldStartParagraph(document, options) {
3320
+ const paragraph = work_docx_index_export_wordElement(document, 'p');
3321
+ paragraph.append(fieldCharacterRun(document, 'begin'));
3322
+ const instructionRun = work_docx_index_export_wordElement(document, 'r');
3323
+ const instruction = work_docx_index_export_wordElement(document, 'instrText');
3324
+ instruction.setAttributeNS(work_docx_index_export_XML_NAMESPACE, 'xml:space', 'preserve');
3325
+ instruction.textContent = ` ${documentIndexInstruction(options)} `;
3326
+ instructionRun.append(instruction);
3327
+ paragraph.append(instructionRun, fieldCharacterRun(document, 'separate'));
3328
+ return paragraph;
3329
+ }
3330
+ function cachedIndexParagraphs(document, patch) {
3331
+ const paragraphs = [];
3332
+ let currentMain = '';
3333
+ for (const entry of patch.entries){
3334
+ if (entry.subEntry && entry.mainEntry !== currentMain) paragraphs.push(cachedIndexParagraph(document, patch.options, {
3335
+ style: 'Index1',
3336
+ term: entry.mainEntry,
3337
+ crossReference: '',
3338
+ pages: []
3339
+ }));
3340
+ paragraphs.push(cachedIndexParagraph(document, patch.options, {
3341
+ style: entry.subEntry ? 'Index2' : 'Index1',
3342
+ term: entry.subEntry || entry.mainEntry,
3343
+ crossReference: entry.crossReference,
3344
+ pages: entry.pages
3345
+ }));
3346
+ currentMain = entry.mainEntry;
3347
+ }
3348
+ return paragraphs;
3349
+ }
3350
+ function cachedIndexParagraph(document, options, source) {
3351
+ const paragraph = work_docx_index_export_wordElement(document, 'p');
3352
+ const properties = work_docx_index_export_wordElement(document, 'pPr');
3353
+ const style = work_docx_index_export_wordElement(document, 'pStyle');
3354
+ work_docx_index_export_setWordAttribute(document, style, 'val', source.style);
3355
+ properties.append(style);
3356
+ if (source.pages.length && options.rightAlignPageNumbers) {
3357
+ const tabs = work_docx_index_export_wordElement(document, 'tabs');
3358
+ const tab = work_docx_index_export_wordElement(document, 'tab');
3359
+ work_docx_index_export_setWordAttribute(document, tab, 'val', 'right');
3360
+ const leader = docxIndexLeader(options.leader);
3361
+ if (leader) work_docx_index_export_setWordAttribute(document, tab, 'leader', leader);
3362
+ work_docx_index_export_setWordAttribute(document, tab, 'pos', '9360');
3363
+ tabs.append(tab);
3364
+ properties.append(tabs);
3365
+ }
3366
+ paragraph.append(properties, textRun(document, source.term));
3367
+ if (source.crossReference) {
3368
+ paragraph.append(textRun(document, `, See ${source.crossReference}`));
3369
+ return paragraph;
3370
+ }
3371
+ if (!source.pages.length) return paragraph;
3372
+ paragraph.append(options.rightAlignPageNumbers ? tabRun(document) : textRun(document, ', '));
3373
+ for (const [index, page] of source.pages.entries()){
3374
+ if (index > 0) paragraph.append(textRun(document, ', '));
3375
+ paragraph.append(textRun(document, String(page.pageNumber), {
3376
+ bold: page.pageBold,
3377
+ italic: page.pageItalic
3378
+ }));
3379
+ }
3380
+ return paragraph;
3381
+ }
3382
+ function emptyIndexParagraph(document) {
3383
+ const paragraph = work_docx_index_export_wordElement(document, 'p');
3384
+ const properties = work_docx_index_export_wordElement(document, 'pPr');
3385
+ const style = work_docx_index_export_wordElement(document, 'pStyle');
3386
+ work_docx_index_export_setWordAttribute(document, style, 'val', 'Index1');
3387
+ properties.append(style);
3388
+ paragraph.append(properties);
3389
+ return paragraph;
3390
+ }
3391
+ function textRun(document, value, style = {}) {
3392
+ const run = work_docx_index_export_wordElement(document, 'r');
3393
+ if (style.bold || style.italic) {
3394
+ const properties = work_docx_index_export_wordElement(document, 'rPr');
3395
+ if (style.bold) properties.append(work_docx_index_export_wordElement(document, 'b'));
3396
+ if (style.italic) properties.append(work_docx_index_export_wordElement(document, 'i'));
3397
+ run.append(properties);
3398
+ }
3399
+ const text = work_docx_index_export_wordElement(document, 't');
3400
+ if (/^\s|\s$/.test(value)) text.setAttributeNS(work_docx_index_export_XML_NAMESPACE, 'xml:space', 'preserve');
3401
+ text.textContent = value;
3402
+ run.append(text);
3403
+ return run;
3444
3404
  }
3445
- function imageLayerPatch(properties, patches) {
3446
- for (const property of Array.from(properties.attributes))for (const [marker, patch] of patches)if (property.value.includes(marker)) return patch;
3447
- return null;
3405
+ function tabRun(document) {
3406
+ const run = work_docx_index_export_wordElement(document, 'r');
3407
+ run.append(work_docx_index_export_wordElement(document, 'tab'));
3408
+ return run;
3448
3409
  }
3449
- function removeLayerMarker(properties, marker) {
3450
- for (const property of Array.from(properties.attributes))if (property.value.includes(marker)) property.value = property.value.replace(marker, '');
3410
+ function fieldCharacterRun(document, type) {
3411
+ const run = work_docx_index_export_wordElement(document, 'r');
3412
+ const character = work_docx_index_export_wordElement(document, 'fldChar');
3413
+ work_docx_index_export_setWordAttribute(document, character, 'fldCharType', type);
3414
+ run.append(character);
3415
+ return run;
3451
3416
  }
3452
- function closestAnchor(element) {
3453
- let current = element;
3454
- while(current){
3455
- if ('anchor' === current.localName) return current;
3456
- current = current.parentElement;
3457
- }
3417
+ function docxIndexLeader(value) {
3418
+ if ('dash' === value) return 'hyphen';
3419
+ if ('underline' === value) return 'underscore';
3420
+ if ('dot' === value) return 'dot';
3458
3421
  return null;
3459
3422
  }
3460
- function applyImageLayer(anchor, source) {
3461
- const layer = normalizeDocumentImageLayer(source);
3462
- anchor.setAttribute('relativeHeight', String(layer.relativeHeight));
3463
- anchor.setAttribute('behindDoc', layer.behindDocument ? '1' : '0');
3464
- anchor.setAttribute('allowOverlap', layer.allowOverlap ? '1' : '0');
3465
- anchor.setAttribute('layoutInCell', layer.layoutInCell ? '1' : '0');
3466
- anchor.setAttribute('locked', layer.lockAnchor ? '1' : '0');
3423
+ function work_docx_index_export_wordElement(document, localName) {
3424
+ return document.createElementNS(work_docx_index_export_WORD_NAMESPACE, wordQualifiedName(document.documentElement, localName));
3467
3425
  }
3468
- const WORDPROCESSING_DRAWING_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing';
3469
- class DocxImageWrapPatchCollector {
3426
+ function work_docx_index_export_setWordAttribute(document, element, localName, value) {
3427
+ for (const candidate of Array.from(element.attributes))if (candidate.localName === localName && (candidate.namespaceURI === work_docx_index_export_WORD_NAMESPACE || !candidate.namespaceURI)) element.removeAttributeNode(candidate);
3428
+ element.setAttributeNS(work_docx_index_export_WORD_NAMESPACE, wordQualifiedName(document.documentElement, localName), value);
3429
+ }
3430
+ function wordQualifiedName(root, localName) {
3431
+ return `${xmlNamespacePrefix(root, work_docx_index_export_WORD_NAMESPACE) ?? 'w'}:${localName}`;
3432
+ }
3433
+ const LEGACY_TEXT_EFFECTS_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
3434
+ class DocxLegacyTextEffectsPatchCollector {
3470
3435
  patches = [];
3471
3436
  nextMarker = 1;
3472
- marker(options, contour) {
3473
- if (!isContourImageLayout(options.layout)) return '';
3474
- const marker = `__A3S_IMAGE_WRAP_${this.nextMarker}__`;
3475
- this.nextMarker += 1;
3476
- this.patches.push({
3437
+ occupied;
3438
+ patchesByMarker = new Map();
3439
+ markersByEffectsAndStyle = new Map();
3440
+ constructor(source){
3441
+ this.occupied = source;
3442
+ }
3443
+ marker(value, inheritedStyle) {
3444
+ const effects = normalizeDocumentLegacyTextEffects(value);
3445
+ if (!effects || !Object.keys(effects).length) throw new Error('Document contains invalid legacy text effects.');
3446
+ const style = this.originalStyle(inheritedStyle);
3447
+ const key = JSON.stringify([
3448
+ DOCUMENT_LEGACY_TEXT_EFFECT_NAMES.map((name)=>effects[name] ?? null),
3449
+ style ?? null
3450
+ ]);
3451
+ const existing = this.markersByEffectsAndStyle.get(key);
3452
+ if (existing) return existing;
3453
+ let marker = '';
3454
+ do {
3455
+ marker = `A3SOfficeLegacyTextEffects${String(this.nextMarker).padStart(8, '0')}`;
3456
+ this.nextMarker += 1;
3457
+ }while (this.occupied.includes(marker) || this.patches.some((patch)=>patch.marker === marker))
3458
+ const patch = {
3477
3459
  marker,
3478
- layout: options.layout,
3479
- options,
3480
- contour: contour ?? defaultDocumentImageWrapContour()
3481
- });
3460
+ effects,
3461
+ ...style ? {
3462
+ style
3463
+ } : {}
3464
+ };
3465
+ this.patches.push(patch);
3466
+ this.patchesByMarker.set(marker, patch);
3467
+ this.markersByEffectsAndStyle.set(key, marker);
3482
3468
  return marker;
3483
3469
  }
3470
+ lookup(marker) {
3471
+ return marker ? this.patchesByMarker.get(marker) : void 0;
3472
+ }
3473
+ originalStyle(style) {
3474
+ let current = style;
3475
+ const seen = new Set();
3476
+ while(current && !seen.has(current)){
3477
+ seen.add(current);
3478
+ const patch = this.patchesByMarker.get(current);
3479
+ if (!patch) break;
3480
+ current = patch.style;
3481
+ }
3482
+ return current;
3483
+ }
3484
3484
  }
3485
- async function patchDocxImageWraps(buffer, patches) {
3485
+ async function patchDocxLegacyTextEffects(buffer, patches) {
3486
3486
  if (!patches.length) return buffer;
3487
+ const byMarker = new Map();
3488
+ for (const patch of patches){
3489
+ const effects = normalizeDocumentLegacyTextEffects(patch.effects);
3490
+ if (!patch.marker || !effects || !Object.keys(effects).length || byMarker.has(patch.marker)) throw new Error('Document contains invalid legacy text-effect patches.');
3491
+ byMarker.set(patch.marker, {
3492
+ ...patch,
3493
+ effects
3494
+ });
3495
+ }
3487
3496
  const archive = await jszip.loadAsync(buffer);
3488
- const byMarker = new Map(patches.map((patch)=>[
3489
- patch.marker,
3490
- patch
3491
- ]));
3492
- const entries = Object.values(archive.files).filter((entry)=>!entry.dir && /^word\/(?:document|header\d+|footer\d+|footnotes|endnotes|comments)\.xml$/.test(entry.name));
3493
- for (const entry of entries){
3494
- const document = parseXml(await entry.async('text'), entry.name);
3497
+ const applied = new Set();
3498
+ for (const entry of Object.values(archive.files)){
3499
+ if (entry.dir || !LEGACY_TEXT_EFFECTS_PART_PATTERN.test(entry.name)) continue;
3500
+ const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
3495
3501
  let changed = false;
3496
- for (const properties of descendants(document, 'docPr')){
3497
- const patch = imageWrapPatch(properties, byMarker);
3498
- if (!patch) continue;
3499
- removeWrapMarker(properties, patch.marker);
3500
- changed = true;
3501
- const anchor = work_docx_image_wrap_closestAnchor(properties);
3502
- const source = anchor ? wrappingElement(anchor) : null;
3503
- if (anchor && source) source.parentNode?.replaceChild(createWrappingElement(anchor, patch), source);
3502
+ for (const properties of descendants(document, 'rPr')){
3503
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(properties.namespaceURI ?? '')) continue;
3504
+ const markerStyles = directChildren(properties, 'rStyle').filter((style)=>{
3505
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(style.namespaceURI ?? '')) return false;
3506
+ const marker = work_docx_legacy_text_effects_export_wordValue(style);
3507
+ return null !== marker && byMarker.has(marker);
3508
+ });
3509
+ if (!markerStyles.length) continue;
3510
+ if (1 !== markerStyles.length) throw new Error('Generated DOCX legacy text-effect marker is duplicated in one run.');
3511
+ const marker = work_docx_legacy_text_effects_export_wordValue(markerStyles[0]);
3512
+ const patch = marker ? byMarker.get(marker) : void 0;
3513
+ if (marker && patch) {
3514
+ if (DOCUMENT_LEGACY_TEXT_EFFECT_NAMES.some((name)=>directChildren(properties, name).length > 0)) throw new Error('Generated DOCX legacy text-effect marker unexpectedly has a native value.');
3515
+ if (patch.style) work_docx_legacy_text_effects_export_setWordValue(markerStyles[0], patch.style);
3516
+ else markerStyles[0].remove();
3517
+ for (const name of DOCUMENT_LEGACY_TEXT_EFFECT_NAMES){
3518
+ const value = patch.effects[name];
3519
+ if (void 0 !== value) insertLegacyTextEffectProperty(document, properties, name, value);
3520
+ }
3521
+ applied.add(marker);
3522
+ changed = true;
3523
+ }
3504
3524
  }
3505
- if (changed) archive.file(entry.name, new XMLSerializer().serializeToString(document));
3525
+ if (changed) archive.file(entry.name, serializeUtf8Xml(document));
3506
3526
  }
3527
+ const missing = patches.filter((patch)=>!applied.has(patch.marker));
3528
+ if (missing.length) throw new Error(`DOCX legacy text-effect markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
3507
3529
  return archive.generateAsync({
3508
3530
  type: 'arraybuffer'
3509
3531
  });
3510
3532
  }
3511
- function imageWrapPatch(properties, patches) {
3512
- for (const attribute of Array.from(properties.attributes))for (const [marker, patch] of patches)if (attribute.value.includes(marker)) return patch;
3513
- return null;
3514
- }
3515
- function removeWrapMarker(properties, marker) {
3516
- for (const attribute of Array.from(properties.attributes))if (attribute.value.includes(marker)) attribute.value = attribute.value.replace(marker, '');
3517
- }
3518
- function work_docx_image_wrap_closestAnchor(element) {
3519
- let current = element;
3520
- while(current){
3521
- if ('anchor' === current.localName) return current;
3522
- current = current.parentElement;
3523
- }
3524
- return null;
3533
+ function insertLegacyTextEffectProperty(document, properties, name, value) {
3534
+ const namespace = properties.namespaceURI ?? '';
3535
+ const prefix = properties.prefix || 'w';
3536
+ const effect = document.createElementNS(namespace, `${prefix}:${name}`);
3537
+ if (!value) effect.setAttributeNS(namespace, `${prefix}:val`, '0');
3538
+ const rank = DOCX_RUN_PROPERTY_RANK.get(name) ?? 0;
3539
+ const next = directChildren(properties).find((child)=>{
3540
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(child.namespaceURI ?? '')) return true;
3541
+ return (DOCX_RUN_PROPERTY_RANK.get(child.localName) ?? 1 / 0) > rank;
3542
+ });
3543
+ properties.insertBefore(effect, next ?? null);
3525
3544
  }
3526
- function wrappingElement(anchor) {
3527
- return directChildren(anchor).find((element)=>[
3528
- 'wrapNone',
3529
- 'wrapSquare',
3530
- 'wrapTight',
3531
- 'wrapThrough',
3532
- 'wrapTopAndBottom'
3533
- ].includes(element.localName)) ?? null;
3545
+ function work_docx_legacy_text_effects_export_wordValue(element) {
3546
+ const values = Array.from(element.attributes).filter((attribute)=>'val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? ''));
3547
+ return 1 === values.length ? values[0].value : null;
3534
3548
  }
3535
- function createWrappingElement(anchor, patch) {
3536
- const document = anchor.ownerDocument;
3537
- const namespace = anchor.namespaceURI ?? WORDPROCESSING_DRAWING_NAMESPACE;
3538
- const prefix = anchor.prefix ? `${anchor.prefix}:` : '';
3539
- const wrap = document.createElementNS(namespace, `${prefix}${'tight' === patch.layout ? 'wrapTight' : 'wrapThrough'}`);
3540
- wrap.setAttribute('wrapText', patch.options.wrapSide);
3541
- const distance = String(Math.round(36000 * patch.options.wrapDistance));
3542
- wrap.setAttribute('distL', distance);
3543
- wrap.setAttribute('distR', distance);
3544
- const polygon = document.createElementNS(namespace, `${prefix}wrapPolygon`);
3545
- polygon.setAttribute('edited', patch.contour.edited ? '1' : '0');
3546
- patch.contour.points.forEach((point, index)=>{
3547
- const child = document.createElementNS(namespace, `${prefix}${0 === index ? 'start' : 'lineTo'}`);
3548
- child.setAttribute('x', String(point.x));
3549
- child.setAttribute('y', String(point.y));
3550
- polygon.append(child);
3551
- });
3552
- wrap.append(polygon);
3553
- return wrap;
3549
+ function work_docx_legacy_text_effects_export_setWordValue(element, value) {
3550
+ const namespace = element.namespaceURI ?? '';
3551
+ const prefix = element.prefix || 'w';
3552
+ for (const attribute of Array.from(element.attributes))if ('val' === xmlAttributeLocalName(attribute) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, attribute) ?? '')) element.removeAttributeNode(attribute);
3553
+ element.setAttributeNS(namespace, `${prefix}:val`, value);
3554
3554
  }
3555
3555
  function documentParagraphShadingDocxOptions(element, themePatches) {
3556
3556
  const shading = parseDocumentParagraphShadingElement(element);
@@ -3580,7 +3580,7 @@ async function listToDocxParagraphs(list, docx, context, inlineRuns, depth = 0)
3580
3580
  const ordered = 'ol' === list.tagName.toLowerCase();
3581
3581
  const numbering = ordered ? registerOrderedListNumbering(list, level, docx, context) : registerBulletListNumbering(list, level, docx, context);
3582
3582
  const items = Array.from(list.children).filter((child)=>child instanceof HTMLElement && 'li' === child.tagName.toLowerCase());
3583
- for (const item of items){
3583
+ for (const [itemIndex, item] of items.entries()){
3584
3584
  const directBlocks = Array.from(item.children).filter((child)=>child instanceof HTMLElement && 'ol' !== child.tagName.toLowerCase() && 'ul' !== child.tagName.toLowerCase());
3585
3585
  const contentBlocks = directBlocks.length ? directBlocks : [
3586
3586
  directListItemContentRoot(item)
@@ -3589,8 +3589,13 @@ async function listToDocxParagraphs(list, docx, context, inlineRuns, depth = 0)
3589
3589
  const tag = block.tagName.toLowerCase();
3590
3590
  const heading = 'h1' === tag ? docx.HeadingLevel.HEADING_1 : 'h2' === tag ? docx.HeadingLevel.HEADING_2 : 'h3' === tag ? docx.HeadingLevel.HEADING_3 : void 0;
3591
3591
  const runs = await inlineRuns(block);
3592
+ const numberingChangeMarker = ordered && 0 === blockIndex ? context.numberingChangeMarker?.(list, itemIndex) : null;
3593
+ const paragraphChildren = numberingChangeMarker ? [
3594
+ new docx.TextRun(numberingChangeMarker),
3595
+ ...runs
3596
+ ] : runs;
3592
3597
  paragraphs.push(new docx.Paragraph({
3593
- children: runs.length ? runs : [
3598
+ children: paragraphChildren.length ? paragraphChildren : [
3594
3599
  new docx.TextRun('')
3595
3600
  ],
3596
3601
  heading,
@@ -4023,13 +4028,105 @@ async function patchDocxNumberingRestartRules(buffer, rules) {
4023
4028
  restart.setAttributeNS(work_docx_numbering_WORD_NAMESPACE, work_docx_numbering_wordQualifiedName(document.documentElement, 'val'), String(restartAfterLevel));
4024
4029
  }
4025
4030
  }
4026
- archive.file('word/numbering.xml', new XMLSerializer().serializeToString(document));
4031
+ archive.file('word/numbering.xml', new XMLSerializer().serializeToString(document));
4032
+ return archive.generateAsync({
4033
+ type: 'arraybuffer'
4034
+ });
4035
+ }
4036
+ function work_docx_numbering_wordQualifiedName(root, localName) {
4037
+ return `${xmlNamespacePrefix(root, work_docx_numbering_WORD_NAMESPACE) ?? root.prefix ?? 'w'}:${localName}`;
4038
+ }
4039
+ const work_docx_numbering_change_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
4040
+ const MAX_NUMBERING_CHANGE_PATCHES = 65536;
4041
+ const NUMBERING_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes)\.xml$/i;
4042
+ class DocxNumberingChangePatchCollector {
4043
+ patches = [];
4044
+ register(list, itemIndex, id) {
4045
+ if ('ol' !== list.tagName.toLowerCase() || 'numbering' !== list.dataset.changeKind || !list.hasAttribute('data-document-change')) return null;
4046
+ const key = list.dataset.changeId?.trim() ?? '';
4047
+ const author = list.dataset.changeAuthor?.trim() ?? '';
4048
+ const date = work_docx_numbering_change_export_normalizedRevisionDate(list.dataset.changeDate);
4049
+ const snapshot = parseDocumentNumberingChange(list.dataset.changeBefore ?? '');
4050
+ const value = snapshot ? snapshot.start + itemIndex : 0;
4051
+ if (!key || !author || author.length > 255 || /[\u0000-\u001f\u007f]/.test(author) || !snapshot || !Number.isSafeInteger(itemIndex) || itemIndex < 0 || value < 1 || value > MAX_DOCUMENT_NUMBERING_START) throw new Error('Document contains an invalid numbering revision.');
4052
+ if (this.patches.length >= MAX_NUMBERING_CHANGE_PATCHES) throw new Error('Document exceeds the numbering revision limit.');
4053
+ const marker = `__A3S_WORK_NUMBERING_CHANGE_EXPORT_${this.patches.length + 1}__`;
4054
+ this.patches.push({
4055
+ marker,
4056
+ id,
4057
+ author,
4058
+ date,
4059
+ original: `%${snapshot.level + 1}:${value}:${snapshot.originalFormat}:${snapshot.originalSuffix}`
4060
+ });
4061
+ return marker;
4062
+ }
4063
+ }
4064
+ async function patchDocxNumberingChanges(buffer, patches) {
4065
+ if (!patches.length) return buffer;
4066
+ if (patches.length > MAX_NUMBERING_CHANGE_PATCHES) throw new Error('Document exceeds the numbering revision limit.');
4067
+ const archive = await jszip.loadAsync(buffer);
4068
+ const byMarker = new Map(patches.map((patch)=>[
4069
+ patch.marker,
4070
+ patch
4071
+ ]));
4072
+ const applied = new Set();
4073
+ for (const entry of Object.values(archive.files)){
4074
+ if (entry.dir || !NUMBERING_PART_PATTERN.test(entry.name)) continue;
4075
+ const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
4076
+ let changed = false;
4077
+ for (const paragraph of descendants(document, 'p').filter((element)=>element.namespaceURI === work_docx_numbering_change_export_WORD_NAMESPACE))for (const marker of numberingMarkers(paragraph, byMarker)){
4078
+ if (applied.has(marker.patch.marker)) throw new Error(`Generated DOCX contains a duplicate numbering revision marker: ${marker.patch.marker}.`);
4079
+ setNumberingChange(document, paragraph, marker.patch);
4080
+ marker.run.remove();
4081
+ applied.add(marker.patch.marker);
4082
+ changed = true;
4083
+ }
4084
+ if (changed) archive.file(entry.name, serializeUtf8Xml(document));
4085
+ }
4086
+ const missing = patches.filter((patch)=>!applied.has(patch.marker));
4087
+ if (missing.length) throw new Error(`DOCX numbering revision markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
4027
4088
  return archive.generateAsync({
4028
4089
  type: 'arraybuffer'
4029
4090
  });
4030
4091
  }
4031
- function work_docx_numbering_wordQualifiedName(root, localName) {
4032
- return `${xmlNamespacePrefix(root, work_docx_numbering_WORD_NAMESPACE) ?? root.prefix ?? 'w'}:${localName}`;
4092
+ function numberingMarkers(paragraph, patches) {
4093
+ return directChildren(paragraph, 'r').flatMap((run)=>{
4094
+ if (run.namespaceURI !== work_docx_numbering_change_export_WORD_NAMESPACE) return [];
4095
+ const texts = directChildren(run, 't').filter((text)=>text.namespaceURI === work_docx_numbering_change_export_WORD_NAMESPACE);
4096
+ if (1 !== texts.length || !work_docx_numbering_change_export_runHasOnlyMarkerText(run, texts[0])) return [];
4097
+ const patch = patches.get(texts[0]?.textContent ?? '');
4098
+ return patch ? [
4099
+ {
4100
+ patch,
4101
+ run
4102
+ }
4103
+ ] : [];
4104
+ });
4105
+ }
4106
+ function work_docx_numbering_change_export_runHasOnlyMarkerText(run, markerText) {
4107
+ return Boolean(markerText && directChildren(run).every((child)=>child === markerText || 'rPr' === child.localName && child.namespaceURI === work_docx_numbering_change_export_WORD_NAMESPACE));
4108
+ }
4109
+ function setNumberingChange(document, paragraph, patch) {
4110
+ const propertyNodes = directChildren(paragraph, 'pPr').filter((element)=>element.namespaceURI === work_docx_numbering_change_export_WORD_NAMESPACE);
4111
+ if (1 !== propertyNodes.length) throw new Error('Generated DOCX numbered paragraph must contain one property node.');
4112
+ const numberingNodes = directChildren(propertyNodes[0], 'numPr').filter((element)=>element.namespaceURI === work_docx_numbering_change_export_WORD_NAMESPACE);
4113
+ if (1 !== numberingNodes.length) throw new Error('Generated DOCX numbered paragraph must contain one numbering property node.');
4114
+ const numbering = numberingNodes[0];
4115
+ if (directChildren(numbering, 'numberingChange').some((element)=>element.namespaceURI === work_docx_numbering_change_export_WORD_NAMESPACE)) throw new Error('Generated DOCX paragraph already contains a numbering revision.');
4116
+ const prefix = xmlNamespacePrefix(document.documentElement, work_docx_numbering_change_export_WORD_NAMESPACE) ?? 'w';
4117
+ const change = document.createElementNS(work_docx_numbering_change_export_WORD_NAMESPACE, `${prefix}:numberingChange`);
4118
+ work_docx_numbering_change_export_setWordAttribute(change, prefix, 'id', String(patch.id));
4119
+ work_docx_numbering_change_export_setWordAttribute(change, prefix, 'author', patch.author);
4120
+ work_docx_numbering_change_export_setWordAttribute(change, prefix, 'date', patch.date);
4121
+ work_docx_numbering_change_export_setWordAttribute(change, prefix, 'original', patch.original);
4122
+ numbering.append(change);
4123
+ }
4124
+ function work_docx_numbering_change_export_setWordAttribute(element, prefix, name, value) {
4125
+ element.setAttributeNS(work_docx_numbering_change_export_WORD_NAMESPACE, `${prefix}:${name}`, value);
4126
+ }
4127
+ function work_docx_numbering_change_export_normalizedRevisionDate(value) {
4128
+ const time = Date.parse(value?.trim() ?? '');
4129
+ return Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString();
4033
4130
  }
4034
4131
  const work_docx_paragraph_format_change_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
4035
4132
  const work_docx_paragraph_format_change_export_WORD_2012_NAMESPACE = 'http://schemas.microsoft.com/office/word/2012/wordml';
@@ -4370,25 +4467,254 @@ function work_docx_paragraph_identity_ensureIgnorableNamespace(root, prefix, nam
4370
4467
  attribute?.ownerElement?.removeAttributeNode(attribute);
4371
4468
  root.setAttributeNS(work_docx_paragraph_identity_MARKUP_COMPATIBILITY_NAMESPACE, `${compatibilityPrefix}:Ignorable`, tokens.join(' '));
4372
4469
  }
4373
- function work_docx_paragraph_identity_ensureNamespacePrefix(root, preferred, namespace) {
4374
- const existing = xmlDeclaredPrefix(root, namespace);
4375
- if (existing) return existing;
4376
- if (!xmlNamespaceUri(root, preferred)) {
4377
- root.setAttributeNS(work_docx_settings_xml_XMLNS_NAMESPACE, `xmlns:${preferred}`, namespace);
4378
- return preferred;
4379
- }
4380
- let suffix = 1;
4381
- let prefix = '';
4382
- do {
4383
- prefix = `a3s${suffix}`;
4384
- suffix += 1;
4385
- }while (xmlNamespaceUri(root, prefix))
4386
- root.setAttributeNS(work_docx_settings_xml_XMLNS_NAMESPACE, `xmlns:${prefix}`, namespace);
4387
- return prefix;
4470
+ function work_docx_paragraph_identity_ensureNamespacePrefix(root, preferred, namespace) {
4471
+ const existing = xmlDeclaredPrefix(root, namespace);
4472
+ if (existing) return existing;
4473
+ if (!xmlNamespaceUri(root, preferred)) {
4474
+ root.setAttributeNS(work_docx_settings_xml_XMLNS_NAMESPACE, `xmlns:${preferred}`, namespace);
4475
+ return preferred;
4476
+ }
4477
+ let suffix = 1;
4478
+ let prefix = '';
4479
+ do {
4480
+ prefix = `a3s${suffix}`;
4481
+ suffix += 1;
4482
+ }while (xmlNamespaceUri(root, prefix))
4483
+ root.setAttributeNS(work_docx_settings_xml_XMLNS_NAMESPACE, `xmlns:${prefix}`, namespace);
4484
+ return prefix;
4485
+ }
4486
+ function isParagraphPartRoot(root, path) {
4487
+ const expected = /^word\/header\d*\.xml$/i.test(path) ? 'hdr' : /^word\/footer\d*\.xml$/i.test(path) ? 'ftr' : /^word\/footnotes\.xml$/i.test(path) ? 'footnotes' : /^word\/endnotes\.xml$/i.test(path) ? 'endnotes' : 'document';
4488
+ return root.localName === expected && DOCX_WORDPROCESSING_NAMESPACES.has(root.namespaceURI ?? '');
4489
+ }
4490
+ const work_docx_run_border_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
4491
+ class DocxRunBorderPatchCollector {
4492
+ patches = [];
4493
+ nextMarker = 1;
4494
+ usedColors;
4495
+ constructor(sourceHtml){
4496
+ this.usedColors = work_docx_run_border_export_sourceColors(sourceHtml);
4497
+ }
4498
+ marker(source) {
4499
+ const border = normalizeDocumentRunBorder(source);
4500
+ if (!border) return null;
4501
+ let marker = '';
4502
+ do {
4503
+ const value = 0xc00000 + this.nextMarker;
4504
+ if (value > 0xcfffff) throw new Error('DOCX character border marker space is exhausted.');
4505
+ marker = value.toString(16).padStart(6, '0').toUpperCase();
4506
+ this.nextMarker += 1;
4507
+ }while (this.usedColors.has(marker))
4508
+ this.usedColors.add(marker);
4509
+ this.patches.push({
4510
+ marker,
4511
+ border
4512
+ });
4513
+ return marker;
4514
+ }
4515
+ hasMarker(value) {
4516
+ const marker = value?.toUpperCase();
4517
+ return Boolean(marker && this.patches.some((patch)=>patch.marker === marker));
4518
+ }
4519
+ }
4520
+ function documentRunBorderDocxOptions(source, collector) {
4521
+ const marker = collector.marker(source);
4522
+ return marker ? {
4523
+ border: {
4524
+ style: 'single',
4525
+ color: marker,
4526
+ size: 2,
4527
+ space: 0
4528
+ }
4529
+ } : {};
4530
+ }
4531
+ async function patchDocxRunBorders(buffer, patches) {
4532
+ if (!patches.length) return buffer;
4533
+ const archive = await jszip.loadAsync(buffer);
4534
+ const byMarker = new Map(patches.map((patch)=>[
4535
+ patch.marker,
4536
+ patch
4537
+ ]));
4538
+ const applied = new Set();
4539
+ for (const entry of Object.values(archive.files)){
4540
+ if (entry.dir || !/^word\/(?:document|header\d+|footer\d+|footnotes|endnotes|comments)\.xml$/i.test(entry.name)) continue;
4541
+ const document = parseXml(await entry.async('text'), entry.name);
4542
+ let changed = false;
4543
+ for (const element of Array.from(document.getElementsByTagName('*'))){
4544
+ if ('bdr' !== element.localName || element.namespaceURI !== work_docx_run_border_export_WORD_NAMESPACE || element.parentElement?.localName !== 'rPr') continue;
4545
+ const marker = work_docx_run_border_export_wordAttribute(element, 'color')?.toUpperCase();
4546
+ const patch = marker ? byMarker.get(marker) : void 0;
4547
+ if (marker && patch) {
4548
+ replaceRunBorderAttributes(document, element, patch.border);
4549
+ applied.add(marker);
4550
+ changed = true;
4551
+ }
4552
+ }
4553
+ if (changed) archive.file(entry.name, serializeUtf8Xml(document));
4554
+ }
4555
+ const missing = patches.filter((patch)=>!applied.has(patch.marker));
4556
+ if (missing.length) throw new Error(`Generated DOCX is missing ${missing.length} character border marker(s).`);
4557
+ return archive.generateAsync({
4558
+ type: 'arraybuffer'
4559
+ });
4560
+ }
4561
+ function setDocxRunBorderAttributes(document, element, border) {
4562
+ work_docx_run_border_export_setWordAttribute(document, element, 'val', border.style);
4563
+ const color = border.color;
4564
+ if (color) {
4565
+ work_docx_run_border_export_setWordAttribute(document, element, 'color', 'auto' === color.value ? 'auto' : color.value.slice(1).toUpperCase());
4566
+ if (color.theme) {
4567
+ work_docx_run_border_export_setWordAttribute(document, element, 'themeColor', color.theme.theme);
4568
+ if (color.theme.tint) work_docx_run_border_export_setWordAttribute(document, element, 'themeTint', color.theme.tint);
4569
+ if (color.theme.shade) work_docx_run_border_export_setWordAttribute(document, element, 'themeShade', color.theme.shade);
4570
+ }
4571
+ }
4572
+ if (void 0 !== border.size) work_docx_run_border_export_setWordAttribute(document, element, 'sz', String(border.size));
4573
+ if (void 0 !== border.space) work_docx_run_border_export_setWordAttribute(document, element, 'space', String(border.space));
4574
+ if (void 0 !== border.shadow) work_docx_run_border_export_setWordAttribute(document, element, 'shadow', border.shadow ? '1' : '0');
4575
+ if (void 0 !== border.frame) work_docx_run_border_export_setWordAttribute(document, element, 'frame', border.frame ? '1' : '0');
4576
+ }
4577
+ function replaceRunBorderAttributes(document, element, border) {
4578
+ for (const attribute of Array.from(element.attributes))if ('xmlns' !== attribute.name && !attribute.name.startsWith('xmlns:')) element.removeAttributeNode(attribute);
4579
+ element.replaceChildren();
4580
+ setDocxRunBorderAttributes(document, element, border);
4581
+ }
4582
+ function work_docx_run_border_export_sourceColors(source) {
4583
+ const colors = new Set();
4584
+ for (const match of source.matchAll(/#([0-9a-f]{6})\b/gi))if (match[1]) colors.add(match[1].toUpperCase());
4585
+ return colors;
4586
+ }
4587
+ function work_docx_run_border_export_wordAttribute(element, name) {
4588
+ return element.getAttributeNS(work_docx_run_border_export_WORD_NAMESPACE, name) ?? element.getAttribute(`w:${name}`);
4589
+ }
4590
+ function work_docx_run_border_export_setWordAttribute(document, element, name, value) {
4591
+ const prefix = xmlNamespacePrefix(document.documentElement, work_docx_run_border_export_WORD_NAMESPACE) ?? 'w';
4592
+ element.setAttributeNS(work_docx_run_border_export_WORD_NAMESPACE, `${prefix}:${name}`, value);
4593
+ }
4594
+ const RUN_FONT_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
4595
+ const WORD_FONT_ATTRIBUTE_NAMES = new Set([
4596
+ 'ascii',
4597
+ 'hAnsi',
4598
+ 'eastAsia',
4599
+ 'cs',
4600
+ 'asciiTheme',
4601
+ 'hAnsiTheme',
4602
+ 'eastAsiaTheme',
4603
+ 'cstheme',
4604
+ 'hint'
4605
+ ]);
4606
+ class DocxRunFontsPatchCollector {
4607
+ patches = [];
4608
+ nextMarker = 1;
4609
+ occupied;
4610
+ markersByFonts = new Map();
4611
+ constructor(source){
4612
+ this.occupied = source;
4613
+ }
4614
+ marker(source, slot, currentFontFamily) {
4615
+ const fonts = normalizeDocumentScriptFonts(source);
4616
+ const normalizedSlot = normalizeDocumentScriptFontSlot(slot) ?? documentScriptFontSlotFromHint(fonts?.hint);
4617
+ const expected = fonts ? documentScriptFontFamily(fonts, normalizedSlot) : void 0;
4618
+ const current = documentFontNameFromCssFamily(currentFontFamily);
4619
+ if (!fonts || (expected ? documentFontNameFromCssFamily(expected)?.toLocaleLowerCase() !== current?.toLocaleLowerCase() : Boolean(currentFontFamily.trim()))) return null;
4620
+ const key = serializeDocumentScriptFonts(fonts);
4621
+ if (!key) return null;
4622
+ const existing = this.markersByFonts.get(key);
4623
+ if (existing) return existing;
4624
+ let marker = '';
4625
+ do {
4626
+ marker = `A3SOfficeRunFonts${String(this.nextMarker).padStart(8, '0')}`;
4627
+ this.nextMarker += 1;
4628
+ }while (this.occupied.includes(marker) || this.patches.some((patch)=>patch.marker === marker))
4629
+ this.patches.push({
4630
+ marker,
4631
+ fonts
4632
+ });
4633
+ this.markersByFonts.set(key, marker);
4634
+ return marker;
4635
+ }
4636
+ }
4637
+ async function patchDocxRunFonts(buffer, patches) {
4638
+ if (!patches.length) return buffer;
4639
+ const archive = await jszip.loadAsync(buffer);
4640
+ const byMarker = new Map(patches.map((patch)=>[
4641
+ patch.marker,
4642
+ patch.fonts
4643
+ ]));
4644
+ const applied = new Set();
4645
+ for (const entry of Object.values(archive.files)){
4646
+ if (entry.dir || !RUN_FONT_PART_PATTERN.test(entry.name)) continue;
4647
+ const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
4648
+ let changedPart = false;
4649
+ for (const element of descendants(document, 'rFonts')){
4650
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(element.namespaceURI ?? '')) continue;
4651
+ const marker = wordFontAttribute(element, 'ascii');
4652
+ if (!marker) continue;
4653
+ const fonts = byMarker.get(marker);
4654
+ if (fonts) {
4655
+ replaceWordFontAttributes(element, fonts);
4656
+ applied.add(marker);
4657
+ changedPart = true;
4658
+ }
4659
+ }
4660
+ if (changedPart) archive.file(entry.name, serializeUtf8Xml(document));
4661
+ }
4662
+ const missing = patches.filter((patch)=>!applied.has(patch.marker));
4663
+ if (missing.length) throw new Error(`DOCX run-font markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
4664
+ return archive.generateAsync({
4665
+ type: 'arraybuffer'
4666
+ });
4667
+ }
4668
+ function replaceWordFontAttributes(element, fonts) {
4669
+ for (const item of Array.from(element.attributes))if (item.namespaceURI !== work_docx_settings_xml_XMLNS_NAMESPACE && WORD_FONT_ATTRIBUTE_NAMES.has(xmlAttributeLocalName(item)) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, item) ?? '')) element.removeAttributeNode(item);
4670
+ const values = [
4671
+ [
4672
+ 'ascii',
4673
+ fonts.ascii?.name
4674
+ ],
4675
+ [
4676
+ 'hAnsi',
4677
+ fonts.highAnsi?.name
4678
+ ],
4679
+ [
4680
+ 'eastAsia',
4681
+ fonts.eastAsia?.name
4682
+ ],
4683
+ [
4684
+ 'cs',
4685
+ fonts.complexScript?.name
4686
+ ],
4687
+ [
4688
+ 'asciiTheme',
4689
+ fonts.ascii?.theme
4690
+ ],
4691
+ [
4692
+ 'hAnsiTheme',
4693
+ fonts.highAnsi?.theme
4694
+ ],
4695
+ [
4696
+ 'eastAsiaTheme',
4697
+ fonts.eastAsia?.theme
4698
+ ],
4699
+ [
4700
+ 'cstheme',
4701
+ fonts.complexScript?.theme
4702
+ ],
4703
+ [
4704
+ 'hint',
4705
+ fonts.hint
4706
+ ]
4707
+ ];
4708
+ for (const [name, value] of values)if (value) setWordFontAttribute(element, name, value);
4709
+ }
4710
+ function wordFontAttribute(element, name) {
4711
+ const values = Array.from(element.attributes).filter((item)=>xmlAttributeLocalName(item) === name && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, item) ?? ''));
4712
+ return 1 === values.length ? values[0]?.value ?? null : null;
4388
4713
  }
4389
- function isParagraphPartRoot(root, path) {
4390
- const expected = /^word\/header\d*\.xml$/i.test(path) ? 'hdr' : /^word\/footer\d*\.xml$/i.test(path) ? 'ftr' : /^word\/footnotes\.xml$/i.test(path) ? 'footnotes' : /^word\/endnotes\.xml$/i.test(path) ? 'endnotes' : 'document';
4391
- return root.localName === expected && DOCX_WORDPROCESSING_NAMESPACES.has(root.namespaceURI ?? '');
4714
+ function setWordFontAttribute(element, name, value) {
4715
+ const namespace = element.namespaceURI ?? '';
4716
+ const prefix = element.prefix || 'w';
4717
+ element.setAttributeNS(namespace, `${prefix}:${name}`, value);
4392
4718
  }
4393
4719
  const TWIPS_PER_PIXEL = 15;
4394
4720
  function documentTableCellDocxOptions(cell, docx, themePatches) {
@@ -8331,235 +8657,6 @@ function docxParagraphPartPaths(pathsByLower, contentTypes) {
8331
8657
  function isObfuscatedFontContentType(type) {
8332
8658
  return type?.trim().toLowerCase() === 'application/vnd.openxmlformats-officedocument.obfuscatedfont';
8333
8659
  }
8334
- const RUN_FONT_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
8335
- const WORD_FONT_ATTRIBUTE_NAMES = new Set([
8336
- 'ascii',
8337
- 'hAnsi',
8338
- 'eastAsia',
8339
- 'cs',
8340
- 'asciiTheme',
8341
- 'hAnsiTheme',
8342
- 'eastAsiaTheme',
8343
- 'cstheme',
8344
- 'hint'
8345
- ]);
8346
- class DocxRunFontsPatchCollector {
8347
- patches = [];
8348
- nextMarker = 1;
8349
- occupied;
8350
- markersByFonts = new Map();
8351
- constructor(source){
8352
- this.occupied = source;
8353
- }
8354
- marker(source, slot, currentFontFamily) {
8355
- const fonts = normalizeDocumentScriptFonts(source);
8356
- const normalizedSlot = normalizeDocumentScriptFontSlot(slot) ?? documentScriptFontSlotFromHint(fonts?.hint);
8357
- const expected = fonts ? documentScriptFontFamily(fonts, normalizedSlot) : void 0;
8358
- const current = documentFontNameFromCssFamily(currentFontFamily);
8359
- if (!fonts || (expected ? documentFontNameFromCssFamily(expected)?.toLocaleLowerCase() !== current?.toLocaleLowerCase() : Boolean(currentFontFamily.trim()))) return null;
8360
- const key = serializeDocumentScriptFonts(fonts);
8361
- if (!key) return null;
8362
- const existing = this.markersByFonts.get(key);
8363
- if (existing) return existing;
8364
- let marker = '';
8365
- do {
8366
- marker = `A3SOfficeRunFonts${String(this.nextMarker).padStart(8, '0')}`;
8367
- this.nextMarker += 1;
8368
- }while (this.occupied.includes(marker) || this.patches.some((patch)=>patch.marker === marker))
8369
- this.patches.push({
8370
- marker,
8371
- fonts
8372
- });
8373
- this.markersByFonts.set(key, marker);
8374
- return marker;
8375
- }
8376
- }
8377
- async function patchDocxRunFonts(buffer, patches) {
8378
- if (!patches.length) return buffer;
8379
- const archive = await jszip.loadAsync(buffer);
8380
- const byMarker = new Map(patches.map((patch)=>[
8381
- patch.marker,
8382
- patch.fonts
8383
- ]));
8384
- const applied = new Set();
8385
- for (const entry of Object.values(archive.files)){
8386
- if (entry.dir || !RUN_FONT_PART_PATTERN.test(entry.name)) continue;
8387
- const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
8388
- let changedPart = false;
8389
- for (const element of descendants(document, 'rFonts')){
8390
- if (!DOCX_WORDPROCESSING_NAMESPACES.has(element.namespaceURI ?? '')) continue;
8391
- const marker = wordFontAttribute(element, 'ascii');
8392
- if (!marker) continue;
8393
- const fonts = byMarker.get(marker);
8394
- if (fonts) {
8395
- replaceWordFontAttributes(element, fonts);
8396
- applied.add(marker);
8397
- changedPart = true;
8398
- }
8399
- }
8400
- if (changedPart) archive.file(entry.name, serializeUtf8Xml(document));
8401
- }
8402
- const missing = patches.filter((patch)=>!applied.has(patch.marker));
8403
- if (missing.length) throw new Error(`DOCX run-font markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
8404
- return archive.generateAsync({
8405
- type: 'arraybuffer'
8406
- });
8407
- }
8408
- function replaceWordFontAttributes(element, fonts) {
8409
- for (const item of Array.from(element.attributes))if (item.namespaceURI !== work_docx_settings_xml_XMLNS_NAMESPACE && WORD_FONT_ATTRIBUTE_NAMES.has(xmlAttributeLocalName(item)) && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, item) ?? '')) element.removeAttributeNode(item);
8410
- const values = [
8411
- [
8412
- 'ascii',
8413
- fonts.ascii?.name
8414
- ],
8415
- [
8416
- 'hAnsi',
8417
- fonts.highAnsi?.name
8418
- ],
8419
- [
8420
- 'eastAsia',
8421
- fonts.eastAsia?.name
8422
- ],
8423
- [
8424
- 'cs',
8425
- fonts.complexScript?.name
8426
- ],
8427
- [
8428
- 'asciiTheme',
8429
- fonts.ascii?.theme
8430
- ],
8431
- [
8432
- 'hAnsiTheme',
8433
- fonts.highAnsi?.theme
8434
- ],
8435
- [
8436
- 'eastAsiaTheme',
8437
- fonts.eastAsia?.theme
8438
- ],
8439
- [
8440
- 'cstheme',
8441
- fonts.complexScript?.theme
8442
- ],
8443
- [
8444
- 'hint',
8445
- fonts.hint
8446
- ]
8447
- ];
8448
- for (const [name, value] of values)if (value) setWordFontAttribute(element, name, value);
8449
- }
8450
- function wordFontAttribute(element, name) {
8451
- const values = Array.from(element.attributes).filter((item)=>xmlAttributeLocalName(item) === name && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, item) ?? ''));
8452
- return 1 === values.length ? values[0]?.value ?? null : null;
8453
- }
8454
- function setWordFontAttribute(element, name, value) {
8455
- const namespace = element.namespaceURI ?? '';
8456
- const prefix = element.prefix || 'w';
8457
- element.setAttributeNS(namespace, `${prefix}:${name}`, value);
8458
- }
8459
- const work_docx_run_border_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
8460
- class DocxRunBorderPatchCollector {
8461
- patches = [];
8462
- nextMarker = 1;
8463
- usedColors;
8464
- constructor(sourceHtml){
8465
- this.usedColors = work_docx_run_border_export_sourceColors(sourceHtml);
8466
- }
8467
- marker(source) {
8468
- const border = normalizeDocumentRunBorder(source);
8469
- if (!border) return null;
8470
- let marker = '';
8471
- do {
8472
- const value = 0xc00000 + this.nextMarker;
8473
- if (value > 0xcfffff) throw new Error('DOCX character border marker space is exhausted.');
8474
- marker = value.toString(16).padStart(6, '0').toUpperCase();
8475
- this.nextMarker += 1;
8476
- }while (this.usedColors.has(marker))
8477
- this.usedColors.add(marker);
8478
- this.patches.push({
8479
- marker,
8480
- border
8481
- });
8482
- return marker;
8483
- }
8484
- hasMarker(value) {
8485
- const marker = value?.toUpperCase();
8486
- return Boolean(marker && this.patches.some((patch)=>patch.marker === marker));
8487
- }
8488
- }
8489
- function documentRunBorderDocxOptions(source, collector) {
8490
- const marker = collector.marker(source);
8491
- return marker ? {
8492
- border: {
8493
- style: 'single',
8494
- color: marker,
8495
- size: 2,
8496
- space: 0
8497
- }
8498
- } : {};
8499
- }
8500
- async function patchDocxRunBorders(buffer, patches) {
8501
- if (!patches.length) return buffer;
8502
- const archive = await jszip.loadAsync(buffer);
8503
- const byMarker = new Map(patches.map((patch)=>[
8504
- patch.marker,
8505
- patch
8506
- ]));
8507
- const applied = new Set();
8508
- for (const entry of Object.values(archive.files)){
8509
- if (entry.dir || !/^word\/(?:document|header\d+|footer\d+|footnotes|endnotes|comments)\.xml$/i.test(entry.name)) continue;
8510
- const document = parseXml(await entry.async('text'), entry.name);
8511
- let changed = false;
8512
- for (const element of Array.from(document.getElementsByTagName('*'))){
8513
- if ('bdr' !== element.localName || element.namespaceURI !== work_docx_run_border_export_WORD_NAMESPACE || element.parentElement?.localName !== 'rPr') continue;
8514
- const marker = work_docx_run_border_export_wordAttribute(element, 'color')?.toUpperCase();
8515
- const patch = marker ? byMarker.get(marker) : void 0;
8516
- if (marker && patch) {
8517
- replaceRunBorderAttributes(document, element, patch.border);
8518
- applied.add(marker);
8519
- changed = true;
8520
- }
8521
- }
8522
- if (changed) archive.file(entry.name, serializeUtf8Xml(document));
8523
- }
8524
- const missing = patches.filter((patch)=>!applied.has(patch.marker));
8525
- if (missing.length) throw new Error(`Generated DOCX is missing ${missing.length} character border marker(s).`);
8526
- return archive.generateAsync({
8527
- type: 'arraybuffer'
8528
- });
8529
- }
8530
- function setDocxRunBorderAttributes(document, element, border) {
8531
- work_docx_run_border_export_setWordAttribute(document, element, 'val', border.style);
8532
- const color = border.color;
8533
- if (color) {
8534
- work_docx_run_border_export_setWordAttribute(document, element, 'color', 'auto' === color.value ? 'auto' : color.value.slice(1).toUpperCase());
8535
- if (color.theme) {
8536
- work_docx_run_border_export_setWordAttribute(document, element, 'themeColor', color.theme.theme);
8537
- if (color.theme.tint) work_docx_run_border_export_setWordAttribute(document, element, 'themeTint', color.theme.tint);
8538
- if (color.theme.shade) work_docx_run_border_export_setWordAttribute(document, element, 'themeShade', color.theme.shade);
8539
- }
8540
- }
8541
- if (void 0 !== border.size) work_docx_run_border_export_setWordAttribute(document, element, 'sz', String(border.size));
8542
- if (void 0 !== border.space) work_docx_run_border_export_setWordAttribute(document, element, 'space', String(border.space));
8543
- if (void 0 !== border.shadow) work_docx_run_border_export_setWordAttribute(document, element, 'shadow', border.shadow ? '1' : '0');
8544
- if (void 0 !== border.frame) work_docx_run_border_export_setWordAttribute(document, element, 'frame', border.frame ? '1' : '0');
8545
- }
8546
- function replaceRunBorderAttributes(document, element, border) {
8547
- for (const attribute of Array.from(element.attributes))if ('xmlns' !== attribute.name && !attribute.name.startsWith('xmlns:')) element.removeAttributeNode(attribute);
8548
- element.replaceChildren();
8549
- setDocxRunBorderAttributes(document, element, border);
8550
- }
8551
- function work_docx_run_border_export_sourceColors(source) {
8552
- const colors = new Set();
8553
- for (const match of source.matchAll(/#([0-9a-f]{6})\b/gi))if (match[1]) colors.add(match[1].toUpperCase());
8554
- return colors;
8555
- }
8556
- function work_docx_run_border_export_wordAttribute(element, name) {
8557
- return element.getAttributeNS(work_docx_run_border_export_WORD_NAMESPACE, name) ?? element.getAttribute(`w:${name}`);
8558
- }
8559
- function work_docx_run_border_export_setWordAttribute(document, element, name, value) {
8560
- const prefix = xmlNamespacePrefix(document.documentElement, work_docx_run_border_export_WORD_NAMESPACE) ?? 'w';
8561
- element.setAttributeNS(work_docx_run_border_export_WORD_NAMESPACE, `${prefix}:${name}`, value);
8562
- }
8563
8660
  async function createDocxBlob(content, sourcePackage) {
8564
8661
  const docx = await import("docx");
8565
8662
  const normalizedContent = {
@@ -8605,11 +8702,13 @@ async function createDocxBlob(content, sourcePackage) {
8605
8702
  usedNativeTextEffectMarkers: new Set(),
8606
8703
  usedOpenTypeMarkers: new Set(),
8607
8704
  paragraphFormattingChangePatches: new DocxParagraphFormattingChangePatchCollector(),
8705
+ numberingChangePatches: new DocxNumberingChangePatchCollector(),
8608
8706
  tableOfContentsPatches: new DocxTableOfContentsPatchCollector(),
8609
8707
  indexPatches: new DocxIndexPatchCollector(),
8610
8708
  hasExplicitZeroCharacterSpacing: false,
8611
8709
  hasExplicitZeroKerningThreshold: false
8612
8710
  };
8711
+ noteContext.numberingChangeMarker = (list, itemIndex)=>noteContext.numberingChangePatches.register(list, itemIndex, docxRevisionId(list, noteContext, `numbering:${noteContext.numberingChangePatches.patches.length + 1}`));
8613
8712
  const commentRecords = createDocxCommentRecords(commentThreads, docx, noteContext.commentIds);
8614
8713
  const sections = [];
8615
8714
  let usesOddEvenPageChrome = false;
@@ -8681,7 +8780,8 @@ async function createDocxBlob(content, sourcePackage) {
8681
8780
  const tableOfContentsPatched = await patchDocxTableOfContents(paragraphIdentityPatched, noteContext.tableOfContentsPatches.patches);
8682
8781
  const indexPatched = await patchDocxIndexes(tableOfContentsPatched, noteContext.indexPatches.patches);
8683
8782
  const paragraphFormattingChangesPatched = await patchDocxParagraphFormattingChanges(indexPatched, noteContext.paragraphFormattingChangePatches.patches);
8684
- const equationPatched = await patchDocxEquations(paragraphFormattingChangesPatched, noteContext.equationPatches.patches);
8783
+ const numberingChangesPatched = await patchDocxNumberingChanges(paragraphFormattingChangesPatched, noteContext.numberingChangePatches.patches);
8784
+ const equationPatched = await patchDocxEquations(numberingChangesPatched, noteContext.equationPatches.patches);
8685
8785
  const patched = await patchDocxPageColor(equationPatched, normalizedContent.pageColor);
8686
8786
  const preserved = sourcePackage ? await preserveDocxSourcePackage(patched, sourcePackage, {
8687
8787
  numberingIdentities: noteContext.numberingSourceIdentities
@@ -9234,8 +9334,9 @@ function docxTextRevision(element, kind, context) {
9234
9334
  date: Number.isFinite(time) ? new Date(time).toISOString() : new Date().toISOString()
9235
9335
  };
9236
9336
  }
9237
- function docxRevisionId(element, context) {
9238
- const key = element.dataset.changeId?.trim() || `change-${context.nextChangeId}`;
9337
+ function docxRevisionId(element, context, suffix) {
9338
+ const baseKey = element.dataset.changeId?.trim() || `change-${context.nextChangeId}`;
9339
+ const key = suffix ? `${baseKey}:${suffix}` : baseKey;
9239
9340
  let id = context.changeIds.get(key);
9240
9341
  if (!id) {
9241
9342
  id = context.nextChangeId;
@@ -9246,7 +9347,7 @@ function docxRevisionId(element, context) {
9246
9347
  }
9247
9348
  function documentHasTrackedChanges(html) {
9248
9349
  const document = new DOMParser().parseFromString(html, 'text/html');
9249
- return Boolean(document.body.querySelector('ins[data-document-change], del[data-document-change], span[data-document-change][data-change-kind="formatting"], [data-document-change][data-change-kind="paragraph-formatting"]'));
9350
+ return Boolean(document.body.querySelector('ins[data-document-change], del[data-document-change], span[data-document-change][data-change-kind="formatting"], [data-document-change][data-change-kind="paragraph-formatting"], [data-document-change][data-change-kind="numbering"]'));
9250
9351
  }
9251
9352
  function anchoredDocumentComments(content) {
9252
9353
  const document = new DOMParser().parseFromString(content.html, 'text/html');