@portone/docx-editor 0.1.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/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +63 -0
- package/LICENSE +203 -0
- package/README.md +48 -0
- package/assets/editor.png +0 -0
- package/dist/DocxEditor.d.ts +123 -0
- package/dist/DocxEditor.js +348 -0
- package/dist/core.d.ts +36 -0
- package/dist/core.js +38 -0
- package/dist/docx/comments/constants.d.ts +8 -0
- package/dist/docx/comments/constants.js +20 -0
- package/dist/docx/comments/model.d.ts +37 -0
- package/dist/docx/comments/model.js +104 -0
- package/dist/docx/comments/reading.d.ts +36 -0
- package/dist/docx/comments/reading.js +149 -0
- package/dist/docx/comments/writing.d.ts +11 -0
- package/dist/docx/comments/writing.js +327 -0
- package/dist/docx/comments.d.ts +3 -0
- package/dist/docx/comments.js +4 -0
- package/dist/docx/container.d.ts +12 -0
- package/dist/docx/container.js +130 -0
- package/dist/docx/documentSettings.d.ts +4 -0
- package/dist/docx/documentSettings.js +14 -0
- package/dist/docx/exportDocx.d.ts +15 -0
- package/dist/docx/exportDocx.js +163 -0
- package/dist/docx/exportRefs.d.ts +16 -0
- package/dist/docx/exportRefs.js +10 -0
- package/dist/docx/formatting/direct.d.ts +30 -0
- package/dist/docx/formatting/direct.js +201 -0
- package/dist/docx/formatting/effectiveParagraph.d.ts +15 -0
- package/dist/docx/formatting/effectiveParagraph.js +81 -0
- package/dist/docx/formatting/styles.d.ts +67 -0
- package/dist/docx/formatting/styles.js +165 -0
- package/dist/docx/formatting/tabStops.d.ts +8 -0
- package/dist/docx/formatting/tabStops.js +65 -0
- package/dist/docx/formatting.d.ts +4 -0
- package/dist/docx/formatting.js +4 -0
- package/dist/docx/headersFooters.d.ts +36 -0
- package/dist/docx/headersFooters.js +218 -0
- package/dist/docx/hyperlink.d.ts +75 -0
- package/dist/docx/hyperlink.js +71 -0
- package/dist/docx/importDocx.d.ts +21 -0
- package/dist/docx/importDocx.js +263 -0
- package/dist/docx/importParagraph.d.ts +32 -0
- package/dist/docx/importParagraph.js +308 -0
- package/dist/docx/importTable.d.ts +9 -0
- package/dist/docx/importTable.js +275 -0
- package/dist/docx/media.d.ts +60 -0
- package/dist/docx/media.js +213 -0
- package/dist/docx/notes.d.ts +22 -0
- package/dist/docx/notes.js +71 -0
- package/dist/docx/pageGeometry.d.ts +75 -0
- package/dist/docx/pageGeometry.js +88 -0
- package/dist/docx/paraProps.d.ts +74 -0
- package/dist/docx/paraProps.js +167 -0
- package/dist/docx/propsXml.d.ts +49 -0
- package/dist/docx/propsXml.js +302 -0
- package/dist/docx/relationships.d.ts +42 -0
- package/dist/docx/relationships.js +94 -0
- package/dist/docx/runProps.d.ts +51 -0
- package/dist/docx/runProps.js +207 -0
- package/dist/docx/scan.d.ts +20 -0
- package/dist/docx/scan.js +110 -0
- package/dist/docx/sdt.d.ts +56 -0
- package/dist/docx/sdt.js +104 -0
- package/dist/docx/sdtProps.d.ts +18 -0
- package/dist/docx/sdtProps.js +11 -0
- package/dist/docx/serializeBlock.d.ts +4 -0
- package/dist/docx/serializeBlock.js +30 -0
- package/dist/docx/serializeParagraph.d.ts +15 -0
- package/dist/docx/serializeParagraph.js +208 -0
- package/dist/docx/serializeTable.d.ts +12 -0
- package/dist/docx/serializeTable.js +134 -0
- package/dist/docx/session.d.ts +81 -0
- package/dist/docx/session.js +79 -0
- package/dist/docx/tableFormatting/editing.d.ts +64 -0
- package/dist/docx/tableFormatting/editing.js +318 -0
- package/dist/docx/tableFormatting/reading.d.ts +96 -0
- package/dist/docx/tableFormatting/reading.js +221 -0
- package/dist/docx/tableFormatting.d.ts +2 -0
- package/dist/docx/tableFormatting.js +3 -0
- package/dist/docx/tableTemplate.d.ts +22 -0
- package/dist/docx/tableTemplate.js +111 -0
- package/dist/docx/theme.d.ts +40 -0
- package/dist/docx/theme.js +61 -0
- package/dist/docx/uniqueControls.d.ts +14 -0
- package/dist/docx/uniqueControls.js +62 -0
- package/dist/download.d.ts +57 -0
- package/dist/download.js +44 -0
- package/dist/editor/clipboard/images.d.ts +14 -0
- package/dist/editor/clipboard/images.js +158 -0
- package/dist/editor/clipboard/inlineFormatting.d.ts +21 -0
- package/dist/editor/clipboard/inlineFormatting.js +232 -0
- package/dist/editor/commands/breakCommands.d.ts +8 -0
- package/dist/editor/commands/breakCommands.js +19 -0
- package/dist/editor/commands/canRunCommand.d.ts +6 -0
- package/dist/editor/commands/canRunCommand.js +10 -0
- package/dist/editor/commands/commentCommands.d.ts +3 -0
- package/dist/editor/commands/commentCommands.js +4 -0
- package/dist/editor/commands/comments/editing.d.ts +21 -0
- package/dist/editor/commands/comments/editing.js +305 -0
- package/dist/editor/commands/comments/model.d.ts +31 -0
- package/dist/editor/commands/comments/model.js +0 -0
- package/dist/editor/commands/comments/reading.d.ts +8 -0
- package/dist/editor/commands/comments/reading.js +58 -0
- package/dist/editor/commands/formatting/editing.d.ts +32 -0
- package/dist/editor/commands/formatting/editing.js +138 -0
- package/dist/editor/commands/formatting/reading.d.ts +57 -0
- package/dist/editor/commands/formatting/reading.js +82 -0
- package/dist/editor/commands/formatting/shared.d.ts +32 -0
- package/dist/editor/commands/formatting/shared.js +53 -0
- package/dist/editor/commands/formattingCommands.d.ts +2 -0
- package/dist/editor/commands/formattingCommands.js +3 -0
- package/dist/editor/commands/historyCommands.d.ts +11 -0
- package/dist/editor/commands/historyCommands.js +16 -0
- package/dist/editor/commands/indentCommands.d.ts +15 -0
- package/dist/editor/commands/indentCommands.js +44 -0
- package/dist/editor/commands/index.d.ts +82 -0
- package/dist/editor/commands/index.js +171 -0
- package/dist/editor/commands/linkCommands.d.ts +56 -0
- package/dist/editor/commands/linkCommands.js +146 -0
- package/dist/editor/commands/listCommands.d.ts +58 -0
- package/dist/editor/commands/listCommands.js +140 -0
- package/dist/editor/commands/lockCommands.d.ts +47 -0
- package/dist/editor/commands/lockCommands.js +198 -0
- package/dist/editor/commands/noteQueries.d.ts +12 -0
- package/dist/editor/commands/noteQueries.js +29 -0
- package/dist/editor/commands/paragraphCommands.d.ts +59 -0
- package/dist/editor/commands/paragraphCommands.js +113 -0
- package/dist/editor/commands/spacingCommands.d.ts +23 -0
- package/dist/editor/commands/spacingCommands.js +48 -0
- package/dist/editor/commands/tabCommands.d.ts +5 -0
- package/dist/editor/commands/tabCommands.js +65 -0
- package/dist/editor/createEditor.d.ts +59 -0
- package/dist/editor/createEditor.js +160 -0
- package/dist/editor/documentStyles.d.ts +60 -0
- package/dist/editor/documentStyles.js +80 -0
- package/dist/editor/externalClipboard.d.ts +11 -0
- package/dist/editor/externalClipboard.js +323 -0
- package/dist/editor/imageFiles.d.ts +74 -0
- package/dist/editor/imageFiles.js +173 -0
- package/dist/editor/insertImage.d.ts +31 -0
- package/dist/editor/insertImage.js +35 -0
- package/dist/editor/insertTable.d.ts +18 -0
- package/dist/editor/insertTable.js +44 -0
- package/dist/editor/paragraphEdits.d.ts +42 -0
- package/dist/editor/paragraphEdits.js +59 -0
- package/dist/editor/plainText.d.ts +3 -0
- package/dist/editor/plainText.js +50 -0
- package/dist/editor/plugins/bookmarkProtection.d.ts +3 -0
- package/dist/editor/plugins/bookmarkProtection.js +28 -0
- package/dist/editor/plugins/columnResize.d.ts +27 -0
- package/dist/editor/plugins/columnResize.js +192 -0
- package/dist/editor/plugins/commentDecorations.d.ts +3 -0
- package/dist/editor/plugins/commentDecorations.js +23 -0
- package/dist/editor/plugins/commentReservations.d.ts +5 -0
- package/dist/editor/plugins/commentReservations.js +26 -0
- package/dist/editor/plugins/dropCaret.d.ts +10 -0
- package/dist/editor/plugins/dropCaret.js +11 -0
- package/dist/editor/plugins/imagePaste.d.ts +8 -0
- package/dist/editor/plugins/imagePaste.js +265 -0
- package/dist/editor/plugins/keymap.d.ts +5 -0
- package/dist/editor/plugins/keymap.js +94 -0
- package/dist/editor/plugins/linkPanel.d.ts +23 -0
- package/dist/editor/plugins/linkPanel.js +39 -0
- package/dist/editor/plugins/listInputRules.d.ts +14 -0
- package/dist/editor/plugins/listInputRules.js +36 -0
- package/dist/editor/plugins/lockedContent.d.ts +6 -0
- package/dist/editor/plugins/lockedContent.js +41 -0
- package/dist/editor/plugins/noteProtection.d.ts +3 -0
- package/dist/editor/plugins/noteProtection.js +25 -0
- package/dist/editor/plugins/numberingDecorations.d.ts +42 -0
- package/dist/editor/plugins/numberingDecorations.js +110 -0
- package/dist/editor/plugins/rowResize.d.ts +15 -0
- package/dist/editor/plugins/rowResize.js +196 -0
- package/dist/editor/plugins/styledParagraphs.d.ts +15 -0
- package/dist/editor/plugins/styledParagraphs.js +65 -0
- package/dist/editor/plugins/tabCaret.d.ts +3 -0
- package/dist/editor/plugins/tabCaret.js +126 -0
- package/dist/editor/plugins/tabDecorations.d.ts +11 -0
- package/dist/editor/plugins/tabDecorations.js +87 -0
- package/dist/editor/plugins/tabLayout.d.ts +16 -0
- package/dist/editor/plugins/tabLayout.js +328 -0
- package/dist/editor/plugins/tabPointer.d.ts +7 -0
- package/dist/editor/plugins/tabPointer.js +95 -0
- package/dist/editor/plugins/tableContextMenu.d.ts +21 -0
- package/dist/editor/plugins/tableContextMenu.js +88 -0
- package/dist/editor/plugins/textContextMenu.d.ts +15 -0
- package/dist/editor/plugins/textContextMenu.js +90 -0
- package/dist/editor/views/imageResize.d.ts +53 -0
- package/dist/editor/views/imageResize.js +167 -0
- package/dist/editor/views/runMarkView.d.ts +15 -0
- package/dist/editor/views/runMarkView.js +9 -0
- package/dist/editor/views/tableView.d.ts +24 -0
- package/dist/editor/views/tableView.js +32 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +38 -0
- package/dist/model/format.d.ts +240 -0
- package/dist/model/format.js +305 -0
- package/dist/model/tabStops.d.ts +17 -0
- package/dist/model/tabStops.js +22 -0
- package/dist/numbering/listTemplate.d.ts +29 -0
- package/dist/numbering/listTemplate.js +83 -0
- package/dist/numbering/markers.d.ts +24 -0
- package/dist/numbering/markers.js +93 -0
- package/dist/numbering/parseNumbering.d.ts +65 -0
- package/dist/numbering/parseNumbering.js +116 -0
- package/dist/numbering/writeNumbering.d.ts +13 -0
- package/dist/numbering/writeNumbering.js +49 -0
- package/dist/ooxml/errors.d.ts +39 -0
- package/dist/ooxml/errors.js +21 -0
- package/dist/ooxml/image.d.ts +88 -0
- package/dist/ooxml/image.js +106 -0
- package/dist/ooxml/tabStops.d.ts +3 -0
- package/dist/ooxml/tabStops.js +51 -0
- package/dist/ooxml/units.d.ts +37 -0
- package/dist/ooxml/units.js +116 -0
- package/dist/ooxml/xml.d.ts +36 -0
- package/dist/ooxml/xml.js +89 -0
- package/dist/page/PageGuides.d.ts +16 -0
- package/dist/page/PageGuides.js +114 -0
- package/dist/page/measureBlocks.d.ts +25 -0
- package/dist/page/measureBlocks.js +80 -0
- package/dist/page/pageDecorations.d.ts +36 -0
- package/dist/page/pageDecorations.js +211 -0
- package/dist/page/pageLayout.d.ts +176 -0
- package/dist/page/pageLayout.js +157 -0
- package/dist/page/tableMeasurements.d.ts +10 -0
- package/dist/page/tableMeasurements.js +115 -0
- package/dist/page/usePageLayout.d.ts +57 -0
- package/dist/page/usePageLayout.js +141 -0
- package/dist/schema/docxSchema.d.ts +9 -0
- package/dist/schema/docxSchema.js +863 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +9 -0
- package/dist/schema/locks.d.ts +112 -0
- package/dist/schema/locks.js +211 -0
- package/dist/schema/rendering.d.ts +6 -0
- package/dist/schema/rendering.js +46 -0
- package/dist/styles/classNames.d.ts +214 -0
- package/dist/styles/classNames.js +208 -0
- package/dist/styles/colors.d.ts +11 -0
- package/dist/styles/colors.js +14 -0
- package/dist/styles/fontSizes.d.ts +6 -0
- package/dist/styles/fontSizes.js +20 -0
- package/dist/styles/fontStack.d.ts +55 -0
- package/dist/styles/fontStack.js +262 -0
- package/dist/styles/fonts.d.ts +6 -0
- package/dist/styles/fonts.js +11 -0
- package/dist/styles/inlineStyle.d.ts +67 -0
- package/dist/styles/inlineStyle.js +212 -0
- package/dist/styles/lineSpacings.d.ts +11 -0
- package/dist/styles/lineSpacings.js +10 -0
- package/dist/styles.css +1650 -0
- package/dist/table/cellBorders.d.ts +12 -0
- package/dist/table/cellBorders.js +9 -0
- package/dist/table/cellFormatting.d.ts +57 -0
- package/dist/table/cellFormatting.js +173 -0
- package/dist/table/columns.d.ts +25 -0
- package/dist/table/columns.js +155 -0
- package/dist/table/commands.d.ts +19 -0
- package/dist/table/commands.js +63 -0
- package/dist/table/format.d.ts +35 -0
- package/dist/table/format.js +26 -0
- package/dist/table/gridBorders.d.ts +42 -0
- package/dist/table/gridBorders.js +192 -0
- package/dist/table/index.d.ts +16 -0
- package/dist/table/index.js +52 -0
- package/dist/table/merge.d.ts +36 -0
- package/dist/table/merge.js +125 -0
- package/dist/table/resize.d.ts +55 -0
- package/dist/table/resize.js +66 -0
- package/dist/table/rowResize.d.ts +14 -0
- package/dist/table/rowResize.js +44 -0
- package/dist/table/rows.d.ts +32 -0
- package/dist/table/rows.js +89 -0
- package/dist/table/widths.d.ts +69 -0
- package/dist/table/widths.js +97 -0
- package/dist/ui/AlignMenu.d.ts +15 -0
- package/dist/ui/AlignMenu.js +64 -0
- package/dist/ui/CellStyleGroup.d.ts +15 -0
- package/dist/ui/CellStyleGroup.js +232 -0
- package/dist/ui/ColorPicker.d.ts +13 -0
- package/dist/ui/ColorPicker.js +165 -0
- package/dist/ui/CommentsPanel.d.ts +15 -0
- package/dist/ui/CommentsPanel.js +348 -0
- package/dist/ui/FontFamilySelect.d.ts +14 -0
- package/dist/ui/FontFamilySelect.js +59 -0
- package/dist/ui/FontSizeSelect.d.ts +11 -0
- package/dist/ui/FontSizeSelect.js +49 -0
- package/dist/ui/InsertImageButton.d.ts +9 -0
- package/dist/ui/InsertImageButton.js +47 -0
- package/dist/ui/LinkCard.d.ts +12 -0
- package/dist/ui/LinkCard.js +127 -0
- package/dist/ui/LinkPanel.d.ts +9 -0
- package/dist/ui/LinkPanel.js +121 -0
- package/dist/ui/MenuChoice.d.ts +11 -0
- package/dist/ui/MenuChoice.js +32 -0
- package/dist/ui/NotesPanel.d.ts +6 -0
- package/dist/ui/NotesPanel.js +33 -0
- package/dist/ui/ParagraphStyleSelect.d.ts +11 -0
- package/dist/ui/ParagraphStyleSelect.js +62 -0
- package/dist/ui/Popover.d.ts +22 -0
- package/dist/ui/Popover.js +74 -0
- package/dist/ui/SpacingMenu.d.ts +12 -0
- package/dist/ui/SpacingMenu.js +54 -0
- package/dist/ui/TableMenu.d.ts +12 -0
- package/dist/ui/TableMenu.js +112 -0
- package/dist/ui/TableSizePicker.d.ts +9 -0
- package/dist/ui/TableSizePicker.js +53 -0
- package/dist/ui/TextMenu.d.ts +15 -0
- package/dist/ui/TextMenu.js +194 -0
- package/dist/ui/Toolbar.d.ts +19 -0
- package/dist/ui/Toolbar.js +448 -0
- package/dist/ui/ToolbarButton.d.ts +23 -0
- package/dist/ui/ToolbarButton.js +56 -0
- package/dist/ui/Tooltip.d.ts +19 -0
- package/dist/ui/Tooltip.js +47 -0
- package/dist/ui/ZoomSelect.d.ts +7 -0
- package/dist/ui/ZoomSelect.js +44 -0
- package/dist/ui/comments/CommentComposer.d.ts +17 -0
- package/dist/ui/comments/CommentComposer.js +59 -0
- package/dist/ui/comments/useCommentRailLayout.d.ts +18 -0
- package/dist/ui/comments/useCommentRailLayout.js +183 -0
- package/dist/ui/keyboardWalk.d.ts +22 -0
- package/dist/ui/keyboardWalk.js +70 -0
- package/dist/ui/openAddress.d.ts +11 -0
- package/dist/ui/openAddress.js +16 -0
- package/dist/ui/panelPlacement.d.ts +51 -0
- package/dist/ui/panelPlacement.js +71 -0
- package/dist/ui/presets.d.ts +11 -0
- package/dist/ui/presets.js +0 -0
- package/dist/ui/rovingFocus.d.ts +20 -0
- package/dist/ui/rovingFocus.js +61 -0
- package/dist/ui/runCommand.d.ts +5 -0
- package/dist/ui/runCommand.js +13 -0
- package/dist/ui/useDismiss.d.ts +6 -0
- package/dist/ui/useDismiss.js +31 -0
- package/dist/ui/useFitWidthZoom.d.ts +2 -0
- package/dist/ui/useFitWidthZoom.js +26 -0
- package/dist/ui/useGridKeyboard.d.ts +12 -0
- package/dist/ui/useGridKeyboard.js +90 -0
- package/dist/ui/useMenuKeyboard.d.ts +12 -0
- package/dist/ui/useMenuKeyboard.js +56 -0
- package/dist/ui/zoom.d.ts +4 -0
- package/dist/ui/zoom.js +21 -0
- package/package.json +120 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the span each individual body block occupies in the raw document.xml text and slices it out.
|
|
3
|
+
*
|
|
4
|
+
* A block that was not edited is exported by writing this fragment back out untouched,
|
|
5
|
+
* which keeps its original bytes intact.
|
|
6
|
+
*/
|
|
7
|
+
export interface BodyBlockSlice {
|
|
8
|
+
/** The tag name exactly as written (e.g. "w:p", "w:tbl", "w:sectPr") */
|
|
9
|
+
name: string;
|
|
10
|
+
/** The original XML fragment. It also carries the whitespace that sat between this block and the one before it */
|
|
11
|
+
xml: string;
|
|
12
|
+
}
|
|
13
|
+
export interface BodyScan {
|
|
14
|
+
/** Everything in the raw text before the first block */
|
|
15
|
+
prefix: string;
|
|
16
|
+
blocks: BodyBlockSlice[];
|
|
17
|
+
/** Everything in the raw text after the last block */
|
|
18
|
+
suffix: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function scanBody(source: string): BodyScan;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/docx/scan.ts
|
|
2
|
+
import { DocxImportError } from "../ooxml/errors.js";
|
|
3
|
+
import { localPart } from "../ooxml/xml.js";
|
|
4
|
+
function skipPast(source, from, marker) {
|
|
5
|
+
const at = source.indexOf(marker, from);
|
|
6
|
+
if (at === -1) {
|
|
7
|
+
throw new DocxImportError("malformed-xml", `no ${marker} to close a tag`);
|
|
8
|
+
}
|
|
9
|
+
return at + marker.length;
|
|
10
|
+
}
|
|
11
|
+
function skipNonTag(source, lt) {
|
|
12
|
+
if (source.startsWith("<?", lt)) return skipPast(source, lt, "?>");
|
|
13
|
+
if (source.startsWith("<!--", lt)) return skipPast(source, lt, "-->");
|
|
14
|
+
if (source.startsWith("<![CDATA[", lt)) return skipPast(source, lt, "]]>");
|
|
15
|
+
if (source.startsWith("<!", lt)) return skipPast(source, lt, ">");
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
function readOpenTag(source, lt) {
|
|
19
|
+
let i = lt + 1;
|
|
20
|
+
const nameStart = i;
|
|
21
|
+
while (i < source.length && !" \r\n/>".includes(source[i])) i += 1;
|
|
22
|
+
const name = source.slice(nameStart, i);
|
|
23
|
+
if (!name)
|
|
24
|
+
throw new DocxImportError("malformed-xml", "a tag has an empty name");
|
|
25
|
+
let quote = null;
|
|
26
|
+
while (i < source.length) {
|
|
27
|
+
const ch = source[i];
|
|
28
|
+
if (quote !== null) {
|
|
29
|
+
if (ch === quote) quote = null;
|
|
30
|
+
} else if (ch === '"' || ch === "'") {
|
|
31
|
+
quote = ch;
|
|
32
|
+
} else if (ch === ">") {
|
|
33
|
+
return { name, end: i + 1, selfClosing: source[i - 1] === "/" };
|
|
34
|
+
}
|
|
35
|
+
i += 1;
|
|
36
|
+
}
|
|
37
|
+
throw new DocxImportError("malformed-xml", "a tag is left unclosed");
|
|
38
|
+
}
|
|
39
|
+
function readCloseTag(source, lt) {
|
|
40
|
+
const gt = source.indexOf(">", lt);
|
|
41
|
+
if (gt === -1)
|
|
42
|
+
throw new DocxImportError("malformed-xml", "a tag is left unclosed");
|
|
43
|
+
return { name: source.slice(lt + 2, gt).trim(), end: gt + 1 };
|
|
44
|
+
}
|
|
45
|
+
function isBodyTag(tag, depth) {
|
|
46
|
+
return localPart(tag.name) === "body" && depth === 1 && !tag.selfClosing;
|
|
47
|
+
}
|
|
48
|
+
function scanBody(source) {
|
|
49
|
+
const stack = [];
|
|
50
|
+
const blocks = [];
|
|
51
|
+
let bodyDepth = null;
|
|
52
|
+
let contentStart = -1;
|
|
53
|
+
let sliceStart = -1;
|
|
54
|
+
const atBodyLevel = () => bodyDepth !== null && stack.length === bodyDepth;
|
|
55
|
+
const takeBlock = (name, end) => {
|
|
56
|
+
blocks.push({ name, xml: source.slice(sliceStart, end) });
|
|
57
|
+
sliceStart = end;
|
|
58
|
+
};
|
|
59
|
+
let i = 0;
|
|
60
|
+
while (i < source.length) {
|
|
61
|
+
const lt = source.indexOf("<", i);
|
|
62
|
+
if (lt === -1) break;
|
|
63
|
+
const skipped = skipNonTag(source, lt);
|
|
64
|
+
if (skipped !== null) {
|
|
65
|
+
i = skipped;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (source.startsWith("</", lt)) {
|
|
69
|
+
const tag2 = readCloseTag(source, lt);
|
|
70
|
+
const opened = stack.pop();
|
|
71
|
+
if (opened !== tag2.name) {
|
|
72
|
+
throw new DocxImportError(
|
|
73
|
+
"malformed-xml",
|
|
74
|
+
`mismatched tags: ${opened} vs ${tag2.name}`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
if (atBodyLevel()) takeBlock(tag2.name, tag2.end);
|
|
78
|
+
i = tag2.end;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const tag = readOpenTag(source, lt);
|
|
82
|
+
if (bodyDepth === null && isBodyTag(tag, stack.length)) {
|
|
83
|
+
stack.push(tag.name);
|
|
84
|
+
bodyDepth = stack.length;
|
|
85
|
+
contentStart = tag.end;
|
|
86
|
+
sliceStart = tag.end;
|
|
87
|
+
} else {
|
|
88
|
+
if (atBodyLevel() && tag.selfClosing) takeBlock(tag.name, tag.end);
|
|
89
|
+
if (!tag.selfClosing) stack.push(tag.name);
|
|
90
|
+
}
|
|
91
|
+
i = tag.end;
|
|
92
|
+
}
|
|
93
|
+
if (bodyDepth === null) {
|
|
94
|
+
throw new DocxImportError("missing-body", "document has no w:body");
|
|
95
|
+
}
|
|
96
|
+
if (stack.length > 0) {
|
|
97
|
+
throw new DocxImportError(
|
|
98
|
+
"malformed-xml",
|
|
99
|
+
`elements are left unclosed: ${stack.join(", ")}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
prefix: source.slice(0, contentStart),
|
|
104
|
+
blocks,
|
|
105
|
+
suffix: source.slice(sliceStart)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export {
|
|
109
|
+
scanBody
|
|
110
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading the `w:sdt` content controls a document wraps its settled parts in, and naming a
|
|
3
|
+
* control that has to go out more than once.
|
|
4
|
+
*
|
|
5
|
+
* Only the shape we can write back out unchanged is taken apart: the `w:` prefix on both tags
|
|
6
|
+
* we write ourselves, a `w:sdtPr`, and a `w:sdtContent` that carries no attribute of its own
|
|
7
|
+
* and stands last. Everything else is null, which leaves whatever held the control preserved
|
|
8
|
+
* together with its original fragment.
|
|
9
|
+
*
|
|
10
|
+
* What sits inside the control is the caller's business. A whole cell (`docx/importTable`) and
|
|
11
|
+
* a stretch of runs inside a paragraph (`docx/importParagraph`) each read the content their
|
|
12
|
+
* own way, and the wrapper they put back on export is the same string in both cases.
|
|
13
|
+
*/
|
|
14
|
+
/** The opening of a content control taken apart, and the content it wraps */
|
|
15
|
+
export interface SdtWrapper {
|
|
16
|
+
/** The `<w:sdt>` opening tag followed by everything that stood ahead of `<w:sdtContent>` */
|
|
17
|
+
prefix: string;
|
|
18
|
+
content: Element;
|
|
19
|
+
/** Whether the control says its contents may not be edited */
|
|
20
|
+
contentsLocked: boolean;
|
|
21
|
+
/** Whether the control says it may not be deleted, not even whole */
|
|
22
|
+
deletionLocked: boolean;
|
|
23
|
+
}
|
|
24
|
+
/** Takes a `w:sdt` apart into the wrapper to put back on export and the content to read. null for a shape we do not write ourselves */
|
|
25
|
+
export declare function readSdtWrapper(el: Element): SdtWrapper | null;
|
|
26
|
+
/**
|
|
27
|
+
* The number Word writes on every control. Nothing reads it back and it only has to differ
|
|
28
|
+
* from the other controls in the document, so a draw out of the whole 32 bit range is enough.
|
|
29
|
+
*/
|
|
30
|
+
export declare function newControlId(): number;
|
|
31
|
+
/** What text one child of the control's `w:sdtPr` is to be changed to. A null xml removes that child */
|
|
32
|
+
export type SdtPrEdit = readonly [name: string, xml: string | null];
|
|
33
|
+
/**
|
|
34
|
+
* Whether the control says nothing about itself beyond its id and its lock: no alias or tag, no
|
|
35
|
+
* `w:dataBinding`, no type of its own.
|
|
36
|
+
* A lock may take such a control over a wider stretch, since the stretch is all it ever meant. One
|
|
37
|
+
* Word named keeps the stretch it was given, or the name would come to cover other text.
|
|
38
|
+
*/
|
|
39
|
+
export declare function namesNothing(prefix: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* The opening of a control with a few children of its `w:sdtPr` swapped out and everything else
|
|
42
|
+
* it carries left exactly as it came.
|
|
43
|
+
* null for a prefix whose shape cannot be made out, which leaves the caller to back out.
|
|
44
|
+
*/
|
|
45
|
+
export declare function editSdtPrefix(prefix: string, edits: readonly SdtPrEdit[]): string | null;
|
|
46
|
+
/**
|
|
47
|
+
* The opening of a control that is going out for the second time, under a name of its own.
|
|
48
|
+
*
|
|
49
|
+
* Two controls sharing an id are merely untidy, but two sharing a `w:dataBinding` are a
|
|
50
|
+
* document Word keeps in step by itself: typing in the one would change the other. So the
|
|
51
|
+
* binding stays with the first copy alone.
|
|
52
|
+
*
|
|
53
|
+
* A prefix whose shape cannot be made out is handed back untouched, since writing the copy
|
|
54
|
+
* as it came is still better than refusing to export at all.
|
|
55
|
+
*/
|
|
56
|
+
export declare function copiedControlPrefix(prefix: string, id: number): string;
|
package/dist/docx/sdt.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// src/docx/sdt.ts
|
|
2
|
+
import { wAttr } from "../ooxml/units.js";
|
|
3
|
+
import { attrString, elementChildren, serializeXml } from "../ooxml/xml.js";
|
|
4
|
+
import {
|
|
5
|
+
parseProps,
|
|
6
|
+
propsChild,
|
|
7
|
+
renderProps,
|
|
8
|
+
setPropsChild
|
|
9
|
+
} from "./propsXml.js";
|
|
10
|
+
var CONTENTS_LOCKED = ["contentLocked", "sdtContentLocked"];
|
|
11
|
+
var DELETION_LOCKED = ["sdtLocked", "sdtContentLocked"];
|
|
12
|
+
function lockValue(sdtPr) {
|
|
13
|
+
const lock = elementChildren(sdtPr).find(
|
|
14
|
+
(child) => child.localName === "lock"
|
|
15
|
+
);
|
|
16
|
+
return lock ? wAttr(lock, "val") : null;
|
|
17
|
+
}
|
|
18
|
+
function readSdtWrapper(el) {
|
|
19
|
+
if (el.nodeName !== "w:sdt") return null;
|
|
20
|
+
const children = elementChildren(el);
|
|
21
|
+
const contentAt = children.findIndex(
|
|
22
|
+
(child) => child.localName === "sdtContent"
|
|
23
|
+
);
|
|
24
|
+
const content = contentAt === -1 ? null : children[contentAt];
|
|
25
|
+
if (!content || content.nodeName !== "w:sdtContent") return null;
|
|
26
|
+
if (content.attributes.length > 0) return null;
|
|
27
|
+
if (contentAt !== children.length - 1) return null;
|
|
28
|
+
const head = children.slice(0, contentAt);
|
|
29
|
+
const sdtPr = head.find((child) => child.localName === "sdtPr");
|
|
30
|
+
if (!sdtPr) return null;
|
|
31
|
+
const attrs = attrString(el);
|
|
32
|
+
const val = lockValue(sdtPr);
|
|
33
|
+
return {
|
|
34
|
+
prefix: (attrs ? `<w:sdt ${attrs}>` : "<w:sdt>") + head.map(serializeXml).join(""),
|
|
35
|
+
content,
|
|
36
|
+
contentsLocked: val !== null && CONTENTS_LOCKED.includes(val),
|
|
37
|
+
deletionLocked: val !== null && DELETION_LOCKED.includes(val)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var SDT_ORDER = ["sdtPr", "sdtEndPr", "sdtContent"];
|
|
41
|
+
var SDT_PR_ORDER = [
|
|
42
|
+
"rPr",
|
|
43
|
+
"alias",
|
|
44
|
+
"tag",
|
|
45
|
+
"id",
|
|
46
|
+
"lock",
|
|
47
|
+
"placeholder",
|
|
48
|
+
"showingPlcHdr",
|
|
49
|
+
"dataBinding",
|
|
50
|
+
"temporary"
|
|
51
|
+
];
|
|
52
|
+
function newControlId() {
|
|
53
|
+
return Math.floor(Math.random() * 2147483647);
|
|
54
|
+
}
|
|
55
|
+
function renderPrefix(props) {
|
|
56
|
+
const open = props.attrs ? `<${props.tag} ${props.attrs}>` : `<${props.tag}>`;
|
|
57
|
+
return open + props.children.map((child) => child.xml).join("");
|
|
58
|
+
}
|
|
59
|
+
var NAMES_NOTHING = ["id", "lock"];
|
|
60
|
+
function namesNothing(prefix) {
|
|
61
|
+
const sdt = parseProps(`${prefix}</w:sdt>`);
|
|
62
|
+
if (!sdt || sdt.attrs !== null) return false;
|
|
63
|
+
const sdtPr = propsChild(sdt.children, "sdtPr");
|
|
64
|
+
const props = sdtPr ? parseProps(sdtPr.xml) : null;
|
|
65
|
+
if (!props || sdt.children.length !== 1) return false;
|
|
66
|
+
return props.children.every((child) => NAMES_NOTHING.includes(child.name));
|
|
67
|
+
}
|
|
68
|
+
function emptyProps(props) {
|
|
69
|
+
return props.attrs ? `<${props.tag} ${props.attrs}/>` : `<${props.tag}/>`;
|
|
70
|
+
}
|
|
71
|
+
function editSdtPrefix(prefix, edits) {
|
|
72
|
+
const sdt = parseProps(`${prefix}</w:sdt>`);
|
|
73
|
+
const sdtPr = sdt && propsChild(sdt.children, "sdtPr");
|
|
74
|
+
const props = sdtPr ? parseProps(sdtPr.xml) : null;
|
|
75
|
+
if (!sdt || !props) return null;
|
|
76
|
+
const children = edits.reduce(
|
|
77
|
+
(kept, [name, xml]) => setPropsChild(kept, name, xml, SDT_PR_ORDER),
|
|
78
|
+
props.children
|
|
79
|
+
);
|
|
80
|
+
const rendered = renderProps({ ...props, children });
|
|
81
|
+
return renderPrefix({
|
|
82
|
+
...sdt,
|
|
83
|
+
children: setPropsChild(
|
|
84
|
+
sdt.children,
|
|
85
|
+
"sdtPr",
|
|
86
|
+
rendered === "" ? emptyProps(props) : rendered,
|
|
87
|
+
SDT_ORDER
|
|
88
|
+
)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function copiedControlPrefix(prefix, id) {
|
|
92
|
+
const copied = editSdtPrefix(prefix, [
|
|
93
|
+
["id", `<w:id w:val="${id}"/>`],
|
|
94
|
+
["dataBinding", null]
|
|
95
|
+
]);
|
|
96
|
+
return copied ?? prefix;
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
copiedControlPrefix,
|
|
100
|
+
editSdtPrefix,
|
|
101
|
+
namesNothing,
|
|
102
|
+
newControlId,
|
|
103
|
+
readSdtWrapper
|
|
104
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning the lock of a content control on and off inside the opening XML it goes back out as.
|
|
3
|
+
*
|
|
4
|
+
* Nothing but the `w:lock` child of the `w:sdtPr` moves. The id Word gave the control, the alias
|
|
5
|
+
* and tag it goes by, and the `w:dataBinding` that ties it to the file's own XML all stay, which
|
|
6
|
+
* is what tells lifting a lock apart from throwing the control away.
|
|
7
|
+
*
|
|
8
|
+
* One child carries both clauses of the lock, so the two move together here: shutting writes the
|
|
9
|
+
* value that shuts both, and lifting takes the child away, which leaves the control with neither.
|
|
10
|
+
* A control Word gave nothing but a guard against deletion (`sdtLocked`) is therefore shut by
|
|
11
|
+
* being given the stricter value, and lifting that lock afterwards opens both clauses.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The opening of the control with its lock shut or lifted.
|
|
15
|
+
* null for a prefix whose shape cannot be made out, which leaves the caller to decide what to do
|
|
16
|
+
* with a control it cannot rewrite.
|
|
17
|
+
*/
|
|
18
|
+
export declare function withContentLock(sdtPrefix: string, locked: boolean): string | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/docx/sdtProps.ts
|
|
2
|
+
import { editSdtPrefix } from "./sdt.js";
|
|
3
|
+
var LOCK_BOTH_CLAUSES = '<w:lock w:val="sdtContentLocked"/>';
|
|
4
|
+
function withContentLock(sdtPrefix, locked) {
|
|
5
|
+
return editSdtPrefix(sdtPrefix, [
|
|
6
|
+
["lock", locked ? LOCK_BOTH_CLAUSES : null]
|
|
7
|
+
]);
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
withContentLock
|
|
11
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/docx/serializeBlock.ts
|
|
2
|
+
import { DocxExportError } from "../ooxml/errors.js";
|
|
3
|
+
import { NO_EXPORT_REFS } from "./exportRefs.js";
|
|
4
|
+
import { preservedXml, serializeParagraph } from "./serializeParagraph.js";
|
|
5
|
+
import { serializeTable } from "./serializeTable.js";
|
|
6
|
+
import { originalBlock } from "./session.js";
|
|
7
|
+
function serializeRaw(node, session) {
|
|
8
|
+
const imported = originalBlock(node, session);
|
|
9
|
+
if (!imported) {
|
|
10
|
+
throw new DocxExportError(
|
|
11
|
+
"lost-original",
|
|
12
|
+
"a preserved block has lost its original XML"
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return imported.xml;
|
|
16
|
+
}
|
|
17
|
+
function serializeBlock(node, session, refs = NO_EXPORT_REFS) {
|
|
18
|
+
if (node.type.name === "paragraph") return serializeParagraph(node, refs);
|
|
19
|
+
if (node.type.name === "table") return serializeTable(node, refs);
|
|
20
|
+
if (node.type.name === "rawBlock") return preservedXml(node);
|
|
21
|
+
if (node.type.name === "bookmarkBlock") return serializeRaw(node, session);
|
|
22
|
+
if (node.type.name === "docxRaw") return serializeRaw(node, session);
|
|
23
|
+
throw new DocxExportError(
|
|
24
|
+
"unsupported-content",
|
|
25
|
+
`block we cannot serialize: ${node.type.name}`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
serializeBlock
|
|
30
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuilds an edited paragraph back into OOXML.
|
|
3
|
+
*
|
|
4
|
+
* Body paragraphs and paragraphs inside table cells take the same path.
|
|
5
|
+
*
|
|
6
|
+
* The inlines are grouped three times over: neighbours that share their formatting become one run,
|
|
7
|
+
* the runs that share a hyperlink (`w:hyperlink`) go back inside it, and the links and runs that
|
|
8
|
+
* share a content control (`w:sdt`) go back inside the wrapper that mark carries. The control is
|
|
9
|
+
* the outer of the two wrappers, which is the nesting the mark order records (`schema`).
|
|
10
|
+
*/
|
|
11
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
12
|
+
import { type ExportRefs } from "./exportRefs";
|
|
13
|
+
export declare function openTag(name: string, attrs: unknown): string;
|
|
14
|
+
export declare function preservedXml(node: PMNode): string;
|
|
15
|
+
export declare function serializeParagraph(node: PMNode, refs?: ExportRefs): string;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// src/docx/serializeParagraph.ts
|
|
2
|
+
import { DocxExportError } from "../ooxml/errors.js";
|
|
3
|
+
import {
|
|
4
|
+
imageDrawingXml,
|
|
5
|
+
toImageExtent,
|
|
6
|
+
toImageSrc,
|
|
7
|
+
withExtent
|
|
8
|
+
} from "../ooxml/image.js";
|
|
9
|
+
import { escapeXml } from "../ooxml/xml.js";
|
|
10
|
+
import { NO_EXPORT_REFS } from "./exportRefs.js";
|
|
11
|
+
import { relIdIn, withRelId } from "./hyperlink.js";
|
|
12
|
+
function sameMark(a, b) {
|
|
13
|
+
if (a === null || b === null) return a === b;
|
|
14
|
+
return a.eq(b);
|
|
15
|
+
}
|
|
16
|
+
function markOf(node, name) {
|
|
17
|
+
return node.marks.find((mark) => mark.type.name === name) ?? null;
|
|
18
|
+
}
|
|
19
|
+
function openTag(name, attrs) {
|
|
20
|
+
return typeof attrs === "string" ? `<${name} ${attrs}>` : `<${name}>`;
|
|
21
|
+
}
|
|
22
|
+
function emptyTag(name, attrs) {
|
|
23
|
+
return typeof attrs === "string" ? `<${name} ${attrs}/>` : `<${name}/>`;
|
|
24
|
+
}
|
|
25
|
+
function renderImage(node, images) {
|
|
26
|
+
const extent = toImageExtent(node.attrs.extent);
|
|
27
|
+
const xml = node.attrs.xml;
|
|
28
|
+
if (typeof xml === "string") return extent ? withExtent(xml, extent) : xml;
|
|
29
|
+
const src = toImageSrc(node.attrs.src);
|
|
30
|
+
if (!src || !extent) {
|
|
31
|
+
throw new DocxExportError(
|
|
32
|
+
"lost-original",
|
|
33
|
+
"an image carries neither its original XML nor a size and bytes to rebuild it from"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const relId = images.relIdOf(src);
|
|
37
|
+
if (relId === void 0) {
|
|
38
|
+
throw new DocxExportError(
|
|
39
|
+
"unsupported-content",
|
|
40
|
+
"an inserted image has no media relationship; export it through exportDocx"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return imageDrawingXml({
|
|
44
|
+
relId,
|
|
45
|
+
docPrId: images.takeDocPrId(),
|
|
46
|
+
extent,
|
|
47
|
+
alt: typeof node.attrs.alt === "string" ? node.attrs.alt : null
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function renderInline(node, images) {
|
|
51
|
+
if (node.isText) {
|
|
52
|
+
const tab = markOf(node, "tab");
|
|
53
|
+
return (node.text ?? "").split(" ").flatMap((text, index, pieces) => {
|
|
54
|
+
const rendered = text ? [`<w:t xml:space="preserve">${escapeXml(text)}</w:t>`] : [];
|
|
55
|
+
return index < pieces.length - 1 ? [...rendered, emptyTag("w:tab", tab?.attrs.tabAttrs)] : rendered;
|
|
56
|
+
}).join("");
|
|
57
|
+
}
|
|
58
|
+
if (node.type.name === "hardBreak")
|
|
59
|
+
return emptyTag("w:br", node.attrs.brAttrs);
|
|
60
|
+
if (node.type.name === "image") return renderImage(node, images);
|
|
61
|
+
if (node.type.name === "commentReference") {
|
|
62
|
+
const original = node.attrs.referenceXml;
|
|
63
|
+
if (typeof original === "string") return original;
|
|
64
|
+
const id = node.attrs.id;
|
|
65
|
+
if (typeof id === "string") {
|
|
66
|
+
return `<w:commentReference w:id="${escapeXml(id)}"/>`;
|
|
67
|
+
}
|
|
68
|
+
throw new DocxExportError(
|
|
69
|
+
"lost-original",
|
|
70
|
+
"a comment reference has no id to write"
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (node.type.name === "noteReference") {
|
|
74
|
+
const original = node.attrs.referenceXml;
|
|
75
|
+
if (typeof original === "string") return original;
|
|
76
|
+
const id = node.attrs.id;
|
|
77
|
+
const name = node.attrs.kind === "endnote" ? "endnoteReference" : "footnoteReference";
|
|
78
|
+
if (typeof id === "string") {
|
|
79
|
+
return `<w:${name} w:id="${escapeXml(id)}"/>`;
|
|
80
|
+
}
|
|
81
|
+
throw new DocxExportError(
|
|
82
|
+
"lost-original",
|
|
83
|
+
"a note reference has no id to write"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
throw new DocxExportError(
|
|
87
|
+
"unsupported-content",
|
|
88
|
+
`inline node we cannot serialize: ${node.type.name}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
function preservedXml(node) {
|
|
92
|
+
const xml = node.attrs.xml;
|
|
93
|
+
if (typeof xml !== "string") {
|
|
94
|
+
throw new DocxExportError(
|
|
95
|
+
"lost-original",
|
|
96
|
+
"a preserved element has lost its original XML"
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return xml;
|
|
100
|
+
}
|
|
101
|
+
function addInline(parts, child, images) {
|
|
102
|
+
if (child.type.name === "rawInline") {
|
|
103
|
+
parts.push({ kind: "raw", xml: preservedXml(child) });
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (child.type.name === "commentStart" || child.type.name === "commentEnd") {
|
|
107
|
+
const original = child.attrs.xml;
|
|
108
|
+
if (typeof original === "string") {
|
|
109
|
+
parts.push({ kind: "raw", xml: original });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const id = child.attrs.id;
|
|
113
|
+
if (typeof id !== "string") {
|
|
114
|
+
throw new DocxExportError(
|
|
115
|
+
"lost-original",
|
|
116
|
+
"a comment range marker has no id to write"
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const name = child.type.name === "commentStart" ? "commentRangeStart" : "commentRangeEnd";
|
|
120
|
+
parts.push({ kind: "raw", xml: `<w:${name} w:id="${escapeXml(id)}"/>` });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const piece = renderInline(child, images);
|
|
124
|
+
const mark = markOf(child, "run");
|
|
125
|
+
const last = parts.at(-1);
|
|
126
|
+
if (last?.kind === "run" && sameMark(last.mark, mark)) {
|
|
127
|
+
last.pieces.push(piece);
|
|
128
|
+
} else {
|
|
129
|
+
parts.push({ kind: "run", mark, pieces: [piece] });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function splitParagraphGroups(paragraph, refs) {
|
|
133
|
+
const groups = [];
|
|
134
|
+
paragraph.forEach((child) => {
|
|
135
|
+
const sdt = markOf(child, "sdt");
|
|
136
|
+
const link = markOf(child, "link");
|
|
137
|
+
let group = groups.at(-1);
|
|
138
|
+
if (!group || !sameMark(group.sdt, sdt)) {
|
|
139
|
+
group = { sdt, links: [] };
|
|
140
|
+
groups.push(group);
|
|
141
|
+
}
|
|
142
|
+
let inLink = group.links.at(-1);
|
|
143
|
+
if (!inLink || !sameMark(inLink.link, link)) {
|
|
144
|
+
inLink = { link, parts: [] };
|
|
145
|
+
group.links.push(inLink);
|
|
146
|
+
}
|
|
147
|
+
addInline(inLink.parts, child, refs.images);
|
|
148
|
+
});
|
|
149
|
+
return groups;
|
|
150
|
+
}
|
|
151
|
+
function renderParagraphPart(part) {
|
|
152
|
+
if (part.kind === "raw") return part.xml;
|
|
153
|
+
const open = openTag("w:r", part.mark?.attrs.rAttrs);
|
|
154
|
+
const rPr = part.mark?.attrs.rPr;
|
|
155
|
+
return open + (typeof rPr === "string" ? rPr : "") + part.pieces.join("") + "</w:r>";
|
|
156
|
+
}
|
|
157
|
+
function openLinkTag(mark, links) {
|
|
158
|
+
const prefix = mark.attrs.linkPrefix;
|
|
159
|
+
const original = typeof prefix === "string" ? prefix : null;
|
|
160
|
+
const href = mark.attrs.href;
|
|
161
|
+
if (typeof href !== "string") {
|
|
162
|
+
if (original !== null) return original;
|
|
163
|
+
throw new DocxExportError(
|
|
164
|
+
"lost-original",
|
|
165
|
+
"a hyperlink carries neither an address nor the opening XML it goes back out as"
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
const relId = links.relIdOf(
|
|
169
|
+
href,
|
|
170
|
+
original === null ? null : relIdIn(original)
|
|
171
|
+
);
|
|
172
|
+
if (relId === void 0) {
|
|
173
|
+
if (original !== null) return original;
|
|
174
|
+
throw new DocxExportError(
|
|
175
|
+
"unsupported-content",
|
|
176
|
+
"an inserted hyperlink has no relationship to point at; export it through exportDocx"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
return withRelId(original ?? "<w:hyperlink>", relId);
|
|
180
|
+
}
|
|
181
|
+
function renderLinkGroup(group, links) {
|
|
182
|
+
const body = group.parts.map(renderParagraphPart).join("");
|
|
183
|
+
if (!group.link) return body;
|
|
184
|
+
return `${openLinkTag(group.link, links)}${body}</w:hyperlink>`;
|
|
185
|
+
}
|
|
186
|
+
function renderParagraphGroup(group, refs) {
|
|
187
|
+
const body = group.links.map((inLink) => renderLinkGroup(inLink, refs.links)).join("");
|
|
188
|
+
if (!group.sdt) return body;
|
|
189
|
+
const prefix = group.sdt.attrs.sdtPrefix;
|
|
190
|
+
if (typeof prefix !== "string") {
|
|
191
|
+
throw new DocxExportError(
|
|
192
|
+
"lost-original",
|
|
193
|
+
"a content control has lost the opening XML it goes back out as"
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return `${prefix}<w:sdtContent>${body}</w:sdtContent></w:sdt>`;
|
|
197
|
+
}
|
|
198
|
+
function serializeParagraph(node, refs = NO_EXPORT_REFS) {
|
|
199
|
+
const open = openTag("w:p", node.attrs.pAttrs);
|
|
200
|
+
const pPr = node.attrs.pPr;
|
|
201
|
+
const body = splitParagraphGroups(node, refs).map((group) => renderParagraphGroup(group, refs)).join("");
|
|
202
|
+
return open + (typeof pPr === "string" ? pPr : "") + body + "</w:p>";
|
|
203
|
+
}
|
|
204
|
+
export {
|
|
205
|
+
openTag,
|
|
206
|
+
preservedXml,
|
|
207
|
+
serializeParagraph
|
|
208
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuilds an edited table back into OOXML.
|
|
3
|
+
*
|
|
4
|
+
* The editor model holds a vertical merge as a single `rowspan` on the starting cell, but docx has to
|
|
5
|
+
* write an empty cell out on every continuing row. Those empty cells are recreated here.
|
|
6
|
+
*
|
|
7
|
+
* A cell's `<w:tcPr>` is taken from the original with only the merge counts and the width swapped in.
|
|
8
|
+
* That way, when a horizontal merge shrinks from two cells to one, the `w:gridSpan` goes away with it.
|
|
9
|
+
*/
|
|
10
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
11
|
+
import { type ExportRefs } from "./exportRefs";
|
|
12
|
+
export declare function serializeTable(table: PMNode, refs?: ExportRefs): string;
|