@a3s-lab/office 0.38.1 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -4
- package/dist/{0~7240.js → 0~2330.js} +897 -641
- package/dist/0~5024.js +240 -240
- package/dist/0~7828.js +51 -29
- package/dist/0~document-editor.js +1 -0
- package/dist/0~presentation-editor.js +264 -118
- package/dist/0~work-docx-export.js +1023 -922
- package/dist/0~work-docx-import.js +13 -4
- package/dist/0~work-office-diagnostics.js +96 -94
- package/dist/0~work-pptx-import.js +1 -1
- package/dist/0~work-presentation-transition.js +38 -0
- package/dist/4121.js +42 -9
- package/dist/4174.js +497 -233
- package/dist/4560.js +124 -9
- package/dist/internal/features/work/editors/presentation-animation-panel.d.ts +9 -7
- package/dist/internal/features/work/editors/presentation-command-types.d.ts +10 -10
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +6 -4
- package/dist/internal/features/work/editors/use-presentation-animation-commands.d.ts +7 -7
- package/dist/internal/features/work/work-document-format-change-tracking.d.ts +1 -1
- package/dist/internal/features/work/work-document-numbering-change-tracking.d.ts +7 -0
- package/dist/internal/features/work/work-document-numbering-changes.d.ts +29 -0
- package/dist/internal/features/work/work-docx-import.d.ts +7 -5
- package/dist/internal/features/work/work-docx-list-export.d.ts +1 -0
- package/dist/internal/features/work/work-docx-numbering-change-export.d.ts +13 -0
- package/dist/internal/features/work/work-docx-numbering-change-import.d.ts +17 -0
- package/dist/internal/features/work/work-presentation-animation.d.ts +6 -2
- package/dist/internal/features/work/work-presentation-clipboard.d.ts +0 -5
- package/dist/internal/features/work/work-types.d.ts +8 -4
- package/dist/office-kernel.wasm +0 -0
- package/dist/styles.css +162 -26
- package/package.json +8 -5
- package/dist/0~5809.js +0 -123
|
@@ -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,
|
|
3
|
-
import { normalizeDocumentRunBorder, parseDocumentScriptFonts,
|
|
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
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
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
|
|
2515
|
-
|
|
2516
|
-
if (
|
|
2517
|
-
|
|
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
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
return
|
|
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
|
|
2534
|
-
|
|
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
|
|
2541
|
-
|
|
2542
|
-
if ('
|
|
2543
|
-
if ('
|
|
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
|
|
2547
|
-
return
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
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
|
|
2554
|
-
|
|
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 =
|
|
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 =
|
|
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)
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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(
|
|
3170
|
-
else if ('small-caps' === textCase) properties.append(
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
3253
|
-
const
|
|
3254
|
-
return
|
|
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
|
|
3257
|
-
|
|
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
|
|
3446
|
-
|
|
3447
|
-
|
|
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
|
|
3450
|
-
|
|
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
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
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
|
|
3461
|
-
|
|
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
|
-
|
|
3469
|
-
|
|
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
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
this.
|
|
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
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
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
|
|
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
|
|
3489
|
-
|
|
3490
|
-
|
|
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, '
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
if (
|
|
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,
|
|
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
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
}
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
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
|
|
3527
|
-
|
|
3528
|
-
|
|
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
|
|
3536
|
-
const
|
|
3537
|
-
const
|
|
3538
|
-
const
|
|
3539
|
-
|
|
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:
|
|
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
|
|
4032
|
-
return
|
|
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
|
|
4390
|
-
const
|
|
4391
|
-
|
|
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
|
|
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
|
|
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');
|