@a3s-lab/office 0.26.0 → 0.28.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.
Files changed (87) hide show
  1. package/README.md +89 -2
  2. package/dist/0~4595.js +16 -2
  3. package/dist/0~5093.js +563 -201
  4. package/dist/0~6090.js +77 -77
  5. package/dist/{0~2805.js → 0~7043.js} +305 -2
  6. package/dist/0~document-editor.js +2633 -976
  7. package/dist/0~spreadsheet-editor.js +1324 -378
  8. package/dist/0~work-docx-export.js +737 -17
  9. package/dist/0~work-docx-import.js +173 -98
  10. package/dist/0~work-office-diagnostics.js +250 -5
  11. package/dist/{164.js → 1544.js} +1083 -785
  12. package/dist/4104.js +145 -29
  13. package/dist/5184.js +13 -0
  14. package/dist/6282.js +1898 -345
  15. package/dist/core.js +1 -1
  16. package/dist/index.js +1 -1
  17. package/dist/internal/features/work/editors/document-command-catalog.d.ts +102 -0
  18. package/dist/internal/features/work/editors/document-font-dialog-model.d.ts +111 -0
  19. package/dist/internal/features/work/editors/document-font-dialog-script-font-model.d.ts +18 -0
  20. package/dist/internal/features/work/editors/document-font-dialog.d.ts +9 -0
  21. package/dist/internal/features/work/editors/document-home-ribbon.d.ts +2 -1
  22. package/dist/internal/features/work/editors/document-page-chrome-editor.d.ts +6 -0
  23. package/dist/internal/features/work/editors/document-page-chrome-ribbon.d.ts +2 -1
  24. package/dist/internal/features/work/editors/document-strike-ribbon.d.ts +7 -0
  25. package/dist/internal/features/work/editors/document-text-case-ribbon.d.ts +4 -0
  26. package/dist/internal/features/work/editors/document-toolbar.d.ts +3 -1
  27. package/dist/internal/features/work/editors/document-underline-ribbon.d.ts +9 -0
  28. package/dist/internal/features/work/editors/document-wps-shortcuts.d.ts +1 -0
  29. package/dist/internal/features/work/editors/spreadsheet-cell-fill-format.d.ts +11 -0
  30. package/dist/internal/features/work/editors/spreadsheet-cell-format.d.ts +2 -1
  31. package/dist/internal/features/work/editors/spreadsheet-format-cells-dialog-model.d.ts +13 -2
  32. package/dist/internal/features/work/editors/spreadsheet-format-cells-fill-panel.d.ts +13 -0
  33. package/dist/internal/features/work/editors/spreadsheet-format-cells-fill-preview.d.ts +6 -0
  34. package/dist/internal/features/work/editors/spreadsheet-format-painter.d.ts +1 -1
  35. package/dist/internal/features/work/editors/work-office-chrome.d.ts +9 -1
  36. package/dist/internal/features/work/work-document-character-formatting.d.ts +16 -1
  37. package/dist/internal/features/work/work-document-character-position.d.ts +9 -0
  38. package/dist/internal/features/work/work-document-character-scale.d.ts +8 -0
  39. package/dist/internal/features/work/work-document-character-spacing.d.ts +8 -0
  40. package/dist/internal/features/work/work-document-emphasis.d.ts +7 -0
  41. package/dist/internal/features/work/work-document-equations.d.ts +2 -1
  42. package/dist/internal/features/work/work-document-format-changes.d.ts +20 -2
  43. package/dist/internal/features/work/work-document-hidden-text.d.ts +5 -0
  44. package/dist/internal/features/work/work-document-kerning.d.ts +10 -0
  45. package/dist/internal/features/work/work-document-legacy-text-effects.d.ts +27 -0
  46. package/dist/internal/features/work/work-document-script-font-extension.d.ts +2 -0
  47. package/dist/internal/features/work/work-document-script-fonts.d.ts +51 -0
  48. package/dist/internal/features/work/work-document-strike.d.ts +21 -0
  49. package/dist/internal/features/work/work-document-text-case.d.ts +10 -0
  50. package/dist/internal/features/work/work-document-underline.d.ts +37 -0
  51. package/dist/internal/features/work/work-document-word-line-metrics.d.ts +16 -0
  52. package/dist/internal/features/work/work-docx-character-position.d.ts +2 -0
  53. package/dist/internal/features/work/work-docx-character-scale.d.ts +2 -0
  54. package/dist/internal/features/work/work-docx-character-spacing.d.ts +4 -0
  55. package/dist/internal/features/work/work-docx-emphasis-diagnostics.d.ts +3 -0
  56. package/dist/internal/features/work/work-docx-emphasis.d.ts +14 -0
  57. package/dist/internal/features/work/work-docx-hidden-text-diagnostics.d.ts +3 -0
  58. package/dist/internal/features/work/work-docx-hidden-text-export.d.ts +17 -0
  59. package/dist/internal/features/work/work-docx-hidden-text.d.ts +11 -0
  60. package/dist/internal/features/work/work-docx-kerning-diagnostics.d.ts +3 -0
  61. package/dist/internal/features/work/work-docx-kerning.d.ts +14 -0
  62. package/dist/internal/features/work/work-docx-legacy-text-effects-diagnostics.d.ts +3 -0
  63. package/dist/internal/features/work/work-docx-legacy-text-effects-export.d.ts +18 -0
  64. package/dist/internal/features/work/work-docx-legacy-text-effects.d.ts +12 -0
  65. package/dist/internal/features/work/work-docx-run-fonts-diagnostics.d.ts +3 -0
  66. package/dist/internal/features/work/work-docx-run-fonts-export.d.ts +14 -0
  67. package/dist/internal/features/work/work-docx-run-fonts.d.ts +13 -0
  68. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +27 -2
  69. package/dist/internal/features/work/work-docx-run-property-order.d.ts +2 -0
  70. package/dist/internal/features/work/work-docx-run-script-font-import.d.ts +9 -0
  71. package/dist/internal/features/work/work-docx-strike.d.ts +8 -0
  72. package/dist/internal/features/work/work-docx-theme-reference.d.ts +1 -1
  73. package/dist/internal/features/work/work-docx-twips.d.ts +6 -0
  74. package/dist/internal/features/work/work-docx-underline.d.ts +3 -0
  75. package/dist/internal/features/work/work-spreadsheet-gradient-fill-canvas.d.ts +10 -0
  76. package/dist/internal/features/work/work-spreadsheet-native-fill-canvas.d.ts +8 -0
  77. package/dist/internal/features/work/work-spreadsheet-pattern-fill-canvas.d.ts +8 -0
  78. package/dist/internal/features/work/work-xlsx-cell-style-origin.d.ts +2 -1
  79. package/dist/internal/features/work/work-xlsx-cell-style-writer.d.ts +3 -0
  80. package/dist/internal/features/work/work-xlsx-cell-styles.d.ts +4 -0
  81. package/dist/internal/features/work/work-xlsx-gradient-fill.d.ts +31 -0
  82. package/dist/internal/features/work/work-xlsx-native-fill.d.ts +17 -0
  83. package/dist/internal/features/work/work-xlsx-pattern-fill.d.ts +21 -0
  84. package/dist/office-kernel.wasm +0 -0
  85. package/dist/styles.css +809 -78
  86. package/docs/latest/en/browser-editor-architecture.md +24 -0
  87. package/package.json +35 -1
@@ -1,8 +1,8 @@
1
1
  import jszip from "jszip";
2
- import { normalizeDocumentBookmarkName, applyDocumentImageIdentityToElement, DOCUMENT_PAGE_BORDER_EDGES, createDocumentImageIdentityRegistry, normalizeDocumentColumns, normalizeDocumentTableVerticalAlign, uniqueDocumentParagraphIdentity, documentTableColumnPercentagesFromElement, documentTableCellMarginOverridesFromElement, applyDocumentTableRowIdentityToElement, normalizeDocumentBookmarksHtml, documentImageIdentityFromElement, documentTableGeometryFromElement, normalizeDocumentTableRowHeightRule, documentNoteKey, normalizeDocumentParagraphBorders, normalizeDocumentTableBorderWidth, normalizeDocumentParagraphId, normalizeDocumentTableBorderStyle, normalizeDocumentBookmarkReferencesHtml, documentCitationInstruction, parseDocumentParagraphBordersElement, documentImageWrapContourFromElement, documentImagePositionFromElement, normalizeDocumentPageChrome, documentPageMarginsForLayout, normalizeDocumentImageLayer, documentBookmarkReferenceInstruction, documentImageCropFromElement, normalizeDocumentPageBorders, documentImageLayerFromElement, parseDocxThemeReference, documentCaptionLabel, documentCaptionKind, applyDocumentParagraphIdentityToElement, isContourImageLayout, normalizeDocumentImageIdentity, normalizeTableColor, documentTableBordersFromElement, documentSections, createDocumentParagraphIdentityRegistry, documentCitationTags, docxDocumentFieldKind, wrapsBesideImage, DocxThemePatchCollector, defaultDocumentImageWrapContour, normalizeDocumentParagraphIdentity, normalizeDocumentPaperSource, documentEquationFromElement, DOCUMENT_PARAGRAPH_BORDER_EDGES, parseDocumentCharacterFormatting, patchDocxThemeReferences, parseDocumentParagraphShadingElement, documentParagraphIdentityFromElement, normalizeDocumentEquation, parseDocumentParagraphFormatting, documentTableRowIdentityFromElement, normalizeDocumentBookmarkNativeId, collectDocumentNotes, DOCUMENT_PAGE_MARGIN_KEYS, uniqueDocumentImageIdentity, documentImageLayoutFromElement, documentPageHorizontalMarginTwips, documentPageGeometryForLayout, normalizeDocumentPageMargins, documentNoteKind } from "./6282.js";
2
+ import { normalizeDocumentBookmarkName, normalizeDocumentEmphasisMark, DOCUMENT_STRIKE_STYLE_ATTRIBUTE, applyDocumentImageIdentityToElement, documentEmphasisMarkFromElement, DOCUMENT_PAGE_BORDER_EDGES, DOCUMENT_UNDERLINE_STYLE_ATTRIBUTE, documentScriptFontSlotFromElement, normalizeDocumentColumns, createDocumentImageIdentityRegistry, serializeDocumentScriptFonts, documentCharacterSpacingTwipsFromElement, uniqueDocumentParagraphIdentity, documentCharacterPositionHalfPointsFromElement, normalizeDocumentTableVerticalAlign, documentTableColumnPercentagesFromElement, normalizeDocumentTextCase, documentTableCellMarginOverridesFromElement, applyDocumentTableRowIdentityToElement, normalizeDocumentBookmarksHtml, documentImageIdentityFromElement, documentTableGeometryFromElement, normalizeDocumentTableRowHeightRule, normalizeDocumentScriptFonts, documentNoteKey, DOCUMENT_LEGACY_TEXT_EMBOSS_ATTRIBUTE, normalizeDocumentHiddenText, normalizeDocumentParagraphBorders, documentLegacyTextEffectsConflict, normalizeDocumentTableBorderWidth, normalizeDocumentCharacterSpacingTwips, normalizeDocumentParagraphId, documentStrikeFormattingFromElement, normalizeDocumentTableBorderStyle, normalizeDocumentBookmarkReferencesHtml, documentCitationInstruction, parseDocumentParagraphBordersElement, documentImageWrapContourFromElement, documentImagePositionFromElement, normalizeDocumentCharacterScalePercent, documentScriptFontsFromElement, normalizeDocumentUnderlineStyle, documentPageMarginsForLayout, normalizeDocumentPageChrome, normalizeDocumentImageLayer, documentFontNameFromCssFamily, documentBookmarkReferenceInstruction, documentImageCropFromElement, documentKerningThresholdHalfPointsFromElement, normalizeDocumentPageBorders, documentImageLayerFromElement, normalizeDocumentLegacyTextEffects, documentCaptionLabel, documentCaptionKind, applyDocumentParagraphIdentityToElement, DOCUMENT_LEGACY_TEXT_IMPRINT_ATTRIBUTE, isContourImageLayout, parseDocumentScriptFonts, normalizeDocumentStrikeStyle, parseDocxThemeReference, createDocumentParagraphIdentityRegistry, documentCitationTags, docxDocumentFieldKind, normalizeDocumentImageIdentity, normalizeTableColor, documentTableBordersFromElement, wrapsBesideImage, normalizeDocumentCharacterPositionHalfPoints, normalizeDocumentKerningThresholdHalfPoints, DocxThemePatchCollector, defaultDocumentImageWrapContour, documentSections, normalizeDocumentParagraphIdentity, normalizeDocumentPaperSource, DOCUMENT_LEGACY_TEXT_OUTLINE_ATTRIBUTE, documentEquationFromElement, DOCUMENT_PARAGRAPH_BORDER_EDGES, documentLegacyTextEffectsFromElement, parseDocumentCharacterFormatting, documentScriptFontFamily, documentParagraphIdentityFromElement, DOCUMENT_LEGACY_TEXT_SHADOW_ATTRIBUTE, normalizeDocumentEquation, documentUnderlineFormattingFromElement, normalizeDocumentBookmarkNativeId, patchDocxThemeReferences, DOCUMENT_PAGE_MARGIN_KEYS, uniqueDocumentImageIdentity, documentHiddenTextFromElement, parseDocumentParagraphShadingElement, documentImageLayoutFromElement, documentScriptFontSlotFromHint, parseDocumentParagraphFormatting, documentPageHorizontalMarginTwips, documentTableRowIdentityFromElement, collectDocumentNotes, documentPageGeometryForLayout, normalizeDocumentScriptFontSlot, DOCUMENT_LEGACY_TEXT_EFFECT_NAMES, documentCharacterScalePercentFromElement, normalizeDocumentPageMargins, normalizeDocumentLegacyTextEffect, documentNoteKind } from "./6282.js";
3
3
  import { serializeUtf8Xml, directChildren, xmlNamespacePrefix, parseXml, decodeXmlBytes, descendants, resolvePartTarget, directChild, attribute as work_ooxml_package_attribute } from "./5184.js";
4
4
  import { assignDocxNoteIds, patchDocxPageColor, normalizeDocxCommentId, assignDocxCommentThreads } from "./0~4980.js";
5
- import { paragraphTabStops, mergeDocxIgnorableExtensionsAtPairs, patchDocxBibliography, XMLNS_NAMESPACE as work_docx_settings_xml_XMLNS_NAMESPACE, paragraphAlignment, paragraphSpacingOptions, xmlNamespaceUri, hasNonWhitespaceXmlText, cloneXmlElement, cssColorToHex, DocxParagraphDefaultCollapsedPatchCollector, assertXmlRoot, xmlDeclaredPrefix, paragraphIndent, domDirection, DOCX_WORDPROCESSING_NAMESPACES, paragraphBidirectional, XML_NAMESPACE as work_docx_settings_xml_XML_NAMESPACE, patchDocxParagraphDefaultCollapsed, xmlAttributeNamespace, dataBoolean, mergeDocxIgnorableExtensions, parseDocxColorSchemeMappingElement, paragraphPaginationOptions, DOCX_COLOR_SCHEME_MAPPING_ATTRIBUTES, xmlAttributeLocalName, cssFontSize, paragraphDirectionOptions, cssFontFamily } from "./0~2805.js";
5
+ import { paragraphTabStops, mergeDocxIgnorableExtensionsAtPairs, patchDocxBibliography, docxKerningThresholdValue, docxCharacterPositionValue, docxCharacterScaleValue, docxEmphasisMarkRunOptions, XMLNS_NAMESPACE as work_docx_settings_xml_XMLNS_NAMESPACE, paragraphAlignment, paragraphSpacingOptions, xmlNamespaceUri, hasNonWhitespaceXmlText, patchDocxExplicitZeroCharacterSpacing, cloneXmlElement, cssColorToHex, DocxParagraphDefaultCollapsedPatchCollector, assertXmlRoot, xmlDeclaredPrefix, paragraphIndent, domDirection, DOCX_WORDPROCESSING_NAMESPACES, paragraphBidirectional, XML_NAMESPACE as work_docx_settings_xml_XML_NAMESPACE, patchDocxParagraphDefaultCollapsed, xmlAttributeNamespace, dataBoolean, mergeDocxIgnorableExtensions, patchDocxExplicitZeroKerningThresholds, parseDocxColorSchemeMappingElement, paragraphPaginationOptions, DOCX_COLOR_SCHEME_MAPPING_ATTRIBUTES, xmlAttributeLocalName, cssFontSize, paragraphDirectionOptions, cssFontFamily, docxCharacterSpacingValue } from "./0~7043.js";
6
6
  import { normalizeDocumentHref } from "./0~work-document-links.js";
7
7
  const WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
8
8
  class DocxBookmarkPatchCollector {
@@ -757,6 +757,285 @@ 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
+ }
760
1039
  const MATH_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/math';
761
1040
  const work_docx_equation_export_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
762
1041
  const WORD_DATE_UTC_NAMESPACE = 'http://schemas.microsoft.com/office/word/2023/wordml/word16du';
@@ -2221,15 +2500,129 @@ function appendFormattingProperties(document, properties, namespace, prefix, ser
2221
2500
  appendBooleanProperty(document, properties, namespace, prefix, byType, 'bold', 'bCs');
2222
2501
  appendBooleanProperty(document, properties, namespace, prefix, byType, 'italic', 'i');
2223
2502
  appendBooleanProperty(document, properties, namespace, prefix, byType, 'italic', 'iCs');
2224
- if (byType.has('underline')) appendValuedProperty(document, properties, namespace, prefix, 'u', 'single');
2225
- appendBooleanProperty(document, properties, namespace, prefix, byType, 'strike', 'strike');
2503
+ appendTextCaseProperties(document, properties, namespace, prefix, byType.get('textStyle'));
2504
+ appendStrikeProperties(document, properties, namespace, prefix, byType.get('strike'));
2505
+ appendLegacyTextEffectsProperties(document, properties, namespace, prefix, byType.get('textStyle'));
2506
+ appendSnapToGridProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2507
+ appendHiddenTextProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2226
2508
  appendColorProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2509
+ appendCharacterSpacingProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2510
+ appendCharacterScaleProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2511
+ appendKerningThresholdProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2512
+ appendCharacterPositionProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2227
2513
  appendFontSizeProperties(document, properties, namespace, prefix, byType.get('textStyle'));
2228
2514
  appendHighlightProperty(document, properties, namespace, prefix, byType.get('highlight'));
2229
- appendSnapToGridProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2515
+ appendUnderlineProperty(document, properties, namespace, prefix, byType.get('underline'));
2230
2516
  appendVerticalAlignProperty(document, properties, namespace, prefix, byType);
2517
+ appendEmphasisMarkProperty(document, properties, namespace, prefix, byType.get('textStyle'));
2518
+ }
2519
+ function appendLegacyTextEffectsProperties(document, properties, namespace, prefix, mark) {
2520
+ const effects = {};
2521
+ const attributes = [
2522
+ [
2523
+ 'outline',
2524
+ 'legacyTextOutline'
2525
+ ],
2526
+ [
2527
+ 'shadow',
2528
+ 'legacyTextShadow'
2529
+ ],
2530
+ [
2531
+ 'emboss',
2532
+ 'legacyTextEmboss'
2533
+ ],
2534
+ [
2535
+ 'imprint',
2536
+ 'legacyTextImprint'
2537
+ ]
2538
+ ];
2539
+ for (const [effect, attribute] of attributes){
2540
+ const value = normalizeDocumentLegacyTextEffect(mark?.attrs?.[attribute]);
2541
+ if (null !== value) effects[effect] = value;
2542
+ }
2543
+ if (documentLegacyTextEffectsConflict(effects)) throw new Error('Document contains conflicting legacy text effects.');
2544
+ for (const name of DOCUMENT_LEGACY_TEXT_EFFECT_NAMES){
2545
+ const value = effects[name];
2546
+ if (void 0 !== value) if (value) properties.append(wordElement(document, namespace, prefix, name));
2547
+ else appendValuedProperty(document, properties, namespace, prefix, name, '0');
2548
+ }
2549
+ }
2550
+ function appendHiddenTextProperty(document, properties, namespace, prefix, mark) {
2551
+ const hiddenText = normalizeDocumentHiddenText(mark?.attrs?.hiddenText);
2552
+ if (null === hiddenText) return;
2553
+ if (hiddenText) properties.append(wordElement(document, namespace, prefix, 'vanish'));
2554
+ else appendValuedProperty(document, properties, namespace, prefix, 'vanish', '0');
2555
+ }
2556
+ function appendEmphasisMarkProperty(document, properties, namespace, prefix, mark) {
2557
+ const emphasisMark = normalizeDocumentEmphasisMark(mark?.attrs?.emphasisMark);
2558
+ if (null === emphasisMark) return;
2559
+ appendValuedProperty(document, properties, namespace, prefix, 'em', emphasisMark);
2560
+ }
2561
+ function appendCharacterScaleProperty(document, properties, namespace, prefix, mark) {
2562
+ const scale = normalizeDocumentCharacterScalePercent(mark?.attrs?.characterScalePercent);
2563
+ if (null === scale) return;
2564
+ appendValuedProperty(document, properties, namespace, prefix, 'w', String(scale));
2565
+ }
2566
+ function appendKerningThresholdProperty(document, properties, namespace, prefix, mark) {
2567
+ const threshold = normalizeDocumentKerningThresholdHalfPoints(mark?.attrs?.kerningThresholdHalfPoints);
2568
+ if (null === threshold) return;
2569
+ appendValuedProperty(document, properties, namespace, prefix, 'kern', String(threshold));
2570
+ }
2571
+ function appendCharacterPositionProperty(document, properties, namespace, prefix, mark) {
2572
+ const position = normalizeDocumentCharacterPositionHalfPoints(mark?.attrs?.characterPositionHalfPoints);
2573
+ if (null === position) return;
2574
+ appendValuedProperty(document, properties, namespace, prefix, 'position', String(position));
2575
+ }
2576
+ function appendCharacterSpacingProperty(document, properties, namespace, prefix, mark) {
2577
+ const spacing = normalizeDocumentCharacterSpacingTwips(mark?.attrs?.characterSpacingTwips);
2578
+ if (null === spacing) return;
2579
+ appendValuedProperty(document, properties, namespace, prefix, 'spacing', String(spacing));
2231
2580
  }
2232
2581
  function appendFontProperties(document, properties, namespace, prefix, mark) {
2582
+ const scriptFonts = parseDocumentScriptFonts('string' == typeof mark?.attrs?.scriptFonts ? mark.attrs.scriptFonts : void 0);
2583
+ if (scriptFonts) {
2584
+ const fonts = wordElement(document, namespace, prefix, 'rFonts');
2585
+ for (const [name, value] of [
2586
+ [
2587
+ 'ascii',
2588
+ scriptFonts.ascii?.name
2589
+ ],
2590
+ [
2591
+ 'hAnsi',
2592
+ scriptFonts.highAnsi?.name
2593
+ ],
2594
+ [
2595
+ 'eastAsia',
2596
+ scriptFonts.eastAsia?.name
2597
+ ],
2598
+ [
2599
+ 'cs',
2600
+ scriptFonts.complexScript?.name
2601
+ ],
2602
+ [
2603
+ 'asciiTheme',
2604
+ scriptFonts.ascii?.theme
2605
+ ],
2606
+ [
2607
+ 'hAnsiTheme',
2608
+ scriptFonts.highAnsi?.theme
2609
+ ],
2610
+ [
2611
+ 'eastAsiaTheme',
2612
+ scriptFonts.eastAsia?.theme
2613
+ ],
2614
+ [
2615
+ 'cstheme',
2616
+ scriptFonts.complexScript?.theme
2617
+ ],
2618
+ [
2619
+ 'hint',
2620
+ scriptFonts.hint
2621
+ ]
2622
+ ])if (value) work_docx_format_change_export_setWordAttribute(fonts, namespace, prefix, name, value);
2623
+ properties.append(fonts);
2624
+ return;
2625
+ }
2233
2626
  const family = firstFontFamily(mark?.attrs?.fontFamily);
2234
2627
  if (!family) return;
2235
2628
  const fonts = wordElement(document, namespace, prefix, 'rFonts');
@@ -2241,14 +2634,41 @@ function appendFontProperties(document, properties, namespace, prefix, mark) {
2241
2634
  ])work_docx_format_change_export_setWordAttribute(fonts, namespace, prefix, name, family);
2242
2635
  properties.append(fonts);
2243
2636
  }
2637
+ function appendUnderlineProperty(document, properties, namespace, prefix, mark) {
2638
+ if (!mark) return;
2639
+ const style = normalizeDocumentUnderlineStyle(mark.attrs?.underlineStyle) ?? 'single';
2640
+ const color = normalizedHex(mark.attrs?.underlineColor);
2641
+ const theme = themeReference(mark.attrs?.underlineThemeColor);
2642
+ const underline = wordElement(document, namespace, prefix, 'u');
2643
+ work_docx_format_change_export_setWordAttribute(underline, namespace, prefix, 'val', style);
2644
+ const resolved = color ?? normalizedHex(theme?.resolved);
2645
+ if (resolved) work_docx_format_change_export_setWordAttribute(underline, namespace, prefix, 'color', resolved);
2646
+ if (theme) setThemeAttributes(underline, namespace, prefix, theme, 'themeColor');
2647
+ properties.append(underline);
2648
+ }
2244
2649
  function appendBooleanProperty(document, properties, namespace, prefix, marks, mark, property) {
2245
2650
  if (marks.has(mark)) properties.append(wordElement(document, namespace, prefix, property));
2246
2651
  }
2652
+ function appendStrikeProperties(document, properties, namespace, prefix, mark) {
2653
+ if (!mark) return;
2654
+ const style = normalizeDocumentStrikeStyle(mark.attrs?.strikeStyle) ?? 'single';
2655
+ appendValuedProperty(document, properties, namespace, prefix, 'strike', 'single' === style ? '1' : '0');
2656
+ appendValuedProperty(document, properties, namespace, prefix, 'dstrike', 'double' === style ? '1' : '0');
2657
+ }
2247
2658
  function appendValuedProperty(document, properties, namespace, prefix, property, value) {
2248
2659
  const element = wordElement(document, namespace, prefix, property);
2249
2660
  work_docx_format_change_export_setWordAttribute(element, namespace, prefix, 'val', value);
2250
2661
  properties.append(element);
2251
2662
  }
2663
+ function appendTextCaseProperties(document, properties, namespace, prefix, mark) {
2664
+ const textCase = mark?.attrs?.textCase;
2665
+ if ('all-caps' === textCase) properties.append(wordElement(document, namespace, prefix, 'caps'));
2666
+ else if ('small-caps' === textCase) properties.append(wordElement(document, namespace, prefix, 'smallCaps'));
2667
+ else if ('none' === textCase) {
2668
+ appendValuedProperty(document, properties, namespace, prefix, 'caps', '0');
2669
+ appendValuedProperty(document, properties, namespace, prefix, 'smallCaps', '0');
2670
+ }
2671
+ }
2252
2672
  function appendColorProperty(document, properties, namespace, prefix, mark) {
2253
2673
  const color = normalizedHex(mark?.attrs?.color);
2254
2674
  const theme = themeReference(mark?.attrs?.themeColor);
@@ -7283,6 +7703,131 @@ function docxParagraphPartPaths(pathsByLower, contentTypes) {
7283
7703
  function isObfuscatedFontContentType(type) {
7284
7704
  return type?.trim().toLowerCase() === 'application/vnd.openxmlformats-officedocument.obfuscatedfont';
7285
7705
  }
7706
+ const RUN_FONT_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes|comments)\.xml$/i;
7707
+ const WORD_FONT_ATTRIBUTE_NAMES = new Set([
7708
+ 'ascii',
7709
+ 'hAnsi',
7710
+ 'eastAsia',
7711
+ 'cs',
7712
+ 'asciiTheme',
7713
+ 'hAnsiTheme',
7714
+ 'eastAsiaTheme',
7715
+ 'cstheme',
7716
+ 'hint'
7717
+ ]);
7718
+ class DocxRunFontsPatchCollector {
7719
+ patches = [];
7720
+ nextMarker = 1;
7721
+ occupied;
7722
+ markersByFonts = new Map();
7723
+ constructor(source){
7724
+ this.occupied = source;
7725
+ }
7726
+ marker(source, slot, currentFontFamily) {
7727
+ const fonts = normalizeDocumentScriptFonts(source);
7728
+ const normalizedSlot = normalizeDocumentScriptFontSlot(slot) ?? documentScriptFontSlotFromHint(fonts?.hint);
7729
+ const expected = fonts ? documentScriptFontFamily(fonts, normalizedSlot) : void 0;
7730
+ const current = documentFontNameFromCssFamily(currentFontFamily);
7731
+ if (!fonts || (expected ? documentFontNameFromCssFamily(expected)?.toLocaleLowerCase() !== current?.toLocaleLowerCase() : Boolean(currentFontFamily.trim()))) return null;
7732
+ const key = serializeDocumentScriptFonts(fonts);
7733
+ if (!key) return null;
7734
+ const existing = this.markersByFonts.get(key);
7735
+ if (existing) return existing;
7736
+ let marker = '';
7737
+ do {
7738
+ marker = `A3SOfficeRunFonts${String(this.nextMarker).padStart(8, '0')}`;
7739
+ this.nextMarker += 1;
7740
+ }while (this.occupied.includes(marker) || this.patches.some((patch)=>patch.marker === marker))
7741
+ this.patches.push({
7742
+ marker,
7743
+ fonts
7744
+ });
7745
+ this.markersByFonts.set(key, marker);
7746
+ return marker;
7747
+ }
7748
+ }
7749
+ async function patchDocxRunFonts(buffer, patches) {
7750
+ if (!patches.length) return buffer;
7751
+ const archive = await jszip.loadAsync(buffer);
7752
+ const byMarker = new Map(patches.map((patch)=>[
7753
+ patch.marker,
7754
+ patch.fonts
7755
+ ]));
7756
+ const applied = new Set();
7757
+ for (const entry of Object.values(archive.files)){
7758
+ if (entry.dir || !RUN_FONT_PART_PATTERN.test(entry.name)) continue;
7759
+ const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
7760
+ let changedPart = false;
7761
+ for (const element of descendants(document, 'rFonts')){
7762
+ if (!DOCX_WORDPROCESSING_NAMESPACES.has(element.namespaceURI ?? '')) continue;
7763
+ const marker = wordFontAttribute(element, 'ascii');
7764
+ if (!marker) continue;
7765
+ const fonts = byMarker.get(marker);
7766
+ if (fonts) {
7767
+ replaceWordFontAttributes(element, fonts);
7768
+ applied.add(marker);
7769
+ changedPart = true;
7770
+ }
7771
+ }
7772
+ if (changedPart) archive.file(entry.name, serializeUtf8Xml(document));
7773
+ }
7774
+ const missing = patches.filter((patch)=>!applied.has(patch.marker));
7775
+ if (missing.length) throw new Error(`DOCX run-font markers were not emitted: ${missing.map((patch)=>patch.marker).join(', ')}.`);
7776
+ return archive.generateAsync({
7777
+ type: 'arraybuffer'
7778
+ });
7779
+ }
7780
+ function replaceWordFontAttributes(element, fonts) {
7781
+ 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);
7782
+ const values = [
7783
+ [
7784
+ 'ascii',
7785
+ fonts.ascii?.name
7786
+ ],
7787
+ [
7788
+ 'hAnsi',
7789
+ fonts.highAnsi?.name
7790
+ ],
7791
+ [
7792
+ 'eastAsia',
7793
+ fonts.eastAsia?.name
7794
+ ],
7795
+ [
7796
+ 'cs',
7797
+ fonts.complexScript?.name
7798
+ ],
7799
+ [
7800
+ 'asciiTheme',
7801
+ fonts.ascii?.theme
7802
+ ],
7803
+ [
7804
+ 'hAnsiTheme',
7805
+ fonts.highAnsi?.theme
7806
+ ],
7807
+ [
7808
+ 'eastAsiaTheme',
7809
+ fonts.eastAsia?.theme
7810
+ ],
7811
+ [
7812
+ 'cstheme',
7813
+ fonts.complexScript?.theme
7814
+ ],
7815
+ [
7816
+ 'hint',
7817
+ fonts.hint
7818
+ ]
7819
+ ];
7820
+ for (const [name, value] of values)if (value) setWordFontAttribute(element, name, value);
7821
+ }
7822
+ function wordFontAttribute(element, name) {
7823
+ const values = Array.from(element.attributes).filter((item)=>xmlAttributeLocalName(item) === name && DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, item) ?? ''));
7824
+ return 1 === values.length ? values[0]?.value ?? null : null;
7825
+ }
7826
+ function setWordFontAttribute(element, name, value) {
7827
+ const namespace = element.namespaceURI ?? '';
7828
+ const prefix = element.prefix || 'w';
7829
+ element.setAttributeNS(namespace, `${prefix}:${name}`, value);
7830
+ }
7286
7831
  async function createDocxBlob(content, sourcePackage) {
7287
7832
  const docx = await import("docx");
7288
7833
  const normalizedContent = {
@@ -7317,7 +7862,13 @@ async function createDocxBlob(content, sourcePackage) {
7317
7862
  paragraphIdentityPatches: new DocxParagraphIdentityPatchCollector(JSON.stringify(normalizedContent)),
7318
7863
  equationPatches: new DocxEquationPatchCollector(JSON.stringify(normalizedContent)),
7319
7864
  formattingChangePatches: new DocxRunFormattingChangePatchCollector(),
7320
- paragraphFormattingChangePatches: new DocxParagraphFormattingChangePatchCollector()
7865
+ runFontPatches: new DocxRunFontsPatchCollector(JSON.stringify(normalizedContent)),
7866
+ hiddenTextPatches: new DocxHiddenTextPatchCollector(JSON.stringify(normalizedContent)),
7867
+ legacyTextEffectsPatches: new DocxLegacyTextEffectsPatchCollector(JSON.stringify(normalizedContent)),
7868
+ usedNativeTextEffectMarkers: new Set(),
7869
+ paragraphFormattingChangePatches: new DocxParagraphFormattingChangePatchCollector(),
7870
+ hasExplicitZeroCharacterSpacing: false,
7871
+ hasExplicitZeroKerningThreshold: false
7321
7872
  };
7322
7873
  const commentRecords = createDocxCommentRecords(commentThreads, docx, noteContext.commentIds);
7323
7874
  const sections = [];
@@ -7364,7 +7915,12 @@ async function createDocxBlob(content, sourcePackage) {
7364
7915
  }
7365
7916
  });
7366
7917
  const packed = await docx.Packer.toBlob(document);
7367
- const formattingChangesPatched = await patchDocxRunFormattingChanges(await packed.arrayBuffer(), noteContext.formattingChangePatches.patches);
7918
+ const characterSpacingPatched = noteContext.hasExplicitZeroCharacterSpacing ? await patchDocxExplicitZeroCharacterSpacing(await packed.arrayBuffer()) : await packed.arrayBuffer();
7919
+ const kerningPatched = noteContext.hasExplicitZeroKerningThreshold ? await patchDocxExplicitZeroKerningThresholds(characterSpacingPatched) : characterSpacingPatched;
7920
+ const runFontsPatched = await patchDocxRunFonts(kerningPatched, noteContext.runFontPatches.patches);
7921
+ const hiddenTextPatched = await patchDocxHiddenText(runFontsPatched, noteContext.hiddenTextPatches.patches.filter((patch)=>noteContext.usedNativeTextEffectMarkers.has(patch.marker)));
7922
+ const legacyTextEffectsPatched = await patchDocxLegacyTextEffects(hiddenTextPatched, noteContext.legacyTextEffectsPatches.patches.filter((patch)=>noteContext.usedNativeTextEffectMarkers.has(patch.marker)));
7923
+ const formattingChangesPatched = await patchDocxRunFormattingChanges(legacyTextEffectsPatched, noteContext.formattingChangePatches.patches);
7368
7924
  const noteImageRelationshipsPatched = await patchDocxNoteImageRelationships(formattingChangesPatched);
7369
7925
  const commentMetadataPatched = await patchDocxCommentMetadata(noteImageRelationshipsPatched, commentThreads);
7370
7926
  const numberingPatched = await patchDocxNumberingRestartRules(commentMetadataPatched, noteContext.numberingRestartRules);
@@ -7391,6 +7947,60 @@ async function createDocxBlob(content, sourcePackage) {
7391
7947
  type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
7392
7948
  });
7393
7949
  }
7950
+ function docxNativeTextEffectState(style, context) {
7951
+ let current = style;
7952
+ let hiddenText;
7953
+ const legacyTextEffects = {};
7954
+ const seen = new Set();
7955
+ while(current && !seen.has(current)){
7956
+ seen.add(current);
7957
+ const hiddenPatch = context.hiddenTextPatches.lookup(current);
7958
+ if (hiddenPatch) {
7959
+ hiddenText ??= hiddenPatch.value;
7960
+ current = hiddenPatch.style;
7961
+ continue;
7962
+ }
7963
+ const legacyPatch = context.legacyTextEffectsPatches.lookup(current);
7964
+ if (legacyPatch) {
7965
+ for (const [name, value] of Object.entries(legacyPatch.effects)){
7966
+ const effect = name;
7967
+ if (void 0 === legacyTextEffects[effect]) legacyTextEffects[effect] = value;
7968
+ }
7969
+ current = legacyPatch.style;
7970
+ continue;
7971
+ }
7972
+ break;
7973
+ }
7974
+ return {
7975
+ ...current ? {
7976
+ baseStyle: current
7977
+ } : {},
7978
+ ...void 0 !== hiddenText ? {
7979
+ hiddenText
7980
+ } : {},
7981
+ legacyTextEffects
7982
+ };
7983
+ }
7984
+ function markDocxNativeTextEffectStyleUsed(style, context) {
7985
+ let current = style;
7986
+ const seen = new Set();
7987
+ while(current && !seen.has(current)){
7988
+ seen.add(current);
7989
+ const hiddenPatch = context.hiddenTextPatches.lookup(current);
7990
+ if (hiddenPatch) {
7991
+ context.usedNativeTextEffectMarkers.add(current);
7992
+ current = hiddenPatch.style;
7993
+ continue;
7994
+ }
7995
+ const legacyPatch = context.legacyTextEffectsPatches.lookup(current);
7996
+ if (legacyPatch) {
7997
+ context.usedNativeTextEffectMarkers.add(current);
7998
+ current = legacyPatch.style;
7999
+ continue;
8000
+ }
8001
+ break;
8002
+ }
8003
+ }
7394
8004
  function work_docx_export_sectionProperties(layout, docx) {
7395
8005
  const landscape = 'landscape' === layout.orientation;
7396
8006
  const dimensions = documentPageGeometryForLayout(layout);
@@ -7560,6 +8170,7 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
7560
8170
  const visit = async (node, inherited = {}, revision)=>{
7561
8171
  if (node.nodeType === Node.TEXT_NODE) {
7562
8172
  if (!node.textContent) return [];
8173
+ markDocxNativeTextEffectStyleUsed(inherited.style, noteContext);
7563
8174
  if (revision?.kind === 'insertion') return [
7564
8175
  new docx.InsertedTextRun({
7565
8176
  ...inherited,
@@ -7624,29 +8235,80 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
7624
8235
  fill: backgroundColor
7625
8236
  } : inherited.shading;
7626
8237
  const themeFillMarker = noteContext.themePatches.marker('fill', parseDocxThemeReference(node.dataset.officeThemeFill), resolvedShading?.fill ? `#${resolvedShading.fill}` : null);
8238
+ const explicitTextCase = normalizeDocumentTextCase(node.dataset.officeTextCase);
8239
+ const explicitCharacterSpacing = documentCharacterSpacingTwipsFromElement(node);
8240
+ const explicitCharacterScale = documentCharacterScalePercentFromElement(node);
8241
+ const explicitKerningThreshold = documentKerningThresholdHalfPointsFromElement(node);
8242
+ const explicitEmphasisMark = documentEmphasisMarkFromElement(node);
8243
+ const explicitHiddenText = documentHiddenTextFromElement(node);
8244
+ const hasExplicitLegacyTextEffects = 'span' === tag && [
8245
+ DOCUMENT_LEGACY_TEXT_OUTLINE_ATTRIBUTE,
8246
+ DOCUMENT_LEGACY_TEXT_SHADOW_ATTRIBUTE,
8247
+ DOCUMENT_LEGACY_TEXT_EMBOSS_ATTRIBUTE,
8248
+ DOCUMENT_LEGACY_TEXT_IMPRINT_ATTRIBUTE
8249
+ ].some((attribute)=>node.hasAttribute(attribute));
8250
+ const explicitLegacyTextEffects = hasExplicitLegacyTextEffects ? documentLegacyTextEffectsFromElement(node) : {};
8251
+ if (hasExplicitLegacyTextEffects && !explicitLegacyTextEffects) throw new Error('Document contains invalid legacy text effects.');
8252
+ const explicitCharacterPosition = documentCharacterPositionHalfPointsFromElement(node);
8253
+ if (0 === explicitCharacterSpacing) noteContext.hasExplicitZeroCharacterSpacing = true;
8254
+ if (0 === explicitKerningThreshold) noteContext.hasExplicitZeroKerningThreshold = true;
8255
+ const characterSpacing = null === explicitCharacterSpacing ? inherited.characterSpacing : docxCharacterSpacingValue(explicitCharacterSpacing);
8256
+ const scale = null === explicitCharacterScale ? inherited.scale : docxCharacterScaleValue(explicitCharacterScale);
8257
+ const kern = null === explicitKerningThreshold ? inherited.kern : docxKerningThresholdValue(explicitKerningThreshold);
8258
+ const position = null === explicitCharacterPosition ? inherited.position : docxCharacterPositionValue(explicitCharacterPosition);
8259
+ const emphasisMark = null === explicitEmphasisMark ? inherited.emphasisMark : docxEmphasisMarkRunOptions(explicitEmphasisMark);
8260
+ const textCaseOptions = docxTextCaseRunOptions(explicitTextCase, inherited);
8261
+ const inheritedNativeTextEffects = docxNativeTextEffectState(inherited.style, noteContext);
8262
+ const legacyTextEffects = {
8263
+ ...inheritedNativeTextEffects.legacyTextEffects,
8264
+ ...explicitLegacyTextEffects ?? {}
8265
+ };
8266
+ if (documentLegacyTextEffectsConflict(legacyTextEffects)) throw new Error('Document contains conflicting legacy text effects.');
8267
+ const hiddenText = null === explicitHiddenText ? inheritedNativeTextEffects.hiddenText : explicitHiddenText;
8268
+ let nativeTextEffectStyle = inherited.style;
8269
+ if (hasExplicitLegacyTextEffects || null !== explicitHiddenText) {
8270
+ nativeTextEffectStyle = inheritedNativeTextEffects.baseStyle;
8271
+ if (Object.keys(legacyTextEffects).length) nativeTextEffectStyle = noteContext.legacyTextEffectsPatches.marker(legacyTextEffects, nativeTextEffectStyle);
8272
+ if (void 0 !== hiddenText) nativeTextEffectStyle = noteContext.hiddenTextPatches.marker(hiddenText, nativeTextEffectStyle);
8273
+ }
8274
+ const underline = docxUnderlineRunOptions(node, inherited.underline, noteContext.themePatches);
8275
+ const strike = docxStrikeRunOptions(node, inherited);
8276
+ const scriptFontMarker = noteContext.runFontPatches.marker(documentScriptFontsFromElement(node), documentScriptFontSlotFromElement(node), node.style.fontFamily);
7627
8277
  const style = {
7628
8278
  ...inherited,
8279
+ style: nativeTextEffectStyle,
7629
8280
  bold: inherited.bold || 'strong' === tag || 'b' === tag,
7630
8281
  italics: inherited.italics || 'em' === tag || 'i' === tag,
7631
- underline: inherited.underline || 'u' === tag ? {} : void 0,
7632
- strike: inherited.strike || 's' === tag || 'strike' === tag,
8282
+ underline,
8283
+ ...strike,
7633
8284
  subScript: inherited.subScript || 'sub' === tag,
7634
8285
  superScript: inherited.superScript || 'sup' === tag,
7635
8286
  color: themeColorMarker ?? resolvedColor,
7636
- font: cssFontFamily(node.style.fontFamily) ?? inherited.font,
8287
+ font: scriptFontMarker ? {
8288
+ ascii: scriptFontMarker
8289
+ } : cssFontFamily(node.style.fontFamily) ?? inherited.font,
7637
8290
  size: cssFontSize(node.style.fontSize) ?? inherited.size,
8291
+ characterSpacing,
8292
+ scale,
8293
+ kern,
8294
+ position,
8295
+ emphasisMark,
7638
8296
  shading: themeFillMarker ? {
7639
8297
  fill: themeFillMarker
7640
8298
  } : resolvedShading,
7641
8299
  snapToGrid: dataBoolean(node.dataset.officeWordSnapToGrid) ?? inherited.snapToGrid,
7642
- rightToLeft: void 0 === direction ? inherited.rightToLeft : 'rtl' === direction
8300
+ rightToLeft: void 0 === direction ? inherited.rightToLeft : 'rtl' === direction,
8301
+ ...textCaseOptions
7643
8302
  };
7644
- if ('br' === tag) return [
7645
- new docx.TextRun({
7646
- ...style,
7647
- break: 1
7648
- })
7649
- ];
8303
+ if ('br' === tag) {
8304
+ markDocxNativeTextEffectStyleUsed(style.style, noteContext);
8305
+ return [
8306
+ new docx.TextRun({
8307
+ ...style,
8308
+ break: 1
8309
+ })
8310
+ ];
8311
+ }
7650
8312
  if ('img' === tag) return [
7651
8313
  await imageToDocx(node, docx, noteContext.imageCropPatches, noteContext.imageWrapPatches, noteContext.imageLayerPatches, noteContext.imageIdentityPatches)
7652
8314
  ];
@@ -7692,6 +8354,64 @@ async function work_docx_export_inlineRuns(root, docx, noteContext) {
7692
8354
  for (const node of root.childNodes)runs.push(...await visit(node, inheritedDirection));
7693
8355
  return runs;
7694
8356
  }
8357
+ function docxStrikeRunOptions(element, inherited) {
8358
+ if (element.hasAttribute(DOCUMENT_UNDERLINE_STYLE_ATTRIBUTE) && !element.hasAttribute(DOCUMENT_STRIKE_STYLE_ATTRIBUTE)) return {
8359
+ doubleStrike: inherited.doubleStrike,
8360
+ strike: inherited.strike
8361
+ };
8362
+ const formatting = documentStrikeFormattingFromElement(element);
8363
+ if (!formatting) return {
8364
+ doubleStrike: inherited.doubleStrike,
8365
+ strike: inherited.strike
8366
+ };
8367
+ return {
8368
+ doubleStrike: 'double' === formatting.style,
8369
+ strike: 'single' === formatting.style
8370
+ };
8371
+ }
8372
+ function docxUnderlineRunOptions(element, inherited, themePatches) {
8373
+ if (element.hasAttribute(DOCUMENT_STRIKE_STYLE_ATTRIBUTE) && !element.hasAttribute(DOCUMENT_UNDERLINE_STYLE_ATTRIBUTE)) return inherited;
8374
+ const formatting = documentUnderlineFormattingFromElement(element);
8375
+ if (!formatting) return inherited;
8376
+ const color = docxUnderlineColor(formatting, themePatches);
8377
+ return {
8378
+ type: formatting.style,
8379
+ ...color ? {
8380
+ color
8381
+ } : {}
8382
+ };
8383
+ }
8384
+ function docxUnderlineColor(formatting, themePatches) {
8385
+ const direct = formatting.color ? cssColorToHex(formatting.color) : void 0;
8386
+ return themePatches.marker('underline', formatting.themeColor ?? null, direct ? `#${direct}` : null) ?? direct;
8387
+ }
8388
+ function docxTextCaseRunOptions(explicitTextCase, inherited) {
8389
+ if (null === explicitTextCase) {
8390
+ if (void 0 !== inherited.smallCaps) return {
8391
+ allCaps: void 0,
8392
+ smallCaps: inherited.smallCaps
8393
+ };
8394
+ return void 0 === inherited.allCaps ? {} : {
8395
+ allCaps: inherited.allCaps,
8396
+ smallCaps: void 0
8397
+ };
8398
+ }
8399
+ if ('all-caps' === explicitTextCase) return {
8400
+ allCaps: true,
8401
+ smallCaps: void 0
8402
+ };
8403
+ if ('small-caps' === explicitTextCase) return {
8404
+ allCaps: void 0,
8405
+ smallCaps: true
8406
+ };
8407
+ return inherited.smallCaps ? {
8408
+ allCaps: void 0,
8409
+ smallCaps: false
8410
+ } : {
8411
+ allCaps: false,
8412
+ smallCaps: void 0
8413
+ };
8414
+ }
7695
8415
  function docxTextRevision(element, kind, context) {
7696
8416
  const id = docxRevisionId(element, context);
7697
8417
  const sourceDate = element.dataset.changeDate?.trim() ?? '';