@a3s-lab/office 0.2.2 → 0.4.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/COLLABORATION_ROADMAP.md +426 -0
- package/README.md +649 -10
- package/dist/0~1403.js +1 -1
- package/dist/0~2805.js +1279 -0
- package/dist/{0~use-office-draft.js → 0~345.js} +30 -1
- package/dist/0~3539.js +1 -1
- package/dist/0~3557.js +1 -1
- package/dist/0~3635.js +1 -1
- package/dist/0~4595.js +123 -12
- package/dist/0~4980.js +154 -0
- package/dist/0~5050.js +124 -113
- package/dist/{0~9512.js → 0~6090.js} +540 -3
- package/dist/0~7048.js +92 -66
- package/dist/0~7614.js +2 -1
- package/dist/0~7754.js +5653 -0
- package/dist/0~9445.js +1 -1
- package/dist/0~document-editor.js +4992 -3323
- package/dist/0~markdown-editor.js +250 -44
- package/dist/0~pdf-viewer.js +1016 -24
- package/dist/0~presentation-editor.js +179 -8
- package/dist/0~spreadsheet-editor.js +3371 -578
- package/dist/0~work-docx-export.js +6801 -658
- package/dist/0~work-docx-import.js +1388 -795
- package/dist/0~work-office-diagnostics.js +524 -30
- package/dist/0~work-pptx-export.js +2 -1
- package/dist/0~work-pptx-import.js +1 -2
- package/dist/0~work-presentation-charts.js +1 -1
- package/dist/2591.js +197 -0
- package/dist/3818.js +1090 -0
- package/dist/4104.js +970 -60
- package/dist/432.js +171 -14
- package/dist/4476.js +2008 -0
- package/dist/4560.js +674 -0
- package/dist/4650.js +70 -0
- package/dist/{1134.js → 5184.js} +183 -1
- package/dist/6489.js +18 -18
- package/dist/7060.js +132 -0
- package/dist/8715.js +2 -136
- package/dist/8928.js +16778 -0
- package/dist/9787.js +209 -0
- package/dist/core.d.ts +29 -9
- package/dist/core.js +10 -2
- package/dist/index.js +13 -3
- package/dist/internal/collaboration/office-collaboration-flat-json.d.ts +13 -0
- package/dist/internal/collaboration/office-collaboration-json.d.ts +4 -0
- package/dist/internal/collaboration/office-collaboration-presence.d.ts +86 -0
- package/dist/internal/collaboration/office-collaboration-transport.d.ts +42 -0
- package/dist/internal/collaboration/office-collaboration.d.ts +85 -0
- package/dist/internal/collaboration/office-document-collaboration-bibliography.d.ts +7 -0
- package/dist/internal/collaboration/office-document-collaboration-claims.d.ts +9 -0
- package/dist/internal/collaboration/office-document-collaboration-comments.d.ts +7 -0
- package/dist/internal/collaboration/office-document-collaboration-sidecar-utils.d.ts +27 -0
- package/dist/internal/collaboration/office-document-collaboration-sidecars.d.ts +16 -0
- package/dist/internal/collaboration/office-document-collaboration.d.ts +45 -0
- package/dist/internal/collaboration/office-markdown-collaboration.d.ts +31 -0
- package/dist/internal/collaboration/office-pdf-collaboration-claims.d.ts +4 -0
- package/dist/internal/collaboration/office-pdf-collaboration-conflicts.d.ts +2 -0
- package/dist/internal/collaboration/office-pdf-collaboration-model.d.ts +26 -0
- package/dist/internal/collaboration/office-pdf-collaboration-records.d.ts +21 -0
- package/dist/internal/collaboration/office-pdf-collaboration-types.d.ts +91 -0
- package/dist/internal/collaboration/office-pdf-collaboration-validation.d.ts +4 -0
- package/dist/internal/collaboration/office-pdf-collaboration.d.ts +35 -0
- package/dist/internal/collaboration/office-presentation-collaboration-model.d.ts +25 -0
- package/dist/internal/collaboration/office-presentation-collaboration-records.d.ts +22 -0
- package/dist/internal/collaboration/office-presentation-collaboration-validation.d.ts +4 -0
- package/dist/internal/collaboration/office-presentation-collaboration.d.ts +32 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-cells.d.ts +13 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-claims.d.ts +4 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-conflicts.d.ts +2 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-flat-json.d.ts +10 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-model.d.ts +31 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-records.d.ts +17 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration-validation.d.ts +4 -0
- package/dist/internal/collaboration/office-spreadsheet-collaboration.d.ts +32 -0
- package/dist/internal/design-system/primitives/navigation/tabs.d.ts +3 -1
- package/dist/internal/features/work/components/work-document-page-border.d.ts +6 -0
- package/dist/internal/features/work/editors/document-command-catalog.d.ts +536 -0
- package/dist/internal/features/work/editors/document-editor-focus.d.ts +1 -1
- package/dist/internal/features/work/editors/document-editor-support.d.ts +7 -3
- package/dist/internal/features/work/editors/document-editor.d.ts +8 -2
- package/dist/internal/features/work/editors/document-external-content.d.ts +14 -0
- package/dist/internal/features/work/editors/document-format-clipboard.d.ts +6 -0
- package/dist/internal/features/work/editors/document-format-tools.d.ts +4 -0
- package/dist/internal/features/work/editors/document-layout-panel.d.ts +4 -1
- package/dist/internal/features/work/editors/document-page-chrome-wps-shortcuts.d.ts +2 -0
- package/dist/internal/features/work/editors/document-page-layout-ribbon.d.ts +14 -0
- package/dist/internal/features/work/editors/document-page-stack.d.ts +10 -1
- package/dist/internal/features/work/editors/document-page-thumbnail.d.ts +2 -0
- package/dist/internal/features/work/editors/document-pagination-run-coordinator.d.ts +26 -0
- package/dist/internal/features/work/editors/document-picture-properties-dialog-model.d.ts +20 -1
- package/dist/internal/features/work/editors/document-review-navigation.d.ts +9 -0
- package/dist/internal/features/work/editors/document-statistics-dialog.d.ts +7 -0
- package/dist/internal/features/work/editors/document-status-bar.d.ts +2 -1
- package/dist/internal/features/work/editors/document-table-properties-dialog-model.d.ts +4 -1
- package/dist/internal/features/work/editors/document-toolbar.d.ts +19 -42
- package/dist/internal/features/work/editors/document-wps-shortcuts.d.ts +12 -0
- package/dist/internal/features/work/editors/document-zoom.d.ts +17 -0
- package/dist/internal/features/work/editors/markdown-editor-focus.d.ts +1 -1
- package/dist/internal/features/work/editors/markdown-editor.d.ts +5 -1
- package/dist/internal/features/work/editors/markdown-toolbar.d.ts +2 -1
- package/dist/internal/features/work/editors/office-collaboration-participants.d.ts +3 -0
- package/dist/internal/features/work/editors/office-collaboration-presence-context.d.ts +17 -0
- package/dist/internal/features/work/editors/office-collaboration-presence-ui.d.ts +25 -0
- package/dist/internal/features/work/editors/office-editor-focus-handoff.d.ts +27 -0
- package/dist/internal/features/work/editors/pdf-annotation-controller.d.ts +5 -0
- package/dist/internal/features/work/editors/pdf-collaboration-presence.d.ts +3 -0
- package/dist/internal/features/work/editors/pdf-collaboration-projection.d.ts +10 -0
- package/dist/internal/features/work/editors/pdf-evidence-contract.d.ts +36 -0
- package/dist/internal/features/work/editors/pdf-evidence-overlay.d.ts +37 -0
- package/dist/internal/features/work/editors/pdf-toolbar.d.ts +2 -1
- package/dist/internal/features/work/editors/pdf-viewer-controller.d.ts +7 -1
- package/dist/internal/features/work/editors/pdf-viewer.d.ts +11 -1
- package/dist/internal/features/work/editors/presentation-collaboration-presence.d.ts +5 -0
- package/dist/internal/features/work/editors/presentation-editor-focus.d.ts +3 -2
- package/dist/internal/features/work/editors/presentation-editor-types.d.ts +3 -0
- package/dist/internal/features/work/editors/presentation-editor.d.ts +1 -1
- package/dist/internal/features/work/editors/spreadsheet-auto-filter.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-clear.d.ts +3 -0
- package/dist/internal/features/work/editors/spreadsheet-cell-merge.d.ts +12 -0
- package/dist/internal/features/work/editors/spreadsheet-clipboard-shortcuts.d.ts +3 -7
- package/dist/internal/features/work/editors/spreadsheet-collaboration-presence.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +464 -0
- package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +45 -9
- package/dist/internal/features/work/editors/spreadsheet-context-menu.d.ts +2 -0
- package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +9 -28
- package/dist/internal/features/work/editors/spreadsheet-editor.d.ts +8 -2
- package/dist/internal/features/work/editors/spreadsheet-format-painter.d.ts +27 -0
- package/dist/internal/features/work/editors/spreadsheet-freeze-panes.d.ts +14 -0
- package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +3 -2
- package/dist/internal/features/work/editors/use-document-pagination.d.ts +6 -1
- package/dist/internal/features/work/editors/use-document-review-conflicts.d.ts +28 -0
- package/dist/internal/features/work/editors/use-spreadsheet-auto-filter.d.ts +21 -0
- package/dist/internal/features/work/editors/use-spreadsheet-collaboration.d.ts +27 -0
- package/dist/internal/features/work/editors/use-spreadsheet-format-painter.d.ts +18 -0
- package/dist/internal/features/work/editors/work-office-chrome.d.ts +22 -3
- package/dist/internal/features/work/editors/work-office-ribbon-overflow.d.ts +2 -0
- package/dist/internal/features/work/work-document-bookmark-references.d.ts +37 -0
- package/dist/internal/features/work/work-document-bookmarks.d.ts +29 -0
- package/dist/internal/features/work/work-document-caption-nodes.d.ts +4 -1
- package/dist/internal/features/work/work-document-comments.d.ts +1 -1
- package/dist/internal/features/work/work-document-equations.d.ts +513 -0
- package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
- package/dist/internal/features/work/work-document-field-identity.d.ts +2 -0
- package/dist/internal/features/work/work-document-field-node.d.ts +2 -2
- package/dist/internal/features/work/work-document-fields.d.ts +7 -0
- package/dist/internal/features/work/work-document-image-identity.d.ts +30 -0
- package/dist/internal/features/work/work-document-image-layout.d.ts +46 -1
- package/dist/internal/features/work/work-document-image-wrap-contour.d.ts +16 -0
- package/dist/internal/features/work/work-document-incremental-layout.d.ts +2 -2
- package/dist/internal/features/work/work-document-model-codec.d.ts +4 -1
- package/dist/internal/features/work/work-document-note-integrity.d.ts +2 -0
- package/dist/internal/features/work/work-document-page-borders.d.ts +25 -0
- package/dist/internal/features/work/work-document-page-capture.d.ts +8 -0
- package/dist/internal/features/work/work-document-page-frames.d.ts +15 -0
- package/dist/internal/features/work/work-document-page-margins.d.ts +56 -0
- package/dist/internal/features/work/work-document-page-size.d.ts +47 -0
- package/dist/internal/features/work/work-document-pagination-measurement.d.ts +1 -2
- package/dist/internal/features/work/work-document-pagination-types.d.ts +7 -2
- package/dist/internal/features/work/work-document-pagination.d.ts +2 -2
- package/dist/internal/features/work/work-document-paragraph-borders.d.ts +32 -0
- package/dist/internal/features/work/work-document-paragraph-identity.d.ts +26 -0
- package/dist/internal/features/work/work-document-paragraph-shading.d.ts +18 -0
- package/dist/internal/features/work/work-document-review-conflicts.d.ts +35 -0
- package/dist/internal/features/work/work-document-section.d.ts +4 -0
- package/dist/internal/features/work/work-document-snapshot.d.ts +19 -0
- package/dist/internal/features/work/work-document-source.d.ts +23 -0
- package/dist/internal/features/work/work-document-table-column-widths.d.ts +7 -0
- package/dist/internal/features/work/work-document-table-property-changes.d.ts +3 -0
- package/dist/internal/features/work/work-document-table-row-identity.d.ts +15 -0
- package/dist/internal/features/work/work-document-table-sizing.d.ts +3 -0
- package/dist/internal/features/work/work-document-text-layout.d.ts +3 -1
- package/dist/internal/features/work/work-document-word-line-metrics.d.ts +2 -0
- package/dist/internal/features/work/work-docx-bookmark-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-bookmark-import.d.ts +24 -0
- package/dist/internal/features/work/work-docx-bookmarks.d.ts +17 -0
- package/dist/internal/features/work/work-docx-comment-durable-id-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-comment-export.d.ts +3 -0
- package/dist/internal/features/work/work-docx-comment-metadata.d.ts +2 -0
- package/dist/internal/features/work/work-docx-drawing-extension-preservation.d.ts +4 -0
- package/dist/internal/features/work/work-docx-equation-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-equation-export.d.ts +14 -0
- package/dist/internal/features/work/work-docx-equation-import.d.ts +30 -0
- package/dist/internal/features/work/work-docx-equation-story.d.ts +9 -0
- package/dist/internal/features/work/work-docx-export-formatting.d.ts +3 -1
- package/dist/internal/features/work/work-docx-export-image.d.ts +5 -1
- package/dist/internal/features/work/work-docx-export.d.ts +1 -1
- package/dist/internal/features/work/work-docx-field-instructions.d.ts +8 -0
- package/dist/internal/features/work/work-docx-font-table-preservation.d.ts +3 -0
- package/dist/internal/features/work/work-docx-ignorable-extension-preservation.d.ts +15 -0
- package/dist/internal/features/work/work-docx-image-crop.d.ts +12 -0
- package/dist/internal/features/work/work-docx-image-identity.d.ts +17 -0
- package/dist/internal/features/work/work-docx-image-layer.d.ts +12 -0
- package/dist/internal/features/work/work-docx-image-layout-import.d.ts +14 -2
- package/dist/internal/features/work/work-docx-image-wrap.d.ts +15 -0
- package/dist/internal/features/work/work-docx-import.d.ts +13 -3
- package/dist/internal/features/work/work-docx-list-export.d.ts +18 -0
- package/dist/internal/features/work/work-docx-list-import.d.ts +14 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-block-preservation.d.ts +18 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-model.d.ts +47 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-preservation.d.ts +4 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-properties.d.ts +17 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-table-model.d.ts +17 -0
- package/dist/internal/features/work/work-docx-note-comment-content-control-xml.d.ts +14 -0
- package/dist/internal/features/work/work-docx-note-comment-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-note-comment-hyperlink-content.d.ts +40 -0
- package/dist/internal/features/work/work-docx-note-comment-hyperlink-preservation.d.ts +5 -0
- package/dist/internal/features/work/work-docx-note-comment-hyperlink-relationships.d.ts +35 -0
- package/dist/internal/features/work/work-docx-note-comment-identity.d.ts +15 -0
- package/dist/internal/features/work/work-docx-note-comment-run-preservation.d.ts +4 -0
- package/dist/internal/features/work/work-docx-note-comment-run-properties.d.ts +2 -0
- package/dist/internal/features/work/work-docx-note-image-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-note-image-relationships.d.ts +1 -0
- package/dist/internal/features/work/work-docx-numbering-extension-preservation.d.ts +6 -0
- package/dist/internal/features/work/work-docx-numbering.d.ts +1 -0
- package/dist/internal/features/work/work-docx-page-borders-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-page-borders-import.d.ts +10 -0
- package/dist/internal/features/work/work-docx-page-margins-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-page-margins-import.d.ts +18 -0
- package/dist/internal/features/work/work-docx-page-size-diagnostics.d.ts +2 -0
- package/dist/internal/features/work/work-docx-page-size-import.d.ts +17 -0
- package/dist/internal/features/work/work-docx-paragraph-borders-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-paragraph-borders-export.d.ts +18 -0
- package/dist/internal/features/work/work-docx-paragraph-borders-import.d.ts +24 -0
- package/dist/internal/features/work/work-docx-paragraph-default-collapsed.d.ts +19 -0
- package/dist/internal/features/work/work-docx-paragraph-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-paragraph-identity-import.d.ts +10 -0
- package/dist/internal/features/work/work-docx-paragraph-identity.d.ts +18 -0
- package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +6 -1
- package/dist/internal/features/work/work-docx-paragraph-shading-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-paragraph-shading-export.d.ts +5 -0
- package/dist/internal/features/work/work-docx-paragraph-shading-import.d.ts +18 -0
- package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +3 -0
- package/dist/internal/features/work/work-docx-settings-preservation.d.ts +10 -0
- package/dist/internal/features/work/work-docx-settings-xml.d.ts +16 -0
- package/dist/internal/features/work/work-docx-style-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-table-cell-export.d.ts +2 -1
- package/dist/internal/features/work/work-docx-table-cell-import.d.ts +3 -0
- package/dist/internal/features/work/work-docx-table-extension-preservation.d.ts +2 -0
- package/dist/internal/features/work/work-docx-table-row-import.d.ts +2 -0
- package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +1 -0
- package/dist/internal/features/work/work-docx-theme-reference.d.ts +23 -0
- package/dist/internal/features/work/work-docx-theme.d.ts +4 -1
- package/dist/internal/features/work/work-docx-twips.d.ts +12 -0
- package/dist/internal/features/work/work-file-contract.d.ts +1 -0
- package/dist/internal/features/work/work-file-io.d.ts +1 -1
- package/dist/internal/features/work/work-markdown-extensions.d.ts +4 -1
- package/dist/internal/features/work/work-ooxml-package-preservation.d.ts +12 -0
- package/dist/internal/features/work/work-ooxml-package-security.d.ts +6 -0
- package/dist/internal/features/work/work-ooxml-relationship-preservation.d.ts +10 -0
- package/dist/internal/features/work/work-ooxml-xml.d.ts +2 -0
- package/dist/internal/features/work/work-pdf-export.d.ts +10 -0
- package/dist/internal/features/work/work-repository.d.ts +2 -0
- package/dist/internal/features/work/work-types.d.ts +15 -2
- package/dist/internal/kernel/office-kernel-client-types.d.ts +1 -0
- package/dist/internal/kernel/office-kernel-protocol.d.ts +7 -0
- package/dist/internal/kernel/office-kernel-version.d.ts +1 -1
- package/dist/office-kernel.wasm +0 -0
- package/dist/office-kernel.worker.js +93 -41
- package/dist/react.d.ts +18 -12
- package/dist/react.js +1 -1
- package/dist/styles.css +1367 -16
- package/dist/vue.d.ts +48 -5
- package/dist/vue.js +42 -3
- package/dist/web-component.d.ts +24 -2
- package/dist/web-component.js +101 -2
- package/docs/latest/en/browser-editor-architecture.md +262 -47
- package/package.json +29 -4
- package/dist/0~152.js +0 -94
- package/dist/0~2582.js +0 -485
- package/dist/0~4750.js +0 -939
- package/dist/0~5861.js +0 -596
- package/dist/0~work-document-extensions.js +0 -3045
- package/dist/0~work-document-model-codec.js +0 -42
- package/dist/0~work-docx-bibliography.js +0 -347
- package/dist/0~work-docx-page-color.js +0 -42
- package/dist/2881.js +0 -1455
- package/dist/8728.js +0 -723
package/dist/0~2805.js
ADDED
|
@@ -0,0 +1,1279 @@
|
|
|
1
|
+
import jszip from "jszip";
|
|
2
|
+
import { serializeUtf8Xml, directChildren, xmlNamespacePrefix, parseXml, firstDescendant, decodeXmlBytes, directChild, descendants, attribute as work_ooxml_package_attribute } from "./5184.js";
|
|
3
|
+
import { createDocumentBibliography, documentCitationStyle, documentCitationStyleDetails, normalizeDocumentTabStops } from "./8928.js";
|
|
4
|
+
const BIBLIOGRAPHY_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/bibliography';
|
|
5
|
+
const CUSTOM_XML_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/customXml';
|
|
6
|
+
const OFFICE_RELATIONSHIPS_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
|
|
7
|
+
const PACKAGE_RELATIONSHIPS_NAMESPACE = 'http://schemas.openxmlformats.org/package/2006/relationships';
|
|
8
|
+
const CONTENT_TYPES_NAMESPACE = 'http://schemas.openxmlformats.org/package/2006/content-types';
|
|
9
|
+
const CUSTOM_XML_RELATIONSHIP = `${OFFICE_RELATIONSHIPS_NAMESPACE}/customXml`;
|
|
10
|
+
const CUSTOM_XML_PROPS_RELATIONSHIP = `${OFFICE_RELATIONSHIPS_NAMESPACE}/customXmlProps`;
|
|
11
|
+
const CUSTOM_XML_PROPS_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.customXmlProperties+xml';
|
|
12
|
+
const COMMON_SOURCE_TYPES = new Set([
|
|
13
|
+
'Book',
|
|
14
|
+
'BookSection',
|
|
15
|
+
'JournalArticle',
|
|
16
|
+
'ArticleInAPeriodical',
|
|
17
|
+
'ConferenceProceedings',
|
|
18
|
+
'Report',
|
|
19
|
+
'InternetSite',
|
|
20
|
+
'DocumentFromInternetSite',
|
|
21
|
+
'ElectronicSource',
|
|
22
|
+
'Misc'
|
|
23
|
+
]);
|
|
24
|
+
const RESERVED_FIELDS = new Set([
|
|
25
|
+
'Tag',
|
|
26
|
+
'SourceType',
|
|
27
|
+
'Guid',
|
|
28
|
+
'Author',
|
|
29
|
+
'Title',
|
|
30
|
+
'Year',
|
|
31
|
+
'Publisher',
|
|
32
|
+
'City',
|
|
33
|
+
'JournalName',
|
|
34
|
+
'Volume',
|
|
35
|
+
'Issue',
|
|
36
|
+
'Pages',
|
|
37
|
+
'URL',
|
|
38
|
+
'StandardNumber',
|
|
39
|
+
'ConferenceName',
|
|
40
|
+
'Institution'
|
|
41
|
+
]);
|
|
42
|
+
async function readDocxBibliography(archive) {
|
|
43
|
+
const paths = archive.paths('customXml/').filter((path)=>/^customXml\/item(?!Props)[^/]*\.xml$/i.test(path));
|
|
44
|
+
const sources = [];
|
|
45
|
+
const usedTags = new Set();
|
|
46
|
+
const duplicateTags = new Set();
|
|
47
|
+
const uncommonSourceTypes = new Set();
|
|
48
|
+
let sourcePartCount = 0;
|
|
49
|
+
let unreadablePartCount = 0;
|
|
50
|
+
let selectedStyle = '';
|
|
51
|
+
let styleName = '';
|
|
52
|
+
for (const path of paths)try {
|
|
53
|
+
const document = await archive.xml(path);
|
|
54
|
+
if ('Sources' !== document.documentElement.localName || document.documentElement.namespaceURI !== BIBLIOGRAPHY_NAMESPACE) continue;
|
|
55
|
+
sourcePartCount += 1;
|
|
56
|
+
selectedStyle ||= work_ooxml_package_attribute(document.documentElement, 'SelectedStyle')?.trim() ?? '';
|
|
57
|
+
styleName ||= work_ooxml_package_attribute(document.documentElement, 'StyleName')?.trim() ?? '';
|
|
58
|
+
for (const element of directChildren(document.documentElement, 'Source')){
|
|
59
|
+
const source = parseCitationSource(element, sources.length + 1);
|
|
60
|
+
if (usedTags.has(source.tag)) {
|
|
61
|
+
duplicateTags.add(source.tag);
|
|
62
|
+
source.tag = uniqueSourceTag(source.tag, usedTags);
|
|
63
|
+
}
|
|
64
|
+
usedTags.add(source.tag);
|
|
65
|
+
if (!COMMON_SOURCE_TYPES.has(source.sourceType)) uncommonSourceTypes.add(source.sourceType);
|
|
66
|
+
sources.push(source);
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
unreadablePartCount += 1;
|
|
70
|
+
}
|
|
71
|
+
if (!sourcePartCount) return {
|
|
72
|
+
sourcePartCount,
|
|
73
|
+
unreadablePartCount,
|
|
74
|
+
duplicateTags: [],
|
|
75
|
+
uncommonSourceTypes: [],
|
|
76
|
+
uncommonStyle: void 0
|
|
77
|
+
};
|
|
78
|
+
const styleResult = bibliographyStyle(selectedStyle, styleName);
|
|
79
|
+
const style = styleResult.style;
|
|
80
|
+
return {
|
|
81
|
+
bibliography: {
|
|
82
|
+
...createDocumentBibliography(style),
|
|
83
|
+
selectedStyle: selectedStyle || documentCitationStyleDetails(style).selectedStyle,
|
|
84
|
+
styleName: styleName || documentCitationStyleDetails(style).name,
|
|
85
|
+
sources
|
|
86
|
+
},
|
|
87
|
+
sourcePartCount,
|
|
88
|
+
unreadablePartCount,
|
|
89
|
+
duplicateTags: Array.from(duplicateTags),
|
|
90
|
+
uncommonSourceTypes: Array.from(uncommonSourceTypes),
|
|
91
|
+
uncommonStyle: styleResult.recognized ? void 0 : styleName || selectedStyle
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async function patchDocxBibliography(buffer, bibliography) {
|
|
95
|
+
if (!bibliography?.sources.length) return buffer;
|
|
96
|
+
const archive = await jszip.loadAsync(buffer);
|
|
97
|
+
const itemId = stableGuid(`bibliography:${bibliography.sources.map((source)=>source.tag).join('|')}`);
|
|
98
|
+
archive.file('customXml/item1.xml', serializeBibliographySources(bibliography));
|
|
99
|
+
archive.file('customXml/itemProps1.xml', serializeCustomXmlProperties(itemId));
|
|
100
|
+
archive.file('customXml/_rels/item1.xml.rels', serializeCustomXmlRelationships());
|
|
101
|
+
await upsertRelationship(archive, 'word/document.xml', CUSTOM_XML_RELATIONSHIP, '../customXml/item1.xml');
|
|
102
|
+
await upsertContentType(archive, '/customXml/itemProps1.xml', CUSTOM_XML_PROPS_CONTENT_TYPE);
|
|
103
|
+
return archive.generateAsync({
|
|
104
|
+
type: 'arraybuffer',
|
|
105
|
+
compression: 'DEFLATE'
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function parseCitationSource(element, index) {
|
|
109
|
+
const simple = new Map(directChildren(element).filter((child)=>!child.children.length).map((child)=>[
|
|
110
|
+
child.localName,
|
|
111
|
+
child.textContent?.trim() ?? ''
|
|
112
|
+
]));
|
|
113
|
+
const tag = simple.get('Tag') || `Source${index}`;
|
|
114
|
+
const additionalFields = {};
|
|
115
|
+
for (const [name, value] of simple)if (!RESERVED_FIELDS.has(name) && value) additionalFields[name] = value;
|
|
116
|
+
return {
|
|
117
|
+
id: `docx-source-${safeIdPart(tag)}-${index}`,
|
|
118
|
+
tag,
|
|
119
|
+
sourceType: simple.get('SourceType') || 'Misc',
|
|
120
|
+
guid: simple.get('Guid') || void 0,
|
|
121
|
+
title: simple.get('Title') || '',
|
|
122
|
+
year: simple.get('Year') || void 0,
|
|
123
|
+
contributors: parseCitationContributors(directChild(element, 'Author')),
|
|
124
|
+
publisher: simple.get('Publisher') || void 0,
|
|
125
|
+
city: simple.get('City') || void 0,
|
|
126
|
+
journalName: simple.get('JournalName') || void 0,
|
|
127
|
+
volume: simple.get('Volume') || void 0,
|
|
128
|
+
issue: simple.get('Issue') || void 0,
|
|
129
|
+
pages: simple.get('Pages') || void 0,
|
|
130
|
+
url: simple.get('URL') || void 0,
|
|
131
|
+
standardNumber: simple.get('StandardNumber') || void 0,
|
|
132
|
+
conferenceName: simple.get('ConferenceName') || void 0,
|
|
133
|
+
institution: simple.get('Institution') || void 0,
|
|
134
|
+
additionalFields: Object.keys(additionalFields).length ? additionalFields : void 0
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function parseCitationContributors(wrapper) {
|
|
138
|
+
if (!wrapper) return;
|
|
139
|
+
const roles = directChild(wrapper, 'NameList') || directChild(wrapper, 'Corporate') ? [
|
|
140
|
+
wrapper
|
|
141
|
+
] : directChildren(wrapper);
|
|
142
|
+
const contributors = {};
|
|
143
|
+
for (const role of roles){
|
|
144
|
+
const nameList = directChild(role, 'NameList');
|
|
145
|
+
const people = nameList ? directChildren(nameList, 'Person').map(parseCitationPerson).filter(validCitationPerson) : [];
|
|
146
|
+
const corporate = directChild(role, 'Corporate')?.textContent?.trim();
|
|
147
|
+
if (people.length || corporate) contributors[role === wrapper ? 'Author' : role.localName] = {
|
|
148
|
+
people: people.length ? people : void 0,
|
|
149
|
+
corporate: corporate || void 0
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return Object.keys(contributors).length ? contributors : void 0;
|
|
153
|
+
}
|
|
154
|
+
function parseCitationPerson(element) {
|
|
155
|
+
return {
|
|
156
|
+
first: directChild(element, 'First')?.textContent?.trim() ?? '',
|
|
157
|
+
middle: directChild(element, 'Middle')?.textContent?.trim() || void 0,
|
|
158
|
+
last: directChild(element, 'Last')?.textContent?.trim() ?? '',
|
|
159
|
+
suffix: directChild(element, 'Suffix')?.textContent?.trim() || void 0
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function validCitationPerson(person) {
|
|
163
|
+
return Boolean(person.first || person.middle || person.last || person.suffix);
|
|
164
|
+
}
|
|
165
|
+
function serializeBibliographySources(bibliography) {
|
|
166
|
+
const styleDetails = documentCitationStyleDetails(bibliography.style);
|
|
167
|
+
const document = parseXml(`<b:Sources xmlns:b="${BIBLIOGRAPHY_NAMESPACE}" xmlns="${BIBLIOGRAPHY_NAMESPACE}"/>`, 'DOCX bibliography sources');
|
|
168
|
+
const root = document.documentElement;
|
|
169
|
+
root.setAttribute('SelectedStyle', bibliography.selectedStyle || styleDetails.selectedStyle);
|
|
170
|
+
root.setAttribute('StyleName', bibliography.styleName || styleDetails.name);
|
|
171
|
+
root.setAttribute('Version', '6');
|
|
172
|
+
for (const source of bibliography.sources){
|
|
173
|
+
const element = bibliographyElement(document, 'Source');
|
|
174
|
+
appendTextElement(document, element, 'Tag', source.tag);
|
|
175
|
+
appendTextElement(document, element, 'SourceType', source.sourceType || 'Misc');
|
|
176
|
+
appendTextElement(document, element, 'Guid', normalizeGuid(source.guid) || stableGuid(`source:${source.id}:${source.tag}`));
|
|
177
|
+
appendContributors(document, element, source.contributors);
|
|
178
|
+
appendTextElement(document, element, 'Title', source.title);
|
|
179
|
+
appendTextElement(document, element, 'Year', source.year);
|
|
180
|
+
appendTextElement(document, element, 'Publisher', source.publisher);
|
|
181
|
+
appendTextElement(document, element, 'City', source.city);
|
|
182
|
+
appendTextElement(document, element, 'JournalName', source.journalName);
|
|
183
|
+
appendTextElement(document, element, 'Volume', source.volume);
|
|
184
|
+
appendTextElement(document, element, 'Issue', source.issue);
|
|
185
|
+
appendTextElement(document, element, 'Pages', source.pages);
|
|
186
|
+
appendTextElement(document, element, 'URL', source.url);
|
|
187
|
+
appendTextElement(document, element, 'StandardNumber', source.standardNumber);
|
|
188
|
+
appendTextElement(document, element, 'ConferenceName', source.conferenceName);
|
|
189
|
+
appendTextElement(document, element, 'Institution', source.institution);
|
|
190
|
+
for (const [name, value] of Object.entries(source.additionalFields ?? {}).sort(([left], [right])=>left.localeCompare(right)))if (!RESERVED_FIELDS.has(name) && /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(name)) appendTextElement(document, element, name, value);
|
|
191
|
+
root.append(element);
|
|
192
|
+
}
|
|
193
|
+
return new XMLSerializer().serializeToString(document);
|
|
194
|
+
}
|
|
195
|
+
function appendContributors(document, source, contributors) {
|
|
196
|
+
const entries = Object.entries(contributors ?? {}).filter(([, contributor])=>contributor.people?.length || contributor.corporate?.trim());
|
|
197
|
+
if (!entries.length) return;
|
|
198
|
+
const wrapper = bibliographyElement(document, 'Author');
|
|
199
|
+
for (const [roleName, contributor] of entries){
|
|
200
|
+
if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(roleName)) continue;
|
|
201
|
+
const role = bibliographyElement(document, roleName);
|
|
202
|
+
if (contributor.people?.length) {
|
|
203
|
+
const nameList = bibliographyElement(document, 'NameList');
|
|
204
|
+
for (const person of contributor.people){
|
|
205
|
+
const element = bibliographyElement(document, 'Person');
|
|
206
|
+
appendTextElement(document, element, 'Last', person.last);
|
|
207
|
+
appendTextElement(document, element, 'First', person.first);
|
|
208
|
+
appendTextElement(document, element, 'Middle', person.middle);
|
|
209
|
+
appendTextElement(document, element, 'Suffix', person.suffix);
|
|
210
|
+
nameList.append(element);
|
|
211
|
+
}
|
|
212
|
+
role.append(nameList);
|
|
213
|
+
} else if (contributor.corporate?.trim()) appendTextElement(document, role, 'Corporate', contributor.corporate);
|
|
214
|
+
wrapper.append(role);
|
|
215
|
+
}
|
|
216
|
+
if (wrapper.children.length) source.append(wrapper);
|
|
217
|
+
}
|
|
218
|
+
function appendTextElement(document, parent, name, value) {
|
|
219
|
+
if (!value?.trim()) return;
|
|
220
|
+
const element = bibliographyElement(document, name);
|
|
221
|
+
element.textContent = value.trim();
|
|
222
|
+
parent.append(element);
|
|
223
|
+
}
|
|
224
|
+
function bibliographyElement(document, name) {
|
|
225
|
+
return document.createElementNS(BIBLIOGRAPHY_NAMESPACE, `b:${name}`);
|
|
226
|
+
}
|
|
227
|
+
function serializeCustomXmlProperties(itemId) {
|
|
228
|
+
const document = parseXml(`<ds:datastoreItem xmlns:ds="${CUSTOM_XML_NAMESPACE}" ds:itemID="${itemId}"/>`, 'DOCX custom XML properties');
|
|
229
|
+
const references = document.createElementNS(CUSTOM_XML_NAMESPACE, 'ds:schemaRefs');
|
|
230
|
+
const reference = document.createElementNS(CUSTOM_XML_NAMESPACE, 'ds:schemaRef');
|
|
231
|
+
reference.setAttributeNS(CUSTOM_XML_NAMESPACE, 'ds:uri', BIBLIOGRAPHY_NAMESPACE);
|
|
232
|
+
references.append(reference);
|
|
233
|
+
document.documentElement.append(references);
|
|
234
|
+
return new XMLSerializer().serializeToString(document);
|
|
235
|
+
}
|
|
236
|
+
function serializeCustomXmlRelationships() {
|
|
237
|
+
return [
|
|
238
|
+
`<Relationships xmlns="${PACKAGE_RELATIONSHIPS_NAMESPACE}">`,
|
|
239
|
+
`<Relationship Id="rId1" Type="${CUSTOM_XML_PROPS_RELATIONSHIP}" Target="itemProps1.xml"/>`,
|
|
240
|
+
'</Relationships>'
|
|
241
|
+
].join('');
|
|
242
|
+
}
|
|
243
|
+
async function upsertRelationship(archive, sourcePart, type, target) {
|
|
244
|
+
const path = relationshipPartPath(sourcePart);
|
|
245
|
+
const entry = archive.file(path);
|
|
246
|
+
const document = entry ? parseXml(await entry.async('text'), path) : parseXml(`<Relationships xmlns="${PACKAGE_RELATIONSHIPS_NAMESPACE}"/>`, path);
|
|
247
|
+
const root = document.documentElement;
|
|
248
|
+
const existing = directChildren(root, 'Relationship').find((item)=>work_ooxml_package_attribute(item, 'Type') === type);
|
|
249
|
+
if (existing) existing.setAttribute('Target', target);
|
|
250
|
+
else {
|
|
251
|
+
const relationship = document.createElementNS(PACKAGE_RELATIONSHIPS_NAMESPACE, 'Relationship');
|
|
252
|
+
relationship.setAttribute('Id', nextRelationshipId(root));
|
|
253
|
+
relationship.setAttribute('Type', type);
|
|
254
|
+
relationship.setAttribute('Target', target);
|
|
255
|
+
root.append(relationship);
|
|
256
|
+
}
|
|
257
|
+
archive.file(path, new XMLSerializer().serializeToString(document));
|
|
258
|
+
}
|
|
259
|
+
async function upsertContentType(archive, partName, contentType) {
|
|
260
|
+
const path = '[Content_Types].xml';
|
|
261
|
+
const entry = archive.file(path);
|
|
262
|
+
if (!entry) throw new Error('DOCX content types part is missing.');
|
|
263
|
+
const document = parseXml(await entry.async('text'), path);
|
|
264
|
+
const existing = directChildren(document.documentElement, 'Override').find((item)=>work_ooxml_package_attribute(item, 'PartName') === partName);
|
|
265
|
+
if (existing) existing.setAttribute('ContentType', contentType);
|
|
266
|
+
else {
|
|
267
|
+
const override = document.createElementNS(CONTENT_TYPES_NAMESPACE, 'Override');
|
|
268
|
+
override.setAttribute('PartName', partName);
|
|
269
|
+
override.setAttribute('ContentType', contentType);
|
|
270
|
+
document.documentElement.append(override);
|
|
271
|
+
}
|
|
272
|
+
archive.file(path, new XMLSerializer().serializeToString(document));
|
|
273
|
+
}
|
|
274
|
+
function bibliographyStyle(selectedStyle, styleName) {
|
|
275
|
+
const value = `${selectedStyle} ${styleName}`.toLowerCase();
|
|
276
|
+
if (value.includes('ieee')) return {
|
|
277
|
+
style: 'ieee',
|
|
278
|
+
recognized: true
|
|
279
|
+
};
|
|
280
|
+
if (value.includes('mla')) return {
|
|
281
|
+
style: 'mla',
|
|
282
|
+
recognized: true
|
|
283
|
+
};
|
|
284
|
+
if (value.includes('chicago')) return {
|
|
285
|
+
style: 'chicago',
|
|
286
|
+
recognized: true
|
|
287
|
+
};
|
|
288
|
+
if (!value.trim() || value.includes('apa')) return {
|
|
289
|
+
style: documentCitationStyle('apa'),
|
|
290
|
+
recognized: true
|
|
291
|
+
};
|
|
292
|
+
return {
|
|
293
|
+
style: documentCitationStyle('apa'),
|
|
294
|
+
recognized: false
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function relationshipPartPath(sourcePart) {
|
|
298
|
+
const separator = sourcePart.lastIndexOf('/');
|
|
299
|
+
const directory = separator >= 0 ? sourcePart.slice(0, separator + 1) : '';
|
|
300
|
+
const fileName = separator >= 0 ? sourcePart.slice(separator + 1) : sourcePart;
|
|
301
|
+
return `${directory}_rels/${fileName}.rels`;
|
|
302
|
+
}
|
|
303
|
+
function nextRelationshipId(root) {
|
|
304
|
+
const used = new Set(directChildren(root, 'Relationship').map((item)=>work_ooxml_package_attribute(item, 'Id') ?? ''));
|
|
305
|
+
let index = 1;
|
|
306
|
+
while(used.has(`rId${index}`))index += 1;
|
|
307
|
+
return `rId${index}`;
|
|
308
|
+
}
|
|
309
|
+
function uniqueSourceTag(base, used) {
|
|
310
|
+
let index = 2;
|
|
311
|
+
while(used.has(`${base}_${index}`))index += 1;
|
|
312
|
+
return `${base}_${index}`;
|
|
313
|
+
}
|
|
314
|
+
function safeIdPart(value) {
|
|
315
|
+
return value.replace(/[^a-z0-9_-]/gi, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'source';
|
|
316
|
+
}
|
|
317
|
+
function normalizeGuid(value) {
|
|
318
|
+
const match = /^\{?([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}?$/i.exec(value?.trim() ?? '');
|
|
319
|
+
return match ? `{${match[1].toUpperCase()}}` : '';
|
|
320
|
+
}
|
|
321
|
+
function stableGuid(source) {
|
|
322
|
+
const values = [
|
|
323
|
+
0,
|
|
324
|
+
1,
|
|
325
|
+
2,
|
|
326
|
+
3
|
|
327
|
+
].map((salt)=>stableHash(`${salt}:${source}`));
|
|
328
|
+
const hex = values.join('').slice(0, 32).split('');
|
|
329
|
+
hex[12] = '4';
|
|
330
|
+
hex[16] = [
|
|
331
|
+
'8',
|
|
332
|
+
'9',
|
|
333
|
+
'A',
|
|
334
|
+
'B'
|
|
335
|
+
][Number.parseInt(hex[16] ?? '0', 16) % 4];
|
|
336
|
+
const value = hex.join('').toUpperCase();
|
|
337
|
+
return `{${value.slice(0, 8)}-${value.slice(8, 12)}-${value.slice(12, 16)}-${value.slice(16, 20)}-${value.slice(20)}}`;
|
|
338
|
+
}
|
|
339
|
+
function stableHash(source) {
|
|
340
|
+
let hash = 2166136261;
|
|
341
|
+
for(let index = 0; index < source.length; index += 1){
|
|
342
|
+
hash ^= source.charCodeAt(index);
|
|
343
|
+
hash = Math.imul(hash, 16777619);
|
|
344
|
+
}
|
|
345
|
+
return (hash >>> 0).toString(16).padStart(8, '0');
|
|
346
|
+
}
|
|
347
|
+
const XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/';
|
|
348
|
+
const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
|
|
349
|
+
const MARKUP_COMPATIBILITY_NAMESPACE = 'http://schemas.openxmlformats.org/markup-compatibility/2006';
|
|
350
|
+
function declareInheritedNamespaces(importedRoot, sourceRoot) {
|
|
351
|
+
const mappings = new Map();
|
|
352
|
+
for (const element of [
|
|
353
|
+
sourceRoot,
|
|
354
|
+
...Array.from(sourceRoot.querySelectorAll('*'))
|
|
355
|
+
]){
|
|
356
|
+
if (element.prefix && element.namespaceURI && 'xml' !== element.prefix) {
|
|
357
|
+
if (!mappings.has(element.prefix)) mappings.set(element.prefix, element.namespaceURI);
|
|
358
|
+
}
|
|
359
|
+
for (const item of Array.from(element.attributes)){
|
|
360
|
+
const prefix = xmlAttributePrefix(item);
|
|
361
|
+
const namespace = xmlAttributeNamespace(element, item);
|
|
362
|
+
if (prefix && 'xmlns' !== prefix && 'xml' !== prefix && namespace && !mappings.has(prefix)) mappings.set(prefix, namespace);
|
|
363
|
+
}
|
|
364
|
+
if (element.namespaceURI === MARKUP_COMPATIBILITY_NAMESPACE && 'Choice' === element.localName) for (const prefix of (work_ooxml_package_attribute(element, 'Requires') ?? '').trim().split(/\s+/).filter(Boolean)){
|
|
365
|
+
const namespace = xmlNamespaceUri(element, prefix);
|
|
366
|
+
if (namespace && !mappings.has(prefix)) mappings.set(prefix, namespace);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
for (const [prefix, namespace] of mappings)importedRoot.setAttributeNS(XMLNS_NAMESPACE, `xmlns:${prefix}`, namespace);
|
|
370
|
+
}
|
|
371
|
+
function collectRetainedNamespaces(root, retained) {
|
|
372
|
+
for (const element of [
|
|
373
|
+
root,
|
|
374
|
+
...Array.from(root.querySelectorAll('*'))
|
|
375
|
+
]){
|
|
376
|
+
if (element.namespaceURI) retained.add(element.namespaceURI);
|
|
377
|
+
for (const item of Array.from(element.attributes)){
|
|
378
|
+
const namespace = xmlAttributeNamespace(element, item);
|
|
379
|
+
if (namespace && namespace !== XMLNS_NAMESPACE) retained.add(namespace);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
function cloneXmlElement(document, source) {
|
|
384
|
+
const namespace = source.namespaceURI;
|
|
385
|
+
const prefix = xmlNamespacePrefix(source, namespace);
|
|
386
|
+
const qualifiedName = prefix ? `${prefix}:${source.localName}` : source.localName;
|
|
387
|
+
const clone = document.createElementNS(namespace, qualifiedName);
|
|
388
|
+
for (const item of Array.from(source.attributes)){
|
|
389
|
+
const namespaceUri = xmlAttributeNamespace(source, item);
|
|
390
|
+
if (namespaceUri) clone.setAttributeNS(namespaceUri, item.name, item.value);
|
|
391
|
+
else clone.setAttribute(item.name, item.value);
|
|
392
|
+
}
|
|
393
|
+
for (const child of Array.from(source.childNodes))if (child instanceof Element) clone.append(cloneXmlElement(document, child));
|
|
394
|
+
else if (child.nodeType === Node.TEXT_NODE) clone.append(document.createTextNode(child.textContent ?? ''));
|
|
395
|
+
else if (child.nodeType === Node.CDATA_SECTION_NODE) clone.append(document.createCDATASection(child.textContent ?? ''));
|
|
396
|
+
else if (child.nodeType === Node.COMMENT_NODE) clone.append(document.createComment(child.textContent ?? ''));
|
|
397
|
+
return clone;
|
|
398
|
+
}
|
|
399
|
+
function xmlNamespaceUri(element, prefix) {
|
|
400
|
+
if ('xml' === prefix) return XML_NAMESPACE;
|
|
401
|
+
let current = element;
|
|
402
|
+
while(current){
|
|
403
|
+
if (current.prefix === prefix && current.namespaceURI) return current.namespaceURI;
|
|
404
|
+
const declaration = Array.from(current.attributes).find((item)=>isNamespaceDeclaration(item) && (prefix ? item.name === `xmlns:${prefix}` : 'xmlns' === item.name));
|
|
405
|
+
if (declaration) return declaration.value;
|
|
406
|
+
current = current.parentElement;
|
|
407
|
+
}
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
function xmlDeclaredPrefix(root, namespace) {
|
|
411
|
+
for (const item of Array.from(root.attributes))if (isNamespaceDeclaration(item) && item.value === namespace) return 'xmlns' === item.name ? null : item.name.slice(6);
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
function xmlAttributeNamespace(element, item) {
|
|
415
|
+
if (item.namespaceURI) return item.namespaceURI;
|
|
416
|
+
const prefix = xmlAttributePrefix(item);
|
|
417
|
+
return prefix ? xmlNamespaceUri(element, prefix) : null;
|
|
418
|
+
}
|
|
419
|
+
function xmlAttributePrefix(item) {
|
|
420
|
+
if (item.prefix) return item.prefix;
|
|
421
|
+
const separator = item.name.indexOf(':');
|
|
422
|
+
return separator > 0 ? item.name.slice(0, separator) : null;
|
|
423
|
+
}
|
|
424
|
+
function xmlAttributeLocalName(item) {
|
|
425
|
+
const separator = item.localName.indexOf(':');
|
|
426
|
+
return separator >= 0 ? item.localName.slice(separator + 1) : item.localName;
|
|
427
|
+
}
|
|
428
|
+
function hasXmlAttribute(element, namespace, localName) {
|
|
429
|
+
return Array.from(element.attributes).some((item)=>xmlAttributeNamespace(element, item) === namespace && xmlAttributeLocalName(item) === localName);
|
|
430
|
+
}
|
|
431
|
+
function hasUnexpectedXmlAttributes(element, allowedUnqualified) {
|
|
432
|
+
return Array.from(element.attributes).some((item)=>{
|
|
433
|
+
if (isNamespaceDeclaration(item)) return false;
|
|
434
|
+
return null !== xmlAttributeNamespace(element, item) || !allowedUnqualified.has(item.name);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
function hasNonWhitespaceXmlText(element) {
|
|
438
|
+
return Array.from(element.childNodes).some((child)=>(child.nodeType === Node.TEXT_NODE || child.nodeType === Node.CDATA_SECTION_NODE) && Boolean(child.textContent?.trim()));
|
|
439
|
+
}
|
|
440
|
+
function isStructurallyValidAlternateContent(element) {
|
|
441
|
+
if (hasNonWhitespaceXmlText(element) || hasUnexpectedXmlAttributes(element, new Set())) return false;
|
|
442
|
+
const children = Array.from(element.children);
|
|
443
|
+
let choices = 0;
|
|
444
|
+
let fallbacks = 0;
|
|
445
|
+
let sawFallback = false;
|
|
446
|
+
for (const child of children){
|
|
447
|
+
if (child.namespaceURI !== MARKUP_COMPATIBILITY_NAMESPACE || 'Choice' !== child.localName && 'Fallback' !== child.localName) return false;
|
|
448
|
+
const allowedAttributes = 'Choice' === child.localName ? new Set([
|
|
449
|
+
'Requires'
|
|
450
|
+
]) : new Set();
|
|
451
|
+
if (hasNonWhitespaceXmlText(child) || hasUnexpectedXmlAttributes(child, allowedAttributes)) return false;
|
|
452
|
+
if ('Fallback' === child.localName) {
|
|
453
|
+
fallbacks += 1;
|
|
454
|
+
sawFallback = true;
|
|
455
|
+
if (fallbacks > 1) return false;
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
if (sawFallback) return false;
|
|
459
|
+
choices += 1;
|
|
460
|
+
const required = (work_ooxml_package_attribute(child, 'Requires') ?? '').trim().split(/\s+/).filter(Boolean);
|
|
461
|
+
if (!required.length || required.some((prefix)=>!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(prefix) || !xmlNamespaceUri(child, prefix))) return false;
|
|
462
|
+
}
|
|
463
|
+
return choices > 0;
|
|
464
|
+
}
|
|
465
|
+
function assertXmlRoot(root, localName, namespaces, message) {
|
|
466
|
+
if (root.localName !== localName || !namespaces.has(root.namespaceURI ?? '')) throw new Error(message);
|
|
467
|
+
}
|
|
468
|
+
function isNamespaceDeclaration(attributeNode) {
|
|
469
|
+
return attributeNode.namespaceURI === XMLNS_NAMESPACE || 'xmlns' === attributeNode.name || attributeNode.name.startsWith('xmlns:');
|
|
470
|
+
}
|
|
471
|
+
const DOCX_WORDPROCESSING_NAMESPACES = new Set([
|
|
472
|
+
'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
|
|
473
|
+
'http://purl.oclc.org/ooxml/wordprocessingml/main'
|
|
474
|
+
]);
|
|
475
|
+
const work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE = 'http://schemas.openxmlformats.org/markup-compatibility/2006';
|
|
476
|
+
const RELATIONSHIP_NAMESPACES = new Set([
|
|
477
|
+
'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
|
|
478
|
+
'http://purl.oclc.org/ooxml/officeDocument/relationships'
|
|
479
|
+
]);
|
|
480
|
+
const MAX_EXTENSION_ELEMENTS = 4096;
|
|
481
|
+
const MAX_EXTENSION_DEPTH = 64;
|
|
482
|
+
function mergeDocxIgnorableExtensions(generatedDocument, sourceDocument, options = {}) {
|
|
483
|
+
mergeDocxIgnorableExtensionsAtPairs(generatedDocument, sourceDocument, [
|
|
484
|
+
{
|
|
485
|
+
generated: generatedDocument.documentElement,
|
|
486
|
+
source: sourceDocument.documentElement
|
|
487
|
+
}
|
|
488
|
+
], options);
|
|
489
|
+
}
|
|
490
|
+
function mergeDocxIgnorableExtensionsAtPairs(generatedDocument, sourceDocument, pairs, options = {}) {
|
|
491
|
+
if (!pairs.length) return;
|
|
492
|
+
const sourceIgnorable = ignorableNamespaces(sourceDocument.documentElement);
|
|
493
|
+
const context = {
|
|
494
|
+
generatedDocument,
|
|
495
|
+
generatedRoot: generatedDocument.documentElement,
|
|
496
|
+
sourceRoot: sourceDocument.documentElement,
|
|
497
|
+
sourceIgnorable,
|
|
498
|
+
sourceIgnorableNamespaces: new Set(sourceIgnorable.map((item)=>item.namespace)),
|
|
499
|
+
retainedNamespaces: new Set(),
|
|
500
|
+
nextExtensionPrefix: 1,
|
|
501
|
+
options
|
|
502
|
+
};
|
|
503
|
+
for (const pair of pairs)mergeKnownElement(pair.generated, pair.source, context, 0);
|
|
504
|
+
mergeIgnorableNamespaces(context);
|
|
505
|
+
mergeCompatibilityQNameRules(context);
|
|
506
|
+
}
|
|
507
|
+
function mergeKnownElement(generated, source, context, depth) {
|
|
508
|
+
if (context.options.allowMatchedElementMerge && !context.options.allowMatchedElementMerge(generated, source, depth)) return;
|
|
509
|
+
mergePassiveAttributes(generated, source, context);
|
|
510
|
+
if (depth >= MAX_EXTENSION_DEPTH) return;
|
|
511
|
+
const generatedChildren = directChildren(generated);
|
|
512
|
+
const sourceChildren = directChildren(source);
|
|
513
|
+
const generatedGroups = groupChildren(generatedChildren, 'generated', context);
|
|
514
|
+
const sourceGroups = groupChildren(sourceChildren, 'source', context);
|
|
515
|
+
for (const sourceChild of sourceChildren){
|
|
516
|
+
const key = semanticElementKey(sourceChild, 'source', context);
|
|
517
|
+
const generatedMatches = generatedGroups.get(key) ?? [];
|
|
518
|
+
const sourceMatches = sourceGroups.get(key) ?? [];
|
|
519
|
+
if (1 === generatedMatches.length && 1 === sourceMatches.length) mergeKnownElement(generatedMatches[0], sourceChild, context, depth + 1);
|
|
520
|
+
}
|
|
521
|
+
const generatedSemanticNames = new Set(generatedChildren.filter((child)=>isWordprocessingNamespace(child.namespaceURI)).map((child)=>child.localName));
|
|
522
|
+
for (const [index, sourceChild] of sourceChildren.entries()){
|
|
523
|
+
const key = semanticElementKey(sourceChild, 'source', context);
|
|
524
|
+
if ((generatedGroups.get(key)?.length ?? 0) > 0) continue;
|
|
525
|
+
if (!isExtensionCandidate(sourceChild, context)) continue;
|
|
526
|
+
if (!isSafeExtensionSubtree(sourceChild, generatedSemanticNames, context)) continue;
|
|
527
|
+
const imported = cloneXmlElement(context.generatedDocument, sourceChild);
|
|
528
|
+
declareInheritedNamespaces(imported, sourceChild);
|
|
529
|
+
const anchor = followingGeneratedAnchor(sourceChildren, index, generatedGroups, sourceGroups, context);
|
|
530
|
+
generated.insertBefore(imported, anchor);
|
|
531
|
+
collectRetainedNamespaces(sourceChild, context.retainedNamespaces);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
function mergePassiveAttributes(generated, source, context) {
|
|
535
|
+
for (const sourceAttribute of Array.from(source.attributes)){
|
|
536
|
+
const namespace = xmlAttributeNamespace(source, sourceAttribute);
|
|
537
|
+
const localName = xmlAttributeLocalName(sourceAttribute);
|
|
538
|
+
if (!namespace || namespace === XMLNS_NAMESPACE || namespace === XML_NAMESPACE || namespace === work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE || isWordprocessingNamespace(namespace) || RELATIONSHIP_NAMESPACES.has(namespace) || !context.sourceIgnorableNamespaces.has(namespace) || !isAllowedExtensionNamespace(namespace, context) || hasXmlAttribute(generated, namespace, localName)) continue;
|
|
539
|
+
const prefix = ensureRootNamespace(context, xmlAttributePrefix(sourceAttribute), namespace);
|
|
540
|
+
generated.setAttributeNS(namespace, `${prefix}:${localName}`, sourceAttribute.value);
|
|
541
|
+
context.retainedNamespaces.add(namespace);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
function isExtensionCandidate(element, context) {
|
|
545
|
+
const namespace = element.namespaceURI;
|
|
546
|
+
if (!namespace || isWordprocessingNamespace(namespace)) return false;
|
|
547
|
+
if (namespace === work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE) return 'AlternateContent' === element.localName;
|
|
548
|
+
return namespace !== XMLNS_NAMESPACE && namespace !== XML_NAMESPACE && !RELATIONSHIP_NAMESPACES.has(namespace) && context.sourceIgnorableNamespaces.has(namespace) && isAllowedExtensionNamespace(namespace, context);
|
|
549
|
+
}
|
|
550
|
+
function isSafeExtensionSubtree(root, generatedSemanticNames, context) {
|
|
551
|
+
const stack = [
|
|
552
|
+
{
|
|
553
|
+
element: root,
|
|
554
|
+
depth: 1
|
|
555
|
+
}
|
|
556
|
+
];
|
|
557
|
+
let elementCount = 0;
|
|
558
|
+
let alternateContentCount = 0;
|
|
559
|
+
while(stack.length){
|
|
560
|
+
const current = stack.pop();
|
|
561
|
+
if (!current) break;
|
|
562
|
+
elementCount += 1;
|
|
563
|
+
const namespace = current.element.namespaceURI;
|
|
564
|
+
if (elementCount > MAX_EXTENSION_ELEMENTS || current.depth > MAX_EXTENSION_DEPTH || RELATIONSHIP_NAMESPACES.has(namespace ?? '') || hasUnsafeExtensionAttribute(current.element, generatedSemanticNames, context)) return false;
|
|
565
|
+
if (isSemanticNamespace(namespace, context) && !context.options.allowSemanticElement?.(current.element, generatedSemanticNames)) return false;
|
|
566
|
+
if (namespace === work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE) {
|
|
567
|
+
if ('AlternateContent' === current.element.localName) {
|
|
568
|
+
alternateContentCount += 1;
|
|
569
|
+
if (alternateContentCount > 1 || !isStructurallyValidAlternateContent(current.element) || hasUnsafeAlternateContentRequirements(current.element, context)) return false;
|
|
570
|
+
} else if ('Choice' !== current.element.localName && 'Fallback' !== current.element.localName) return false;
|
|
571
|
+
}
|
|
572
|
+
for (const child of directChildren(current.element))stack.push({
|
|
573
|
+
element: child,
|
|
574
|
+
depth: current.depth + 1
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
579
|
+
function hasUnsafeExtensionAttribute(element, generatedSemanticNames, context) {
|
|
580
|
+
return Array.from(element.attributes).some((item)=>{
|
|
581
|
+
const namespace = xmlAttributeNamespace(element, item) ?? '';
|
|
582
|
+
if (!namespace || namespace === XMLNS_NAMESPACE || namespace === XML_NAMESPACE) return false;
|
|
583
|
+
if (RELATIONSHIP_NAMESPACES.has(namespace) || namespace === work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE) return true;
|
|
584
|
+
if (isSemanticNamespace(namespace, context)) return !(isSemanticNamespace(element.namespaceURI, context) && context.options.allowSemanticElement?.(element, generatedSemanticNames));
|
|
585
|
+
return !isAllowedExtensionNamespace(namespace, context);
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
function hasUnsafeAlternateContentRequirements(alternateContent, context) {
|
|
589
|
+
for (const choice of directChildren(alternateContent, 'Choice'))for (const prefix of (work_ooxml_package_attribute(choice, 'Requires') ?? '').trim().split(/\s+/).filter(Boolean)){
|
|
590
|
+
const namespace = xmlNamespaceUri(choice, prefix);
|
|
591
|
+
if (namespace && (isSemanticNamespace(namespace, context) || !isAllowedExtensionNamespace(namespace, context))) return true;
|
|
592
|
+
}
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
function followingGeneratedAnchor(sourceChildren, sourceIndex, generatedGroups, sourceGroups, context) {
|
|
596
|
+
for(let index = sourceIndex + 1; index < sourceChildren.length; index += 1){
|
|
597
|
+
const key = semanticElementKey(sourceChildren[index], 'source', context);
|
|
598
|
+
const generatedMatches = generatedGroups.get(key) ?? [];
|
|
599
|
+
if (1 === generatedMatches.length && (sourceGroups.get(key)?.length ?? 0) === 1) return generatedMatches[0];
|
|
600
|
+
}
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
function groupChildren(children, role, context) {
|
|
604
|
+
const groups = new Map();
|
|
605
|
+
for (const child of children){
|
|
606
|
+
const key = semanticElementKey(child, role, context);
|
|
607
|
+
const existing = groups.get(key);
|
|
608
|
+
if (existing) existing.push(child);
|
|
609
|
+
else groups.set(key, [
|
|
610
|
+
child
|
|
611
|
+
]);
|
|
612
|
+
}
|
|
613
|
+
return groups;
|
|
614
|
+
}
|
|
615
|
+
function semanticElementKey(element, role, context) {
|
|
616
|
+
return context.options.semanticKey?.(element, role) ?? (isWordprocessingNamespace(element.namespaceURI) ? `word:${element.localName}` : `{${element.namespaceURI ?? ''}}${element.localName}`);
|
|
617
|
+
}
|
|
618
|
+
function ignorableNamespaces(root) {
|
|
619
|
+
const result = [];
|
|
620
|
+
const seen = new Set();
|
|
621
|
+
for (const prefix of (work_ooxml_package_attribute(root, 'mc:Ignorable') ?? '').trim().split(/\s+/).filter(Boolean)){
|
|
622
|
+
const namespace = xmlNamespaceUri(root, prefix);
|
|
623
|
+
if (!(!namespace || seen.has(namespace))) {
|
|
624
|
+
seen.add(namespace);
|
|
625
|
+
result.push({
|
|
626
|
+
prefix,
|
|
627
|
+
namespace
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
}
|
|
633
|
+
function mergeIgnorableNamespaces(context) {
|
|
634
|
+
const tokens = (work_ooxml_package_attribute(context.generatedRoot, 'mc:Ignorable') ?? '').trim().split(/\s+/).filter(Boolean);
|
|
635
|
+
const usedTokens = new Set(tokens);
|
|
636
|
+
const coveredNamespaces = new Set(tokens.flatMap((prefix)=>{
|
|
637
|
+
const namespace = xmlNamespaceUri(context.generatedRoot, prefix);
|
|
638
|
+
return namespace ? [
|
|
639
|
+
namespace
|
|
640
|
+
] : [];
|
|
641
|
+
}));
|
|
642
|
+
let changed = false;
|
|
643
|
+
for (const item of context.sourceIgnorable){
|
|
644
|
+
if (!context.retainedNamespaces.has(item.namespace) || coveredNamespaces.has(item.namespace)) continue;
|
|
645
|
+
const prefix = ensureRootNamespace(context, item.prefix, item.namespace);
|
|
646
|
+
if (!usedTokens.has(prefix)) {
|
|
647
|
+
tokens.push(prefix);
|
|
648
|
+
usedTokens.add(prefix);
|
|
649
|
+
changed = true;
|
|
650
|
+
}
|
|
651
|
+
coveredNamespaces.add(item.namespace);
|
|
652
|
+
}
|
|
653
|
+
if (changed) setCompatibilityAttribute(context, 'Ignorable', tokens);
|
|
654
|
+
}
|
|
655
|
+
function mergeCompatibilityQNameRules(context) {
|
|
656
|
+
for (const name of [
|
|
657
|
+
'PreserveAttributes',
|
|
658
|
+
'PreserveElements',
|
|
659
|
+
'ProcessContent'
|
|
660
|
+
]){
|
|
661
|
+
const tokens = (work_ooxml_package_attribute(context.generatedRoot, `mc:${name}`) ?? '').trim().split(/\s+/).filter(Boolean);
|
|
662
|
+
const used = new Set(tokens);
|
|
663
|
+
let changed = false;
|
|
664
|
+
for (const sourceToken of (work_ooxml_package_attribute(context.sourceRoot, `mc:${name}`) ?? '').trim().split(/\s+/).filter(Boolean)){
|
|
665
|
+
const match = /^([A-Za-z_][A-Za-z0-9_.-]*):([A-Za-z_][A-Za-z0-9_.-]*|\*)$/.exec(sourceToken);
|
|
666
|
+
const namespace = match ? xmlNamespaceUri(context.sourceRoot, match[1]) : null;
|
|
667
|
+
if (!match || !namespace || !context.retainedNamespaces.has(namespace)) continue;
|
|
668
|
+
const prefix = ensureRootNamespace(context, match[1], namespace);
|
|
669
|
+
const token = `${prefix}:${match[2]}`;
|
|
670
|
+
if (!used.has(token)) {
|
|
671
|
+
tokens.push(token);
|
|
672
|
+
changed = true;
|
|
673
|
+
}
|
|
674
|
+
used.add(token);
|
|
675
|
+
}
|
|
676
|
+
if (changed) setCompatibilityAttribute(context, name, tokens);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function setCompatibilityAttribute(context, localName, tokens) {
|
|
680
|
+
for (const item of Array.from(context.generatedRoot.attributes))if (xmlAttributeLocalName(item) === localName && (xmlAttributeNamespace(context.generatedRoot, item) === work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE || item.name === `mc:${localName}`)) context.generatedRoot.removeAttributeNode(item);
|
|
681
|
+
const prefix = ensureRootNamespace(context, 'mc', work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE);
|
|
682
|
+
context.generatedRoot.setAttributeNS(work_docx_ignorable_extension_preservation_MARKUP_COMPATIBILITY_NAMESPACE, `${prefix}:${localName}`, tokens.join(' '));
|
|
683
|
+
}
|
|
684
|
+
function ensureRootNamespace(context, preferredPrefix, namespace) {
|
|
685
|
+
const existing = xmlDeclaredPrefix(context.generatedRoot, namespace);
|
|
686
|
+
if (existing) return existing;
|
|
687
|
+
if (preferredPrefix && isNamespacePrefix(preferredPrefix) && !xmlNamespaceUri(context.generatedRoot, preferredPrefix)) {
|
|
688
|
+
context.generatedRoot.setAttributeNS(XMLNS_NAMESPACE, `xmlns:${preferredPrefix}`, namespace);
|
|
689
|
+
return preferredPrefix;
|
|
690
|
+
}
|
|
691
|
+
let prefix;
|
|
692
|
+
do {
|
|
693
|
+
prefix = `a3sExt${context.nextExtensionPrefix}`;
|
|
694
|
+
context.nextExtensionPrefix += 1;
|
|
695
|
+
}while (xmlNamespaceUri(context.generatedRoot, prefix))
|
|
696
|
+
context.generatedRoot.setAttributeNS(XMLNS_NAMESPACE, `xmlns:${prefix}`, namespace);
|
|
697
|
+
return prefix;
|
|
698
|
+
}
|
|
699
|
+
function isWordprocessingNamespace(namespace) {
|
|
700
|
+
return Boolean(namespace && DOCX_WORDPROCESSING_NAMESPACES.has(namespace));
|
|
701
|
+
}
|
|
702
|
+
function isSemanticNamespace(namespace, context) {
|
|
703
|
+
return Boolean(isWordprocessingNamespace(namespace) || namespace && context.options.isAdditionalSemanticNamespace?.(namespace));
|
|
704
|
+
}
|
|
705
|
+
function isAllowedExtensionNamespace(namespace, context) {
|
|
706
|
+
return context.options.allowExtensionNamespace?.(namespace) ?? true;
|
|
707
|
+
}
|
|
708
|
+
function isNamespacePrefix(value) {
|
|
709
|
+
return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) && 'xml' !== value;
|
|
710
|
+
}
|
|
711
|
+
const WORD_THEME_COLOR_KEYS = new Map([
|
|
712
|
+
[
|
|
713
|
+
'dark1',
|
|
714
|
+
'dk1'
|
|
715
|
+
],
|
|
716
|
+
[
|
|
717
|
+
'light1',
|
|
718
|
+
'lt1'
|
|
719
|
+
],
|
|
720
|
+
[
|
|
721
|
+
'dark2',
|
|
722
|
+
'dk2'
|
|
723
|
+
],
|
|
724
|
+
[
|
|
725
|
+
'light2',
|
|
726
|
+
'lt2'
|
|
727
|
+
],
|
|
728
|
+
[
|
|
729
|
+
'background1',
|
|
730
|
+
'lt1'
|
|
731
|
+
],
|
|
732
|
+
[
|
|
733
|
+
'text1',
|
|
734
|
+
'dk1'
|
|
735
|
+
],
|
|
736
|
+
[
|
|
737
|
+
'background2',
|
|
738
|
+
'lt2'
|
|
739
|
+
],
|
|
740
|
+
[
|
|
741
|
+
'text2',
|
|
742
|
+
'dk2'
|
|
743
|
+
],
|
|
744
|
+
[
|
|
745
|
+
'hyperlink',
|
|
746
|
+
'hlink'
|
|
747
|
+
],
|
|
748
|
+
[
|
|
749
|
+
'followedhyperlink',
|
|
750
|
+
'folhlink'
|
|
751
|
+
]
|
|
752
|
+
]);
|
|
753
|
+
const DOCX_COLOR_SCHEME_MAPPING_ATTRIBUTES = new Map([
|
|
754
|
+
[
|
|
755
|
+
'background1',
|
|
756
|
+
'bg1'
|
|
757
|
+
],
|
|
758
|
+
[
|
|
759
|
+
'text1',
|
|
760
|
+
't1'
|
|
761
|
+
],
|
|
762
|
+
[
|
|
763
|
+
'background2',
|
|
764
|
+
'bg2'
|
|
765
|
+
],
|
|
766
|
+
[
|
|
767
|
+
'text2',
|
|
768
|
+
't2'
|
|
769
|
+
],
|
|
770
|
+
[
|
|
771
|
+
'accent1',
|
|
772
|
+
'accent1'
|
|
773
|
+
],
|
|
774
|
+
[
|
|
775
|
+
'accent2',
|
|
776
|
+
'accent2'
|
|
777
|
+
],
|
|
778
|
+
[
|
|
779
|
+
'accent3',
|
|
780
|
+
'accent3'
|
|
781
|
+
],
|
|
782
|
+
[
|
|
783
|
+
'accent4',
|
|
784
|
+
'accent4'
|
|
785
|
+
],
|
|
786
|
+
[
|
|
787
|
+
'accent5',
|
|
788
|
+
'accent5'
|
|
789
|
+
],
|
|
790
|
+
[
|
|
791
|
+
'accent6',
|
|
792
|
+
'accent6'
|
|
793
|
+
],
|
|
794
|
+
[
|
|
795
|
+
'hyperlink',
|
|
796
|
+
'hyperlink'
|
|
797
|
+
],
|
|
798
|
+
[
|
|
799
|
+
'followedhyperlink',
|
|
800
|
+
'followedHyperlink'
|
|
801
|
+
]
|
|
802
|
+
]);
|
|
803
|
+
const WORD_COLOR_SCHEME_INDEXES = new Set([
|
|
804
|
+
'dark1',
|
|
805
|
+
'light1',
|
|
806
|
+
'dark2',
|
|
807
|
+
'light2',
|
|
808
|
+
'accent1',
|
|
809
|
+
'accent2',
|
|
810
|
+
'accent3',
|
|
811
|
+
'accent4',
|
|
812
|
+
'accent5',
|
|
813
|
+
'accent6',
|
|
814
|
+
'hyperlink',
|
|
815
|
+
'followedHyperlink'
|
|
816
|
+
]);
|
|
817
|
+
const WORDPROCESSING_NAMESPACES = new Set([
|
|
818
|
+
'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
|
|
819
|
+
'http://purl.oclc.org/ooxml/wordprocessingml/main'
|
|
820
|
+
]);
|
|
821
|
+
const MAX_COLOR_MAPPING_DEPTH = 16;
|
|
822
|
+
function createDocxThemeResolver(themeDocument, settingsDocument) {
|
|
823
|
+
const colorMapping = docxColorSchemeMapping(settingsDocument);
|
|
824
|
+
if (!themeDocument) return {
|
|
825
|
+
colors: new Map(),
|
|
826
|
+
colorMapping,
|
|
827
|
+
fonts: new Map()
|
|
828
|
+
};
|
|
829
|
+
const fonts = new Map();
|
|
830
|
+
const fontScheme = firstDescendant(themeDocument, 'fontScheme');
|
|
831
|
+
addThemeFontFamily(fonts, 'major', fontScheme ? directChild(fontScheme, 'majorFont') : void 0);
|
|
832
|
+
addThemeFontFamily(fonts, 'minor', fontScheme ? directChild(fontScheme, 'minorFont') : void 0);
|
|
833
|
+
const colors = new Map();
|
|
834
|
+
const colorScheme = firstDescendant(themeDocument, 'clrScheme');
|
|
835
|
+
if (colorScheme) for (const entry of directChildren(colorScheme)){
|
|
836
|
+
const color = themeColorValue(directChildren(entry)[0]);
|
|
837
|
+
if (color) colors.set(entry.localName.toLowerCase(), color);
|
|
838
|
+
}
|
|
839
|
+
return {
|
|
840
|
+
colors,
|
|
841
|
+
colorMapping,
|
|
842
|
+
fonts
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
function resolveDocxThemeResolver(source) {
|
|
846
|
+
return isDocxThemeResolver(source) ? source : createDocxThemeResolver(source);
|
|
847
|
+
}
|
|
848
|
+
function docxThemeFont(resolver, key) {
|
|
849
|
+
return key ? resolver.fonts.get(key.trim().toLowerCase()) : void 0;
|
|
850
|
+
}
|
|
851
|
+
function docxThemeColor(resolver, key, tint, shade) {
|
|
852
|
+
const sourceKey = key?.trim().toLowerCase();
|
|
853
|
+
const mappedKey = sourceKey ? mappedWordThemeColorKey(sourceKey, resolver.colorMapping) : void 0;
|
|
854
|
+
const colorKey = mappedKey ? WORD_THEME_COLOR_KEYS.get(mappedKey) ?? mappedKey : void 0;
|
|
855
|
+
const color = colorKey ? resolver.colors.get(colorKey) : void 0;
|
|
856
|
+
if (!color) return;
|
|
857
|
+
const channels = [
|
|
858
|
+
Number.parseInt(color.slice(0, 2), 16),
|
|
859
|
+
Number.parseInt(color.slice(2, 4), 16),
|
|
860
|
+
Number.parseInt(color.slice(4, 6), 16)
|
|
861
|
+
];
|
|
862
|
+
const shadeFactor = hexFactor(shade);
|
|
863
|
+
const tintFactor = hexFactor(tint);
|
|
864
|
+
const transformed = channels.map((channel)=>{
|
|
865
|
+
const shaded = void 0 === shadeFactor ? channel : channel * shadeFactor;
|
|
866
|
+
const tinted = void 0 === tintFactor ? shaded : shaded + (255 - shaded) * tintFactor;
|
|
867
|
+
return Math.max(0, Math.min(255, Math.round(tinted)));
|
|
868
|
+
});
|
|
869
|
+
return transformed.map((channel)=>channel.toString(16).padStart(2, '0')).join('');
|
|
870
|
+
}
|
|
871
|
+
function docxColorSchemeMapping(settingsDocument) {
|
|
872
|
+
const root = settingsDocument?.documentElement;
|
|
873
|
+
if (!root || 'settings' !== root.localName || !WORDPROCESSING_NAMESPACES.has(root.namespaceURI ?? '')) return new Map();
|
|
874
|
+
const candidates = directChildren(root, 'clrSchemeMapping').filter((element)=>element.namespaceURI === root.namespaceURI);
|
|
875
|
+
if (1 !== candidates.length) return new Map();
|
|
876
|
+
return parseDocxColorSchemeMappingElement(candidates[0]) ?? new Map();
|
|
877
|
+
}
|
|
878
|
+
function parseDocxColorSchemeMappingElement(element) {
|
|
879
|
+
const namespace = element?.namespaceURI ?? '';
|
|
880
|
+
if (!element || 'clrSchemeMapping' !== element.localName || !WORDPROCESSING_NAMESPACES.has(namespace) || directChildren(element).length || Array.from(element.childNodes).some((node)=>node.nodeType === Node.TEXT_NODE && node.textContent?.trim())) return null;
|
|
881
|
+
const semanticsByAttribute = new Map(Array.from(DOCX_COLOR_SCHEME_MAPPING_ATTRIBUTES).map(([semantic, attributeName])=>[
|
|
882
|
+
attributeName,
|
|
883
|
+
semantic
|
|
884
|
+
]));
|
|
885
|
+
const mapping = new Map();
|
|
886
|
+
for (const item of Array.from(element.attributes)){
|
|
887
|
+
if (item.namespaceURI === XMLNS_NAMESPACE || 'xmlns' === item.name || item.name.startsWith('xmlns:')) continue;
|
|
888
|
+
const attributeName = xmlAttributeLocalName(item);
|
|
889
|
+
const semantic = semanticsByAttribute.get(attributeName);
|
|
890
|
+
const value = item.value.trim();
|
|
891
|
+
if (xmlAttributeNamespace(element, item) !== namespace || !semantic || mapping.has(semantic) || !WORD_COLOR_SCHEME_INDEXES.has(value)) return null;
|
|
892
|
+
mapping.set(semantic, value);
|
|
893
|
+
}
|
|
894
|
+
return mapping;
|
|
895
|
+
}
|
|
896
|
+
function mappedWordThemeColorKey(source, mapping) {
|
|
897
|
+
let current = source;
|
|
898
|
+
const visited = new Set();
|
|
899
|
+
for(let depth = 0; depth < MAX_COLOR_MAPPING_DEPTH; depth += 1){
|
|
900
|
+
if (visited.has(current)) break;
|
|
901
|
+
visited.add(current);
|
|
902
|
+
const next = mapping?.get(current)?.toLowerCase();
|
|
903
|
+
if (!next || next === current) return current;
|
|
904
|
+
current = next;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
function addThemeFontFamily(fonts, prefix, family) {
|
|
908
|
+
if (!family) return;
|
|
909
|
+
const latin = typeface(directChild(family, 'latin'));
|
|
910
|
+
const eastAsia = typeface(directChild(family, 'ea')) || scriptTypeface(family, [
|
|
911
|
+
'Hans',
|
|
912
|
+
'Hant',
|
|
913
|
+
'Jpan',
|
|
914
|
+
'Hang'
|
|
915
|
+
]) || latin;
|
|
916
|
+
const complex = typeface(directChild(family, 'cs')) || latin;
|
|
917
|
+
if (latin) {
|
|
918
|
+
fonts.set(`${prefix}ascii`, latin);
|
|
919
|
+
fonts.set(`${prefix}hansi`, latin);
|
|
920
|
+
}
|
|
921
|
+
if (eastAsia) fonts.set(`${prefix}eastasia`, eastAsia);
|
|
922
|
+
if (complex) fonts.set(`${prefix}bidi`, complex);
|
|
923
|
+
}
|
|
924
|
+
function scriptTypeface(family, scripts) {
|
|
925
|
+
const fonts = directChildren(family, 'font');
|
|
926
|
+
for (const script of scripts){
|
|
927
|
+
const font = fonts.find((candidate)=>drawingAttribute(candidate, "script") === script);
|
|
928
|
+
const value = font ? typeface(font) : void 0;
|
|
929
|
+
if (value) return value;
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
function typeface(element) {
|
|
933
|
+
const value = element ? drawingAttribute(element, 'typeface')?.trim() : void 0;
|
|
934
|
+
return value || void 0;
|
|
935
|
+
}
|
|
936
|
+
function themeColorValue(element) {
|
|
937
|
+
if (!element) return;
|
|
938
|
+
const value = 'sysClr' === element.localName ? drawingAttribute(element, 'lastClr') : drawingAttribute(element, 'val');
|
|
939
|
+
return value && /^[0-9a-f]{6}$/i.test(value) ? value.toLowerCase() : void 0;
|
|
940
|
+
}
|
|
941
|
+
function drawingAttribute(element, name) {
|
|
942
|
+
return work_ooxml_package_attribute(element, name) ?? work_ooxml_package_attribute(element, `a:${name}`);
|
|
943
|
+
}
|
|
944
|
+
function hexFactor(value) {
|
|
945
|
+
if (!value || !/^[0-9a-f]{2}$/i.test(value)) return;
|
|
946
|
+
return Number.parseInt(value, 16) / 255;
|
|
947
|
+
}
|
|
948
|
+
function isDocxThemeResolver(source) {
|
|
949
|
+
return Boolean(source && 'colors' in source && source.colors instanceof Map && 'fonts' in source && source.fonts instanceof Map);
|
|
950
|
+
}
|
|
951
|
+
function paragraphAlignment(element, docx) {
|
|
952
|
+
const alignment = element.style.textAlign;
|
|
953
|
+
if ('center' === alignment) return docx.AlignmentType.CENTER;
|
|
954
|
+
if ('right' === alignment || 'end' === alignment) return docx.AlignmentType.RIGHT;
|
|
955
|
+
if ('justify' === alignment) return docx.AlignmentType.JUSTIFIED;
|
|
956
|
+
if ('left' === alignment || 'start' === alignment) return docx.AlignmentType.LEFT;
|
|
957
|
+
}
|
|
958
|
+
function paragraphDirectionOptions(element) {
|
|
959
|
+
const bidirectional = paragraphBidirectional(element);
|
|
960
|
+
return void 0 === bidirectional ? {} : {
|
|
961
|
+
bidirectional
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
function paragraphBidirectional(element) {
|
|
965
|
+
const direction = domDirection(element);
|
|
966
|
+
return void 0 === direction ? void 0 : 'rtl' === direction;
|
|
967
|
+
}
|
|
968
|
+
function domDirection(element) {
|
|
969
|
+
let current = element;
|
|
970
|
+
while(current){
|
|
971
|
+
const attribute = current.getAttribute('dir')?.trim().toLowerCase();
|
|
972
|
+
if ('ltr' === attribute || 'rtl' === attribute) return attribute;
|
|
973
|
+
if ('auto' === attribute) break;
|
|
974
|
+
const css = current.style.direction.trim().toLowerCase();
|
|
975
|
+
if ('ltr' === css || 'rtl' === css) return css;
|
|
976
|
+
current = current.parentElement;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
function paragraphLineSpacing(element) {
|
|
980
|
+
const value = element.style.lineHeight.trim();
|
|
981
|
+
if (!value || 'normal' === value) return;
|
|
982
|
+
const percentage = /^(\d+(?:\.\d+)?)%$/.exec(value);
|
|
983
|
+
if (percentage) return Math.round(Number(percentage[1]) / 100 * 240);
|
|
984
|
+
const unitless = /^(\d+(?:\.\d+)?)$/.exec(value);
|
|
985
|
+
if (unitless) return Math.round(240 * Number(unitless[1]));
|
|
986
|
+
const points = cssLengthToPoints(value);
|
|
987
|
+
return points ? Math.round(20 * points) : void 0;
|
|
988
|
+
}
|
|
989
|
+
function paragraphSpacingOptions(element, heading, docx) {
|
|
990
|
+
const before = dataPointLengthToTwips(element.dataset.officeSpaceBefore) ?? cssSpacingLengthToTwips(element.style.marginTop);
|
|
991
|
+
const after = dataPointLengthToTwips(element.dataset.officeSpaceAfter) ?? cssSpacingLengthToTwips(element.style.marginBottom);
|
|
992
|
+
const lineRule = paragraphLineRule(element, docx);
|
|
993
|
+
return {
|
|
994
|
+
after: after ?? (heading ? 180 : 120),
|
|
995
|
+
...void 0 !== before ? {
|
|
996
|
+
before
|
|
997
|
+
} : {},
|
|
998
|
+
line: paragraphLineSpacing(element) ?? 320,
|
|
999
|
+
...lineRule ? {
|
|
1000
|
+
lineRule
|
|
1001
|
+
} : {}
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
function paragraphLineRule(element, docx) {
|
|
1005
|
+
if ('auto' === element.dataset.officeLineRule) return docx.LineRuleType.AUTO;
|
|
1006
|
+
if ('exact' === element.dataset.officeLineRule) return docx.LineRuleType.EXACT;
|
|
1007
|
+
if ('atLeast' === element.dataset.officeLineRule) return docx.LineRuleType.AT_LEAST;
|
|
1008
|
+
}
|
|
1009
|
+
function paragraphIndent(element, tag) {
|
|
1010
|
+
const blockquoteIndent = 'blockquote' === tag ? 540 : 0;
|
|
1011
|
+
const level = Number(element.dataset.officeIndentLevel);
|
|
1012
|
+
const explicitIndent = Number.isFinite(level) && level > 0 ? Math.round(360 * level) : cssLengthToTwips(element.style.marginLeft);
|
|
1013
|
+
const left = blockquoteIndent + explicitIndent;
|
|
1014
|
+
const right = dataPixelLengthToTwips(element.dataset.officeIndentRight) ?? cssLengthToTwips(element.style.marginRight);
|
|
1015
|
+
const firstLine = dataPixelLengthToTwips(element.dataset.officeIndentFirstLine, true) ?? cssSignedLengthToTwips(element.style.textIndent);
|
|
1016
|
+
if (!left && !right && !firstLine) return;
|
|
1017
|
+
return {
|
|
1018
|
+
...left > 0 ? {
|
|
1019
|
+
left
|
|
1020
|
+
} : {},
|
|
1021
|
+
...right > 0 ? {
|
|
1022
|
+
right
|
|
1023
|
+
} : {},
|
|
1024
|
+
...firstLine > 0 ? {
|
|
1025
|
+
firstLine
|
|
1026
|
+
} : {},
|
|
1027
|
+
...firstLine < 0 ? {
|
|
1028
|
+
hanging: Math.abs(firstLine)
|
|
1029
|
+
} : {}
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
function paragraphPaginationOptions(element) {
|
|
1033
|
+
const outlineLevel = Number(element.dataset.officeOutlineLevel);
|
|
1034
|
+
return {
|
|
1035
|
+
keepLines: dataBoolean(element.dataset.officeKeepLines),
|
|
1036
|
+
keepNext: dataBoolean(element.dataset.officeKeepWithNext),
|
|
1037
|
+
pageBreakBefore: dataBoolean(element.dataset.officePageBreakBefore),
|
|
1038
|
+
widowControl: dataBoolean(element.dataset.officeWidowControl),
|
|
1039
|
+
contextualSpacing: dataBoolean(element.dataset.officeContextualSpacing),
|
|
1040
|
+
...Number.isSafeInteger(outlineLevel) && outlineLevel >= 0 && outlineLevel <= 9 ? {
|
|
1041
|
+
outlineLevel
|
|
1042
|
+
} : {}
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function paragraphTabStops(element, docx) {
|
|
1046
|
+
const tabStops = normalizeDocumentTabStops(element.dataset.officeTabStops ?? '').map((tabStop)=>({
|
|
1047
|
+
type: docxTabStopType(tabStop.alignment, docx),
|
|
1048
|
+
position: Math.round(15 * tabStop.position),
|
|
1049
|
+
...'none' === tabStop.leader ? {} : {
|
|
1050
|
+
leader: docxTabLeader(tabStop.leader, docx)
|
|
1051
|
+
}
|
|
1052
|
+
}));
|
|
1053
|
+
return tabStops.length ? tabStops : void 0;
|
|
1054
|
+
}
|
|
1055
|
+
function docxTabStopType(alignment, docx) {
|
|
1056
|
+
if ('center' === alignment) return docx.TabStopType.CENTER;
|
|
1057
|
+
if ('right' === alignment) return docx.TabStopType.RIGHT;
|
|
1058
|
+
if ('decimal' === alignment) return docx.TabStopType.DECIMAL;
|
|
1059
|
+
return docx.TabStopType.LEFT;
|
|
1060
|
+
}
|
|
1061
|
+
function docxTabLeader(leader, docx) {
|
|
1062
|
+
if ('dot' === leader) return docx.LeaderType.DOT;
|
|
1063
|
+
if ('hyphen' === leader) return docx.LeaderType.HYPHEN;
|
|
1064
|
+
if ('underscore' === leader) return docx.LeaderType.UNDERSCORE;
|
|
1065
|
+
if ('middleDot' === leader) return docx.LeaderType.MIDDLE_DOT;
|
|
1066
|
+
return docx.LeaderType.NONE;
|
|
1067
|
+
}
|
|
1068
|
+
function dataBoolean(value) {
|
|
1069
|
+
if ('true' === value || '1' === value) return true;
|
|
1070
|
+
if ('false' === value || '0' === value) return false;
|
|
1071
|
+
}
|
|
1072
|
+
function cssFontFamily(value) {
|
|
1073
|
+
const family = value.split(',')[0]?.trim().replace(/^(['"])(.*)\1$/, '$2');
|
|
1074
|
+
return family || void 0;
|
|
1075
|
+
}
|
|
1076
|
+
function cssFontSize(value) {
|
|
1077
|
+
const points = cssLengthToPoints(value);
|
|
1078
|
+
return points ? Math.max(1, Math.round(2 * points)) : void 0;
|
|
1079
|
+
}
|
|
1080
|
+
function cssLengthToTwips(value) {
|
|
1081
|
+
const points = cssLengthToPoints(value);
|
|
1082
|
+
return points ? Math.max(0, Math.round(20 * points)) : 0;
|
|
1083
|
+
}
|
|
1084
|
+
function cssSignedLengthToTwips(value) {
|
|
1085
|
+
const match = /^(-?\d+(?:\.\d+)?)(px|pt|in|cm|mm)$/i.exec(value.trim());
|
|
1086
|
+
if (!match) return 0;
|
|
1087
|
+
const amount = Number(match[1]);
|
|
1088
|
+
if (!Number.isFinite(amount) || !amount) return 0;
|
|
1089
|
+
const unit = match[2]?.toLowerCase();
|
|
1090
|
+
const points = 'px' === unit ? 0.75 * amount : 'in' === unit ? 72 * amount : 'cm' === unit ? 72 * amount / 2.54 : 'mm' === unit ? 72 * amount / 25.4 : amount;
|
|
1091
|
+
return Math.round(20 * points);
|
|
1092
|
+
}
|
|
1093
|
+
function dataPixelLengthToTwips(value, signed = false) {
|
|
1094
|
+
if (void 0 === value) return;
|
|
1095
|
+
const pixels = Number(value);
|
|
1096
|
+
if (!Number.isFinite(pixels)) return;
|
|
1097
|
+
const twips = Math.round(15 * pixels);
|
|
1098
|
+
return signed ? twips : Math.max(0, twips);
|
|
1099
|
+
}
|
|
1100
|
+
function dataPointLengthToTwips(value) {
|
|
1101
|
+
if (void 0 === value) return;
|
|
1102
|
+
const points = Number(value);
|
|
1103
|
+
return Number.isFinite(points) && points >= 0 ? Math.round(20 * points) : void 0;
|
|
1104
|
+
}
|
|
1105
|
+
function cssSpacingLengthToTwips(value) {
|
|
1106
|
+
if (!value.trim()) return;
|
|
1107
|
+
if (/^0(?:\.0+)?(?:px|pt|in|cm|mm)?$/i.test(value.trim())) return 0;
|
|
1108
|
+
const points = cssLengthToPoints(value);
|
|
1109
|
+
return void 0 === points ? void 0 : Math.round(20 * points);
|
|
1110
|
+
}
|
|
1111
|
+
function cssLengthToPoints(value) {
|
|
1112
|
+
const match = /^(-?\d+(?:\.\d+)?)(px|pt|in|cm|mm)$/i.exec(value.trim());
|
|
1113
|
+
if (!match) return;
|
|
1114
|
+
const amount = Number(match[1]);
|
|
1115
|
+
if (!Number.isFinite(amount) || amount <= 0) return;
|
|
1116
|
+
const unit = match[2]?.toLowerCase();
|
|
1117
|
+
if ('px' === unit) return 0.75 * amount;
|
|
1118
|
+
if ('in' === unit) return 72 * amount;
|
|
1119
|
+
if ('cm' === unit) return 72 * amount / 2.54;
|
|
1120
|
+
if ('mm' === unit) return 72 * amount / 25.4;
|
|
1121
|
+
return amount;
|
|
1122
|
+
}
|
|
1123
|
+
function cssColorToHex(source) {
|
|
1124
|
+
const value = source.trim();
|
|
1125
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(value)?.[1];
|
|
1126
|
+
if (hex) return (3 === hex.length ? [
|
|
1127
|
+
...hex
|
|
1128
|
+
].map((character)=>character.repeat(2)).join('') : hex).toUpperCase();
|
|
1129
|
+
const rgb = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,[^)]*)?\)$/i.exec(value);
|
|
1130
|
+
if (!rgb) return;
|
|
1131
|
+
return rgb.slice(1, 4).map((channel)=>Math.min(255, Number(channel)).toString(16).padStart(2, '0')).join('').toUpperCase();
|
|
1132
|
+
}
|
|
1133
|
+
const WORD_2012_NAMESPACE = 'http://schemas.microsoft.com/office/word/2012/wordml';
|
|
1134
|
+
const work_docx_paragraph_default_collapsed_MARKUP_COMPATIBILITY_NAMESPACE = 'http://schemas.openxmlformats.org/markup-compatibility/2006';
|
|
1135
|
+
const MAX_PARAGRAPH_DEFAULT_COLLAPSED_PATCHES = 65536;
|
|
1136
|
+
const PARAGRAPH_PART_PATTERN = /^word\/(?:document|header\d*|footer\d*|footnotes|endnotes)\.xml$/i;
|
|
1137
|
+
class DocxParagraphDefaultCollapsedPatchCollector {
|
|
1138
|
+
patches = [];
|
|
1139
|
+
register(marker, element) {
|
|
1140
|
+
const value = dataBoolean(element.dataset.officeDefaultCollapsed);
|
|
1141
|
+
if (void 0 === value) return;
|
|
1142
|
+
if (this.patches.length >= MAX_PARAGRAPH_DEFAULT_COLLAPSED_PATCHES) throw new Error('Document exceeds the default-collapsed paragraph limit.');
|
|
1143
|
+
this.patches.push({
|
|
1144
|
+
marker,
|
|
1145
|
+
value
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
function parseDocxParagraphDefaultCollapsed(properties) {
|
|
1150
|
+
if ('pPr' !== properties.localName || !DOCX_WORDPROCESSING_NAMESPACES.has(properties.namespaceURI ?? '')) return {
|
|
1151
|
+
status: 'absent'
|
|
1152
|
+
};
|
|
1153
|
+
const collapsed = directChildren(properties, 'collapsed').filter((element)=>element.namespaceURI === WORD_2012_NAMESPACE);
|
|
1154
|
+
if (!collapsed.length) return {
|
|
1155
|
+
status: 'absent'
|
|
1156
|
+
};
|
|
1157
|
+
if (1 !== collapsed.length) return {
|
|
1158
|
+
status: 'invalid'
|
|
1159
|
+
};
|
|
1160
|
+
const element = collapsed[0];
|
|
1161
|
+
if (directChildren(element).length || hasNonWhitespaceXmlText(element)) return {
|
|
1162
|
+
status: 'invalid'
|
|
1163
|
+
};
|
|
1164
|
+
const attributes = Array.from(element.attributes).filter((item)=>xmlAttributeNamespace(element, item) !== XMLNS_NAMESPACE);
|
|
1165
|
+
if (!attributes.length) return {
|
|
1166
|
+
status: 'valid',
|
|
1167
|
+
value: true
|
|
1168
|
+
};
|
|
1169
|
+
if (1 !== attributes.length) return {
|
|
1170
|
+
status: 'invalid'
|
|
1171
|
+
};
|
|
1172
|
+
const valueAttribute = attributes[0];
|
|
1173
|
+
if ('val' !== xmlAttributeLocalName(valueAttribute) || !DOCX_WORDPROCESSING_NAMESPACES.has(xmlAttributeNamespace(element, valueAttribute) ?? '')) return {
|
|
1174
|
+
status: 'invalid'
|
|
1175
|
+
};
|
|
1176
|
+
if ('true' === valueAttribute.value || 'on' === valueAttribute.value || '1' === valueAttribute.value) return {
|
|
1177
|
+
status: 'valid',
|
|
1178
|
+
value: true
|
|
1179
|
+
};
|
|
1180
|
+
if ('false' === valueAttribute.value || 'off' === valueAttribute.value || '0' === valueAttribute.value) return {
|
|
1181
|
+
status: 'valid',
|
|
1182
|
+
value: false
|
|
1183
|
+
};
|
|
1184
|
+
return {
|
|
1185
|
+
status: 'invalid'
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
async function patchDocxParagraphDefaultCollapsed(buffer, patches) {
|
|
1189
|
+
if (!patches.length) return buffer;
|
|
1190
|
+
if (patches.length > MAX_PARAGRAPH_DEFAULT_COLLAPSED_PATCHES) throw new Error('Document exceeds the default-collapsed paragraph limit.');
|
|
1191
|
+
const archive = await jszip.loadAsync(buffer);
|
|
1192
|
+
const byMarker = new Map(patches.map((patch)=>[
|
|
1193
|
+
patch.marker,
|
|
1194
|
+
patch
|
|
1195
|
+
]));
|
|
1196
|
+
const applied = new Set();
|
|
1197
|
+
const entries = Object.values(archive.files).filter((entry)=>!entry.dir && PARAGRAPH_PART_PATTERN.test(entry.name));
|
|
1198
|
+
for (const entry of entries){
|
|
1199
|
+
const document = parseXml(decodeXmlBytes(await entry.async('uint8array'), `generated DOCX ${entry.name}`), `generated DOCX ${entry.name}`);
|
|
1200
|
+
if (!isParagraphPartRoot(document.documentElement, entry.name)) continue;
|
|
1201
|
+
let changed = false;
|
|
1202
|
+
for (const paragraph of descendants(document, 'p').filter((element)=>DOCX_WORDPROCESSING_NAMESPACES.has(element.namespaceURI ?? ''))){
|
|
1203
|
+
const patch = paragraphDefaultCollapsedPatch(paragraph, byMarker);
|
|
1204
|
+
if (patch) {
|
|
1205
|
+
setParagraphDefaultCollapsed(document, paragraph, patch.value);
|
|
1206
|
+
applied.add(patch.marker);
|
|
1207
|
+
changed = true;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
if (changed) archive.file(entry.name, serializeUtf8Xml(document));
|
|
1211
|
+
}
|
|
1212
|
+
const missing = patches.map((patch)=>patch.marker).filter((marker)=>!applied.has(marker));
|
|
1213
|
+
if (missing.length) throw new Error(`DOCX default-collapsed paragraph markers were not emitted: ${missing.join(', ')}.`);
|
|
1214
|
+
return archive.generateAsync({
|
|
1215
|
+
type: 'arraybuffer'
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
function paragraphDefaultCollapsedPatch(paragraph, patches) {
|
|
1219
|
+
const matches = directChildren(paragraph, 'r').flatMap((run)=>{
|
|
1220
|
+
if (!DOCX_WORDPROCESSING_NAMESPACES.has(run.namespaceURI ?? '')) return [];
|
|
1221
|
+
const text = directChildren(run, 't').find((element)=>DOCX_WORDPROCESSING_NAMESPACES.has(element.namespaceURI ?? ''));
|
|
1222
|
+
const patch = text ? patches.get(text.textContent ?? '') : void 0;
|
|
1223
|
+
return text && patch && runHasOnlyMarkerText(run, text) ? [
|
|
1224
|
+
patch
|
|
1225
|
+
] : [];
|
|
1226
|
+
});
|
|
1227
|
+
return 1 === matches.length ? matches[0] : null;
|
|
1228
|
+
}
|
|
1229
|
+
function runHasOnlyMarkerText(run, markerText) {
|
|
1230
|
+
return directChildren(run).every((child)=>child === markerText || 'rPr' === child.localName && DOCX_WORDPROCESSING_NAMESPACES.has(child.namespaceURI ?? ''));
|
|
1231
|
+
}
|
|
1232
|
+
function setParagraphDefaultCollapsed(document, paragraph, value) {
|
|
1233
|
+
const wordNamespace = paragraph.namespaceURI ?? '';
|
|
1234
|
+
const root = document.documentElement;
|
|
1235
|
+
const wordPrefix = ensureNamespacePrefix(root, 'w', wordNamespace);
|
|
1236
|
+
const word2012Prefix = ensureNamespacePrefix(root, 'w15', WORD_2012_NAMESPACE);
|
|
1237
|
+
ensureIgnorableNamespace(root, word2012Prefix, WORD_2012_NAMESPACE);
|
|
1238
|
+
const propertyNodes = directChildren(paragraph, 'pPr').filter((element)=>element.namespaceURI === wordNamespace);
|
|
1239
|
+
if (propertyNodes.length > 1) throw new Error('Generated DOCX paragraph contains duplicate properties.');
|
|
1240
|
+
const properties = propertyNodes[0] ?? insertParagraphProperties(document, paragraph, wordPrefix, wordNamespace);
|
|
1241
|
+
for (const child of directChildren(properties, 'collapsed'))if (child.namespaceURI === WORD_2012_NAMESPACE) child.remove();
|
|
1242
|
+
const collapsed = document.createElementNS(WORD_2012_NAMESPACE, `${word2012Prefix}:collapsed`);
|
|
1243
|
+
collapsed.setAttributeNS(wordNamespace, `${wordPrefix}:val`, value ? '1' : '0');
|
|
1244
|
+
properties.append(collapsed);
|
|
1245
|
+
}
|
|
1246
|
+
function insertParagraphProperties(document, paragraph, wordPrefix, wordNamespace) {
|
|
1247
|
+
const properties = document.createElementNS(wordNamespace, `${wordPrefix}:pPr`);
|
|
1248
|
+
paragraph.insertBefore(properties, paragraph.firstChild);
|
|
1249
|
+
return properties;
|
|
1250
|
+
}
|
|
1251
|
+
function ensureIgnorableNamespace(root, prefix, namespace) {
|
|
1252
|
+
const attribute = Array.from(root.attributes).find((item)=>'Ignorable' === xmlAttributeLocalName(item) && xmlAttributeNamespace(root, item) === work_docx_paragraph_default_collapsed_MARKUP_COMPATIBILITY_NAMESPACE);
|
|
1253
|
+
const tokens = (attribute?.value ?? '').trim().split(/\s+/).filter(Boolean);
|
|
1254
|
+
if (!tokens.some((token)=>xmlNamespaceUri(root, token) === namespace)) tokens.push(prefix);
|
|
1255
|
+
const compatibilityPrefix = ensureNamespacePrefix(root, 'mc', work_docx_paragraph_default_collapsed_MARKUP_COMPATIBILITY_NAMESPACE);
|
|
1256
|
+
attribute?.ownerElement?.removeAttributeNode(attribute);
|
|
1257
|
+
root.setAttributeNS(work_docx_paragraph_default_collapsed_MARKUP_COMPATIBILITY_NAMESPACE, `${compatibilityPrefix}:Ignorable`, tokens.join(' '));
|
|
1258
|
+
}
|
|
1259
|
+
function ensureNamespacePrefix(root, preferred, namespace) {
|
|
1260
|
+
const existing = xmlDeclaredPrefix(root, namespace);
|
|
1261
|
+
if (existing) return existing;
|
|
1262
|
+
if (!xmlNamespaceUri(root, preferred)) {
|
|
1263
|
+
root.setAttributeNS(XMLNS_NAMESPACE, `xmlns:${preferred}`, namespace);
|
|
1264
|
+
return preferred;
|
|
1265
|
+
}
|
|
1266
|
+
let suffix = 1;
|
|
1267
|
+
let prefix = '';
|
|
1268
|
+
do {
|
|
1269
|
+
prefix = `a3s${suffix}`;
|
|
1270
|
+
suffix += 1;
|
|
1271
|
+
}while (xmlNamespaceUri(root, prefix))
|
|
1272
|
+
root.setAttributeNS(XMLNS_NAMESPACE, `xmlns:${prefix}`, namespace);
|
|
1273
|
+
return prefix;
|
|
1274
|
+
}
|
|
1275
|
+
function isParagraphPartRoot(root, path) {
|
|
1276
|
+
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';
|
|
1277
|
+
return root.localName === expected && DOCX_WORDPROCESSING_NAMESPACES.has(root.namespaceURI ?? '');
|
|
1278
|
+
}
|
|
1279
|
+
export { DOCX_COLOR_SCHEME_MAPPING_ATTRIBUTES, DOCX_WORDPROCESSING_NAMESPACES, DocxParagraphDefaultCollapsedPatchCollector, XMLNS_NAMESPACE, XML_NAMESPACE, assertXmlRoot, cloneXmlElement, createDocxThemeResolver, cssColorToHex, cssFontFamily, cssFontSize, dataBoolean, docxThemeColor, docxThemeFont, domDirection, hasNonWhitespaceXmlText, mergeDocxIgnorableExtensions, mergeDocxIgnorableExtensionsAtPairs, paragraphAlignment, paragraphBidirectional, paragraphDirectionOptions, paragraphIndent, paragraphPaginationOptions, paragraphSpacingOptions, paragraphTabStops, parseDocxColorSchemeMappingElement, parseDocxParagraphDefaultCollapsed, patchDocxBibliography, patchDocxParagraphDefaultCollapsed, readDocxBibliography, resolveDocxThemeResolver, xmlAttributeLocalName, xmlAttributeNamespace, xmlDeclaredPrefix, xmlNamespaceUri };
|