@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,134 @@
|
|
|
1
|
+
// src/docx/serializeTable.ts
|
|
2
|
+
import {
|
|
3
|
+
spanCount,
|
|
4
|
+
toGridCols,
|
|
5
|
+
toTableWidth,
|
|
6
|
+
widthNumber
|
|
7
|
+
} from "../model/format.js";
|
|
8
|
+
import { DocxExportError } from "../ooxml/errors.js";
|
|
9
|
+
import { NO_EXPORT_REFS } from "./exportRefs.js";
|
|
10
|
+
import {
|
|
11
|
+
parseProps,
|
|
12
|
+
renderProps,
|
|
13
|
+
setPropsChild,
|
|
14
|
+
TBL_PR_ORDER,
|
|
15
|
+
TC_PR_ORDER
|
|
16
|
+
} from "./propsXml.js";
|
|
17
|
+
import {
|
|
18
|
+
openTag,
|
|
19
|
+
preservedXml,
|
|
20
|
+
serializeParagraph
|
|
21
|
+
} from "./serializeParagraph.js";
|
|
22
|
+
function propsOf(xml, tag) {
|
|
23
|
+
if (typeof xml !== "string") return { tag, attrs: null, children: [] };
|
|
24
|
+
const parsed = parseProps(xml);
|
|
25
|
+
if (!parsed) {
|
|
26
|
+
throw new DocxExportError("malformed-xml", `${tag} cannot be rewritten`);
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
function widthXml(name, width) {
|
|
31
|
+
return `<w:${name} w:w="${widthNumber(width) ?? 0}" w:type="${width.type}"/>`;
|
|
32
|
+
}
|
|
33
|
+
function withWidth(props, name, width, order) {
|
|
34
|
+
if (!width) return props;
|
|
35
|
+
return {
|
|
36
|
+
...props,
|
|
37
|
+
children: setPropsChild(props.children, name, widthXml(name, width), order)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function tablePropsXml(table) {
|
|
41
|
+
const props = propsOf(table.attrs.tblPr, "w:tblPr");
|
|
42
|
+
const width = toTableWidth(table.attrs.tblW);
|
|
43
|
+
return renderProps(withWidth(props, "tblW", width, TBL_PR_ORDER));
|
|
44
|
+
}
|
|
45
|
+
function tableGridXml(table) {
|
|
46
|
+
const gridCols = toGridCols(table.attrs.gridCols);
|
|
47
|
+
if (gridCols.length === 0) return "";
|
|
48
|
+
const cols = gridCols.map((w) => `<w:gridCol w:w="${w}"/>`).join("");
|
|
49
|
+
return `<w:tblGrid>${cols}</w:tblGrid>`;
|
|
50
|
+
}
|
|
51
|
+
function cellPropsXml(cell, role) {
|
|
52
|
+
const props = propsOf(cell.attrs.tcPr, "w:tcPr");
|
|
53
|
+
const colspan = spanCount(cell.attrs.colspan);
|
|
54
|
+
const rowspan = spanCount(cell.attrs.rowspan);
|
|
55
|
+
const gridSpan = colspan > 1 ? `<w:gridSpan w:val="${colspan}"/>` : null;
|
|
56
|
+
const vMerge = role === "continue" ? "<w:vMerge/>" : rowspan > 1 ? '<w:vMerge w:val="restart"/>' : null;
|
|
57
|
+
let children = setPropsChild(
|
|
58
|
+
props.children,
|
|
59
|
+
"gridSpan",
|
|
60
|
+
gridSpan,
|
|
61
|
+
TC_PR_ORDER
|
|
62
|
+
);
|
|
63
|
+
children = setPropsChild(children, "vMerge", vMerge, TC_PR_ORDER);
|
|
64
|
+
const width = toTableWidth(cell.attrs.tcW);
|
|
65
|
+
return renderProps(
|
|
66
|
+
withWidth({ ...props, children }, "tcW", width, TC_PR_ORDER)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
function cellBlockXml(block, refs) {
|
|
70
|
+
if (block.type.name === "paragraph") return serializeParagraph(block, refs);
|
|
71
|
+
if (block.type.name === "rawBlock") return preservedXml(block);
|
|
72
|
+
if (block.type.name === "table") return serializeTable(block, refs);
|
|
73
|
+
throw new DocxExportError(
|
|
74
|
+
"unsupported-content",
|
|
75
|
+
`block that cannot go inside a table cell: ${block.type.name}`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function wrapInSdt(xml, cell, role) {
|
|
79
|
+
const prefix = cell.attrs.sdtPrefix;
|
|
80
|
+
if (role !== "start" || typeof prefix !== "string") return xml;
|
|
81
|
+
return `${prefix}<w:sdtContent>${xml}</w:sdtContent></w:sdt>`;
|
|
82
|
+
}
|
|
83
|
+
function cellXml(cell, role, refs) {
|
|
84
|
+
const body = role === "continue" ? "<w:p/>" : cell.children.map((block) => cellBlockXml(block, refs)).join("");
|
|
85
|
+
const xml = openTag("w:tc", cell.attrs.tcAttrs) + cellPropsXml(cell, role) + body + "</w:tc>";
|
|
86
|
+
return wrapInSdt(xml, cell, role);
|
|
87
|
+
}
|
|
88
|
+
function placeRow(row, covering) {
|
|
89
|
+
const placed = [];
|
|
90
|
+
let column = 0;
|
|
91
|
+
let next = 0;
|
|
92
|
+
for (; ; ) {
|
|
93
|
+
const cover = covering.find(
|
|
94
|
+
(entry) => entry.column === column && entry.rowsLeft > 0
|
|
95
|
+
);
|
|
96
|
+
if (cover) {
|
|
97
|
+
placed.push({ cell: cover.cell, role: "continue" });
|
|
98
|
+
cover.rowsLeft -= 1;
|
|
99
|
+
column += cover.colspan;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (next >= row.childCount) break;
|
|
103
|
+
const cell = row.child(next);
|
|
104
|
+
next += 1;
|
|
105
|
+
const colspan = spanCount(cell.attrs.colspan);
|
|
106
|
+
const rowspan = spanCount(cell.attrs.rowspan);
|
|
107
|
+
placed.push({ cell, role: "start" });
|
|
108
|
+
if (rowspan > 1) {
|
|
109
|
+
covering.push({ column, colspan, cell, rowsLeft: rowspan - 1 });
|
|
110
|
+
}
|
|
111
|
+
column += colspan;
|
|
112
|
+
}
|
|
113
|
+
return placed;
|
|
114
|
+
}
|
|
115
|
+
function rowXml(row, covering, refs) {
|
|
116
|
+
const cells = placeRow(row, covering).map((placed) => cellXml(placed.cell, placed.role, refs)).join("");
|
|
117
|
+
const tblPrEx = row.attrs.tblPrEx;
|
|
118
|
+
const trPr = row.attrs.trPr;
|
|
119
|
+
return openTag("w:tr", row.attrs.trAttrs) + (typeof tblPrEx === "string" ? tblPrEx : "") + (typeof trPr === "string" ? trPr : "") + cells + "</w:tr>";
|
|
120
|
+
}
|
|
121
|
+
function serializeTable(table, refs = NO_EXPORT_REFS) {
|
|
122
|
+
const covering = [];
|
|
123
|
+
const rows = table.children.map((row) => rowXml(row, covering, refs)).join("");
|
|
124
|
+
if (covering.some((entry) => entry.rowsLeft > 0)) {
|
|
125
|
+
throw new DocxExportError(
|
|
126
|
+
"invalid-table",
|
|
127
|
+
"a vertical merge in the table reaches past the last row"
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return openTag("w:tbl", table.attrs.tblAttrs) + tablePropsXml(table) + tableGridXml(table) + rows + "</w:tbl>";
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
serializeTable
|
|
134
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store that holds the original as it was the moment the document was opened
|
|
3
|
+
* and hands it back at export time.
|
|
4
|
+
*
|
|
5
|
+
* What leaves the package is `DocxSession`, a token with nothing readable on it: the store behind
|
|
6
|
+
* it holds the file's own XML, which nothing but the exporter can put back together, and a
|
|
7
|
+
* consumer reading a piece of it would be reading an internal shape. What a consumer legitimately
|
|
8
|
+
* needs is an accessor of its own here.
|
|
9
|
+
*/
|
|
10
|
+
import type { Node as PMNode } from "prosemirror-model";
|
|
11
|
+
import type { DocumentDefaults } from "../model/format";
|
|
12
|
+
import { type Numbering } from "../numbering/parseNumbering";
|
|
13
|
+
import type { ImportedComments } from "./comments";
|
|
14
|
+
import type { ParagraphFormatLayer, ParagraphStyleOption, StyleTable } from "./formatting";
|
|
15
|
+
import type { HeadersFooters } from "./headersFooters";
|
|
16
|
+
import type { PageGeometry } from "./pageGeometry";
|
|
17
|
+
export interface ImportedBlock {
|
|
18
|
+
xml: string;
|
|
19
|
+
/** The node as it was the moment the document was opened. Compared against the current node to tell whether it was edited */
|
|
20
|
+
node: PMNode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The session `importDocx` hands out and `exportDocx` takes back, untouched.
|
|
24
|
+
*
|
|
25
|
+
* It carries the parts of the package and the original XML pieces that go back out unchanged,
|
|
26
|
+
* which is how a document nobody edited exports part for part identical.
|
|
27
|
+
*/
|
|
28
|
+
export interface DocxSession {
|
|
29
|
+
readonly kind: "docxSession";
|
|
30
|
+
}
|
|
31
|
+
/** What the package's own layers read the open document off */
|
|
32
|
+
export declare class SessionStore implements DocxSession {
|
|
33
|
+
readonly kind = "docxSession";
|
|
34
|
+
readonly parts: Map<string, Uint8Array>;
|
|
35
|
+
readonly mainPartPath: string;
|
|
36
|
+
readonly documentPrefix: string;
|
|
37
|
+
readonly documentSuffix: string;
|
|
38
|
+
readonly documentHadBom: boolean;
|
|
39
|
+
readonly blocks: ImportedBlock[];
|
|
40
|
+
/** The document default formatting read from styles.xml. Used for display only */
|
|
41
|
+
readonly defaults: DocumentDefaults;
|
|
42
|
+
/** The effective automatic tab interval read from settings.xml. Used for display only. */
|
|
43
|
+
readonly defaultTabStopPt: number;
|
|
44
|
+
/** The paragraph properties at the base of the OOXML formatting hierarchy. */
|
|
45
|
+
readonly paragraphDefaults: ParagraphFormatLayer;
|
|
46
|
+
/** The paper this document is written on, read from the first section. Used for display only: the `w:sectPr` itself goes back out in the preserved tail */
|
|
47
|
+
readonly geometry: PageGeometry;
|
|
48
|
+
/** The style chain from styles.xml. Used only to fold the style a paragraph points at into its displayed values */
|
|
49
|
+
readonly styles: StyleTable;
|
|
50
|
+
/** The paragraph styles this document defines, in the order styles.xml lists them. What the style picker offers */
|
|
51
|
+
readonly paragraphStyles: ParagraphStyleOption[];
|
|
52
|
+
/**
|
|
53
|
+
* The paragraph style every paragraph with no `w:pStyle` of its own wears (`w:default="1"`,
|
|
54
|
+
* usually Normal). null when the document marks none
|
|
55
|
+
*/
|
|
56
|
+
readonly defaultParagraphStyleId: string | null;
|
|
57
|
+
/** The raw text of numbering.xml. Kept around so list numbers can be drawn on screen. null if there is none */
|
|
58
|
+
readonly numberingXml: string | null;
|
|
59
|
+
/** Where numbering.xml sits inside the zip. When a new list is exported it is rewritten at that spot. null if there is none */
|
|
60
|
+
readonly numberingPartPath: string | null;
|
|
61
|
+
/** The Comments part as opened, including comment elements retained for a byte-identical pass. */
|
|
62
|
+
readonly comments: ImportedComments;
|
|
63
|
+
/** Comment ids referenced by the original main story. A missing id after editing means deletion. */
|
|
64
|
+
readonly commentReferenceIds: ReadonlySet<string>;
|
|
65
|
+
/** First-section header and footer stories projected for the page preview. */
|
|
66
|
+
readonly headersFooters: HeadersFooters;
|
|
67
|
+
constructor(opened: Omit<SessionStore, "kind">);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The store behind a session token.
|
|
71
|
+
*
|
|
72
|
+
* Every function that takes a session from outside comes through here, so an object the package
|
|
73
|
+
* never handed out is turned down with a message rather than read field by field into nonsense.
|
|
74
|
+
*/
|
|
75
|
+
export declare function sessionOf(session: DocxSession): SessionStore;
|
|
76
|
+
/** Where the body part sits inside the package, which is the one part an export rewrites */
|
|
77
|
+
export declare function documentPartPath(session: DocxSession): string;
|
|
78
|
+
/** The list definitions the document carries, which is what a paragraph's numbering points into */
|
|
79
|
+
export declare function documentNumbering(session: DocxSession): Numbering;
|
|
80
|
+
/** Finds which original block a node came from. undefined for a node newly created during editing */
|
|
81
|
+
export declare function originalBlock(node: PMNode, session: SessionStore): ImportedBlock | undefined;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/docx/session.ts
|
|
2
|
+
import { parseNumbering } from "../numbering/parseNumbering.js";
|
|
3
|
+
var SessionStore = class {
|
|
4
|
+
kind = "docxSession";
|
|
5
|
+
parts;
|
|
6
|
+
mainPartPath;
|
|
7
|
+
documentPrefix;
|
|
8
|
+
documentSuffix;
|
|
9
|
+
documentHadBom;
|
|
10
|
+
blocks;
|
|
11
|
+
/** The document default formatting read from styles.xml. Used for display only */
|
|
12
|
+
defaults;
|
|
13
|
+
/** The effective automatic tab interval read from settings.xml. Used for display only. */
|
|
14
|
+
defaultTabStopPt;
|
|
15
|
+
/** The paragraph properties at the base of the OOXML formatting hierarchy. */
|
|
16
|
+
paragraphDefaults;
|
|
17
|
+
/** The paper this document is written on, read from the first section. Used for display only: the `w:sectPr` itself goes back out in the preserved tail */
|
|
18
|
+
geometry;
|
|
19
|
+
/** The style chain from styles.xml. Used only to fold the style a paragraph points at into its displayed values */
|
|
20
|
+
styles;
|
|
21
|
+
/** The paragraph styles this document defines, in the order styles.xml lists them. What the style picker offers */
|
|
22
|
+
paragraphStyles;
|
|
23
|
+
/**
|
|
24
|
+
* The paragraph style every paragraph with no `w:pStyle` of its own wears (`w:default="1"`,
|
|
25
|
+
* usually Normal). null when the document marks none
|
|
26
|
+
*/
|
|
27
|
+
defaultParagraphStyleId;
|
|
28
|
+
/** The raw text of numbering.xml. Kept around so list numbers can be drawn on screen. null if there is none */
|
|
29
|
+
numberingXml;
|
|
30
|
+
/** Where numbering.xml sits inside the zip. When a new list is exported it is rewritten at that spot. null if there is none */
|
|
31
|
+
numberingPartPath;
|
|
32
|
+
/** The Comments part as opened, including comment elements retained for a byte-identical pass. */
|
|
33
|
+
comments;
|
|
34
|
+
/** Comment ids referenced by the original main story. A missing id after editing means deletion. */
|
|
35
|
+
commentReferenceIds;
|
|
36
|
+
/** First-section header and footer stories projected for the page preview. */
|
|
37
|
+
headersFooters;
|
|
38
|
+
constructor(opened) {
|
|
39
|
+
this.parts = opened.parts;
|
|
40
|
+
this.mainPartPath = opened.mainPartPath;
|
|
41
|
+
this.documentPrefix = opened.documentPrefix;
|
|
42
|
+
this.documentSuffix = opened.documentSuffix;
|
|
43
|
+
this.documentHadBom = opened.documentHadBom;
|
|
44
|
+
this.blocks = opened.blocks;
|
|
45
|
+
this.defaults = opened.defaults;
|
|
46
|
+
this.defaultTabStopPt = opened.defaultTabStopPt;
|
|
47
|
+
this.paragraphDefaults = opened.paragraphDefaults;
|
|
48
|
+
this.geometry = opened.geometry;
|
|
49
|
+
this.styles = opened.styles;
|
|
50
|
+
this.paragraphStyles = opened.paragraphStyles;
|
|
51
|
+
this.defaultParagraphStyleId = opened.defaultParagraphStyleId;
|
|
52
|
+
this.numberingXml = opened.numberingXml;
|
|
53
|
+
this.numberingPartPath = opened.numberingPartPath;
|
|
54
|
+
this.comments = opened.comments;
|
|
55
|
+
this.commentReferenceIds = opened.commentReferenceIds;
|
|
56
|
+
this.headersFooters = opened.headersFooters;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function sessionOf(session) {
|
|
60
|
+
if (session instanceof SessionStore) return session;
|
|
61
|
+
throw new Error("the session must be the one importDocx handed back");
|
|
62
|
+
}
|
|
63
|
+
function documentPartPath(session) {
|
|
64
|
+
return sessionOf(session).mainPartPath;
|
|
65
|
+
}
|
|
66
|
+
function documentNumbering(session) {
|
|
67
|
+
return parseNumbering(sessionOf(session).numberingXml);
|
|
68
|
+
}
|
|
69
|
+
function originalBlock(node, session) {
|
|
70
|
+
const srcId = node.attrs.srcId;
|
|
71
|
+
return typeof srcId === "number" ? session.blocks[srcId] : void 0;
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
SessionStore,
|
|
75
|
+
documentNumbering,
|
|
76
|
+
documentPartPath,
|
|
77
|
+
originalBlock,
|
|
78
|
+
sessionOf
|
|
79
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies focused edits to a cell's preserved formatting XML. The fragment is never rebuilt:
|
|
3
|
+
* only the child and attributes named by the edit are changed, and unsupported formatting stays
|
|
4
|
+
* untouched.
|
|
5
|
+
*/
|
|
6
|
+
import type { CellFormat, CellMargins, CellVerticalAlign, RowFormat } from "../../model/format";
|
|
7
|
+
import { type CellBorderDefaults } from "./reading";
|
|
8
|
+
export interface CellProps {
|
|
9
|
+
/** The whole `<w:tcPr>...</w:tcPr>` XML. null for a cell with no formatting */
|
|
10
|
+
tcPr: string | null;
|
|
11
|
+
format: CellFormat | null;
|
|
12
|
+
}
|
|
13
|
+
export type CellSide = "top" | "bottom" | "left" | "right";
|
|
14
|
+
export declare const ALL_CELL_SIDES: readonly CellSide[];
|
|
15
|
+
/** The line a border preset writes. `none` states that this side draws nothing */
|
|
16
|
+
export type CellBorderLine = "single" | "none";
|
|
17
|
+
/** A job that changes one piece of cell formatting */
|
|
18
|
+
export type CellFormatEdit =
|
|
19
|
+
/** The background fill (`w:shd`). A null color takes the fill away */
|
|
20
|
+
{
|
|
21
|
+
kind: "background";
|
|
22
|
+
hex: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** The line on the named sides (`w:tcBorders`). The other sides are left alone */
|
|
25
|
+
| {
|
|
26
|
+
kind: "borders";
|
|
27
|
+
line: CellBorderLine;
|
|
28
|
+
sides: readonly CellSide[];
|
|
29
|
+
}
|
|
30
|
+
/** The color of the sides that already draw a line. A null color resets them to `auto` */
|
|
31
|
+
| {
|
|
32
|
+
kind: "borderColor";
|
|
33
|
+
hex: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** The vertical placement of content within the cell (`w:vAlign`). */
|
|
36
|
+
| {
|
|
37
|
+
kind: "verticalAlign";
|
|
38
|
+
align: CellVerticalAlign;
|
|
39
|
+
}
|
|
40
|
+
/** The directly formatted cell margins, in points. Omitted sides remain unchanged. */
|
|
41
|
+
| {
|
|
42
|
+
kind: "padding";
|
|
43
|
+
values: Partial<Record<CellSide, number>>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The cell formatting XML and display values after one piece of formatting is changed.
|
|
47
|
+
*
|
|
48
|
+
* null when there is nothing to do: a fragment whose shape could not be made out, a value that
|
|
49
|
+
* cannot be written into the document, or a cell already in the state the job wants. In every one
|
|
50
|
+
* of those cases the cell keeps its original XML.
|
|
51
|
+
*/
|
|
52
|
+
export declare function editCellProps(tcPr: string | null, edit: CellFormatEdit, defaults?: CellBorderDefaults, margins?: CellMargins): CellProps | null;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the cell's own formatting draws a line on any side.
|
|
55
|
+
* A border color has nothing to act on when it does not, because the lines on screen are then the
|
|
56
|
+
* table's own, which belong to the table rather than to this cell.
|
|
57
|
+
*/
|
|
58
|
+
export declare function drawsOwnCellBorder(tcPr: string | null): boolean;
|
|
59
|
+
export interface RowProps {
|
|
60
|
+
trPr: string;
|
|
61
|
+
format: RowFormat;
|
|
62
|
+
}
|
|
63
|
+
/** Writes a row height in points while retaining unrelated `w:trPr` content. */
|
|
64
|
+
export declare function editRowHeight(trPr: string | null, heightPt: number): RowProps | null;
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// src/docx/tableFormatting/editing.ts
|
|
2
|
+
import { normalizeHex, wAttr } from "../../ooxml/units.js";
|
|
3
|
+
import { childByLocalName, escapeXml, localPart } from "../../ooxml/xml.js";
|
|
4
|
+
import {
|
|
5
|
+
parseProps,
|
|
6
|
+
parsePropsXml,
|
|
7
|
+
propsChild,
|
|
8
|
+
renderProps,
|
|
9
|
+
setPropsChild,
|
|
10
|
+
TC_PR_ORDER,
|
|
11
|
+
TR_PR_ORDER
|
|
12
|
+
} from "../propsXml.js";
|
|
13
|
+
import {
|
|
14
|
+
NO_BORDER_DEFAULTS,
|
|
15
|
+
NO_CELL_MARGINS,
|
|
16
|
+
readCellProps,
|
|
17
|
+
readRowFormat
|
|
18
|
+
} from "./reading.js";
|
|
19
|
+
var ALL_CELL_SIDES = [
|
|
20
|
+
"top",
|
|
21
|
+
"bottom",
|
|
22
|
+
"left",
|
|
23
|
+
"right"
|
|
24
|
+
];
|
|
25
|
+
var TC_BORDERS_ORDER = [
|
|
26
|
+
"top",
|
|
27
|
+
"start",
|
|
28
|
+
"left",
|
|
29
|
+
"bottom",
|
|
30
|
+
"end",
|
|
31
|
+
"right",
|
|
32
|
+
"insideH",
|
|
33
|
+
"insideV",
|
|
34
|
+
"tl2br",
|
|
35
|
+
"tr2bl"
|
|
36
|
+
];
|
|
37
|
+
var SIDE_NAMES = {
|
|
38
|
+
top: ["top"],
|
|
39
|
+
bottom: ["bottom"],
|
|
40
|
+
left: ["left", "start"],
|
|
41
|
+
right: ["right", "end"]
|
|
42
|
+
};
|
|
43
|
+
var PRESET_BORDER_EIGHTHS = 4;
|
|
44
|
+
var THEME_COLOR_ATTRS = ["themeColor", "themeTint", "themeShade"];
|
|
45
|
+
var THEME_FILL_ATTRS = ["themeFill", "themeFillTint", "themeFillShade"];
|
|
46
|
+
var EMPTY_TC_PR = { tag: "w:tcPr", attrs: null, children: [] };
|
|
47
|
+
function attrsOf(el) {
|
|
48
|
+
return el ? Array.from(el.attributes).map((attr) => [attr.name, attr.value]) : [];
|
|
49
|
+
}
|
|
50
|
+
function withAttr(attrs, name, value, overridden = []) {
|
|
51
|
+
const kept = attrs.filter(([attr]) => !overridden.includes(localPart(attr)));
|
|
52
|
+
const at = kept.findIndex(([attr]) => localPart(attr) === name);
|
|
53
|
+
if (at === -1) return [...kept, [`w:${name}`, value]];
|
|
54
|
+
return kept.map(
|
|
55
|
+
(attr, index) => index === at ? [attr[0], value] : attr
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function elementXml(tag, attrs) {
|
|
59
|
+
const text = attrs.map(([name, value]) => `${name}="${escapeXml(value)}"`).join(" ");
|
|
60
|
+
return `<${tag} ${text}/>`;
|
|
61
|
+
}
|
|
62
|
+
function drawsLine(side) {
|
|
63
|
+
if (!side) return false;
|
|
64
|
+
const val = wAttr(side, "val");
|
|
65
|
+
return val !== null && val !== "nil" && val !== "none";
|
|
66
|
+
}
|
|
67
|
+
function sideAttrs(current, edit, fallback = null) {
|
|
68
|
+
if (edit.kind === "color") {
|
|
69
|
+
if (!drawsLine(current)) {
|
|
70
|
+
if (current || !fallback || fallback === "none") return null;
|
|
71
|
+
return inheritedBorderAttrs(fallback, edit.hex);
|
|
72
|
+
}
|
|
73
|
+
return withAttr(
|
|
74
|
+
attrsOf(current),
|
|
75
|
+
"color",
|
|
76
|
+
edit.hex ?? "auto",
|
|
77
|
+
THEME_COLOR_ATTRS
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (edit.line === "none") {
|
|
81
|
+
return current ? withAttr(attrsOf(current), "val", "none") : [["w:val", "none"]];
|
|
82
|
+
}
|
|
83
|
+
if (!current) {
|
|
84
|
+
return [
|
|
85
|
+
["w:val", "single"],
|
|
86
|
+
["w:sz", `${PRESET_BORDER_EIGHTHS}`],
|
|
87
|
+
["w:space", "0"],
|
|
88
|
+
["w:color", "auto"]
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
return withAttr(
|
|
92
|
+
withAttr(attrsOf(current), "val", "single"),
|
|
93
|
+
"sz",
|
|
94
|
+
`${PRESET_BORDER_EIGHTHS}`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
var BORDER_VAL_BY_CSS_STYLE = {
|
|
98
|
+
solid: "single",
|
|
99
|
+
double: "double",
|
|
100
|
+
dashed: "dashed",
|
|
101
|
+
dotted: "dotted"
|
|
102
|
+
};
|
|
103
|
+
function inheritedBorderAttrs(border, hex) {
|
|
104
|
+
const matched = border.match(
|
|
105
|
+
/^(\d+(?:\.\d+)?)pt (solid|double|dashed|dotted) (#[0-9a-f]{6})$/i
|
|
106
|
+
);
|
|
107
|
+
if (!matched) return null;
|
|
108
|
+
const [, widthText, style, currentColor] = matched;
|
|
109
|
+
if (hex !== null && normalizeHex(currentColor) === hex) return null;
|
|
110
|
+
const eighths = Math.round(Number.parseFloat(widthText) * 8);
|
|
111
|
+
if (!Number.isSafeInteger(eighths) || eighths <= 0) return null;
|
|
112
|
+
return [
|
|
113
|
+
["w:val", BORDER_VAL_BY_CSS_STYLE[style.toLowerCase()]],
|
|
114
|
+
["w:sz", `${eighths}`],
|
|
115
|
+
["w:space", "0"],
|
|
116
|
+
["w:color", hex ?? "auto"]
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
function sideNames(borders, side) {
|
|
120
|
+
const [primary, alternate] = SIDE_NAMES[side];
|
|
121
|
+
const usesAlternate = alternate !== void 0 && borders !== null && childByLocalName(borders, primary) === null && childByLocalName(borders, alternate) !== null;
|
|
122
|
+
if (usesAlternate) return { write: alternate, drop: null };
|
|
123
|
+
return { write: primary, drop: alternate ?? null };
|
|
124
|
+
}
|
|
125
|
+
function editedBorders(current, edit, defaults = NO_BORDER_DEFAULTS) {
|
|
126
|
+
const props = current === null ? { tag: "w:tcBorders", attrs: null, children: [] } : parseProps(current);
|
|
127
|
+
const borders = current === null ? null : parsePropsXml(current);
|
|
128
|
+
if (!props || current !== null && !borders) return null;
|
|
129
|
+
const sides = edit.kind === "line" ? edit.sides : ALL_CELL_SIDES;
|
|
130
|
+
const children = sides.reduce((kept, side) => {
|
|
131
|
+
const { write, drop } = sideNames(borders, side);
|
|
132
|
+
const currentSide = borders ? childByLocalName(borders, write) : null;
|
|
133
|
+
const attrs = sideAttrs(currentSide, edit, defaults[side]);
|
|
134
|
+
if (!attrs) return kept;
|
|
135
|
+
const tag = currentSide?.nodeName ?? `w:${write}`;
|
|
136
|
+
const written = setPropsChild(
|
|
137
|
+
kept,
|
|
138
|
+
write,
|
|
139
|
+
elementXml(tag, attrs),
|
|
140
|
+
TC_BORDERS_ORDER
|
|
141
|
+
);
|
|
142
|
+
return drop === null ? written : setPropsChild(written, drop, null, TC_BORDERS_ORDER);
|
|
143
|
+
}, props.children);
|
|
144
|
+
const xml = renderProps({ ...props, children });
|
|
145
|
+
return { xml: xml === "" ? null : xml };
|
|
146
|
+
}
|
|
147
|
+
function hasPattern(shd) {
|
|
148
|
+
const val = shd ? wAttr(shd, "val") : null;
|
|
149
|
+
return val !== null && val !== "clear" && val !== "nil";
|
|
150
|
+
}
|
|
151
|
+
function editedShading(current, fill) {
|
|
152
|
+
const tag = current?.nodeName ?? "w:shd";
|
|
153
|
+
if (fill === null) {
|
|
154
|
+
if (!hasPattern(current)) return null;
|
|
155
|
+
return elementXml(
|
|
156
|
+
tag,
|
|
157
|
+
withAttr(attrsOf(current), "fill", "auto", THEME_FILL_ATTRS)
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (!current) {
|
|
161
|
+
return elementXml(tag, [
|
|
162
|
+
["w:val", "clear"],
|
|
163
|
+
["w:color", "auto"],
|
|
164
|
+
["w:fill", fill]
|
|
165
|
+
]);
|
|
166
|
+
}
|
|
167
|
+
const painting = hasPattern(current) ? attrsOf(current) : withAttr(attrsOf(current), "val", "clear");
|
|
168
|
+
return elementXml(tag, withAttr(painting, "fill", fill, THEME_FILL_ATTRS));
|
|
169
|
+
}
|
|
170
|
+
function bordersChange(props, edit, defaults) {
|
|
171
|
+
const current = propsChild(props.children, "tcBorders")?.xml ?? null;
|
|
172
|
+
const edited = editedBorders(current, edit, defaults);
|
|
173
|
+
return edited === null ? null : { name: "tcBorders", xml: edited.xml };
|
|
174
|
+
}
|
|
175
|
+
var TC_MAR_ORDER = [
|
|
176
|
+
"top",
|
|
177
|
+
"start",
|
|
178
|
+
"left",
|
|
179
|
+
"bottom",
|
|
180
|
+
"end",
|
|
181
|
+
"right"
|
|
182
|
+
];
|
|
183
|
+
var MARGIN_SIDE_NAMES = {
|
|
184
|
+
top: ["top"],
|
|
185
|
+
right: ["end", "right"],
|
|
186
|
+
bottom: ["bottom"],
|
|
187
|
+
left: ["start", "left"]
|
|
188
|
+
};
|
|
189
|
+
function paddingChange(props, values) {
|
|
190
|
+
const current = propsChild(props.children, "tcMar")?.xml ?? null;
|
|
191
|
+
const margins = current === null ? null : parsePropsXml(current);
|
|
192
|
+
const parsed = current === null ? { tag: "w:tcMar", attrs: null, children: [] } : parseProps(current);
|
|
193
|
+
if (!parsed || current !== null && !margins) return null;
|
|
194
|
+
let children = parsed.children;
|
|
195
|
+
let wrote = false;
|
|
196
|
+
for (const side of ALL_CELL_SIDES) {
|
|
197
|
+
const points = values[side];
|
|
198
|
+
if (points === void 0) continue;
|
|
199
|
+
const twips = Math.round(points * 20);
|
|
200
|
+
if (!Number.isFinite(points) || points < 0 || !Number.isSafeInteger(twips)) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const existing = margins ? MARGIN_SIDE_NAMES[side].map((name2) => childByLocalName(margins, name2)).find((element) => element !== null) ?? null : null;
|
|
204
|
+
const name = existing?.localName ?? side;
|
|
205
|
+
const attrs = withAttr(
|
|
206
|
+
withAttr(attrsOf(existing), "w", `${twips}`),
|
|
207
|
+
"type",
|
|
208
|
+
"dxa"
|
|
209
|
+
);
|
|
210
|
+
children = setPropsChild(
|
|
211
|
+
children,
|
|
212
|
+
name,
|
|
213
|
+
elementXml(existing?.nodeName ?? `w:${side}`, attrs),
|
|
214
|
+
TC_MAR_ORDER
|
|
215
|
+
);
|
|
216
|
+
wrote = true;
|
|
217
|
+
}
|
|
218
|
+
if (!wrote) return null;
|
|
219
|
+
return { name: "tcMar", xml: renderProps({ ...parsed, children }) };
|
|
220
|
+
}
|
|
221
|
+
function childChange(edit, props, defaults) {
|
|
222
|
+
switch (edit.kind) {
|
|
223
|
+
case "background": {
|
|
224
|
+
const fill = edit.hex === null ? null : normalizeHex(edit.hex);
|
|
225
|
+
if (edit.hex !== null && fill === null) return null;
|
|
226
|
+
const current = propsChild(props.children, "shd")?.xml ?? null;
|
|
227
|
+
const shd = current === null ? null : parsePropsXml(current);
|
|
228
|
+
if (current !== null && !shd) return null;
|
|
229
|
+
return { name: "shd", xml: editedShading(shd, fill) };
|
|
230
|
+
}
|
|
231
|
+
case "borders":
|
|
232
|
+
return bordersChange(
|
|
233
|
+
props,
|
|
234
|
+
{
|
|
235
|
+
kind: "line",
|
|
236
|
+
line: edit.line,
|
|
237
|
+
sides: edit.sides
|
|
238
|
+
},
|
|
239
|
+
defaults
|
|
240
|
+
);
|
|
241
|
+
case "borderColor": {
|
|
242
|
+
const hex = edit.hex === null ? null : normalizeHex(edit.hex);
|
|
243
|
+
if (edit.hex !== null && hex === null) return null;
|
|
244
|
+
return bordersChange(props, { kind: "color", hex }, defaults);
|
|
245
|
+
}
|
|
246
|
+
case "verticalAlign": {
|
|
247
|
+
const current = propsChild(props.children, "vAlign")?.xml ?? null;
|
|
248
|
+
const element = current === null ? null : parsePropsXml(current);
|
|
249
|
+
if (current !== null && !element) return null;
|
|
250
|
+
return {
|
|
251
|
+
name: "vAlign",
|
|
252
|
+
xml: elementXml(
|
|
253
|
+
element?.nodeName ?? "w:vAlign",
|
|
254
|
+
withAttr(attrsOf(element), "val", edit.align)
|
|
255
|
+
)
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
case "padding":
|
|
259
|
+
return paddingChange(props, edit.values);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function editCellProps(tcPr, edit, defaults = NO_BORDER_DEFAULTS, margins = NO_CELL_MARGINS) {
|
|
263
|
+
const props = tcPr === null ? EMPTY_TC_PR : parseProps(tcPr);
|
|
264
|
+
if (!props) return null;
|
|
265
|
+
const change = childChange(edit, props, defaults);
|
|
266
|
+
if (!change) return null;
|
|
267
|
+
const children = setPropsChild(
|
|
268
|
+
props.children,
|
|
269
|
+
change.name,
|
|
270
|
+
change.xml,
|
|
271
|
+
TC_PR_ORDER
|
|
272
|
+
);
|
|
273
|
+
const rendered = renderProps({ ...props, children });
|
|
274
|
+
const next = rendered === "" ? null : rendered;
|
|
275
|
+
if (next === tcPr) return null;
|
|
276
|
+
return { tcPr: next, format: readCellProps(next, defaults, margins) };
|
|
277
|
+
}
|
|
278
|
+
function drawsOwnCellBorder(tcPr) {
|
|
279
|
+
const el = tcPr === null ? null : parsePropsXml(tcPr);
|
|
280
|
+
const borders = el ? childByLocalName(el, "tcBorders") : null;
|
|
281
|
+
if (!borders) return false;
|
|
282
|
+
return ALL_CELL_SIDES.some(
|
|
283
|
+
(side) => SIDE_NAMES[side].some((name) => drawsLine(childByLocalName(borders, name)))
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
function editRowHeight(trPr, heightPt) {
|
|
287
|
+
const twips = Math.round(heightPt * 20);
|
|
288
|
+
if (!Number.isFinite(heightPt) || heightPt <= 0 || !Number.isSafeInteger(twips)) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
const props = trPr === null ? { tag: "w:trPr", attrs: null, children: [] } : parseProps(trPr);
|
|
292
|
+
if (!props) return null;
|
|
293
|
+
const current = propsChild(props.children, "trHeight")?.xml ?? null;
|
|
294
|
+
const element = current === null ? null : parsePropsXml(current);
|
|
295
|
+
if (current !== null && !element) return null;
|
|
296
|
+
const writtenRule = element ? wAttr(element, "hRule") : null;
|
|
297
|
+
const attrs = withAttr(attrsOf(element), "val", `${twips}`);
|
|
298
|
+
const nextAttrs = withAttr(
|
|
299
|
+
attrs,
|
|
300
|
+
"hRule",
|
|
301
|
+
writtenRule === "exact" ? "exact" : "atLeast"
|
|
302
|
+
);
|
|
303
|
+
const child = elementXml(element?.nodeName ?? "w:trHeight", nextAttrs);
|
|
304
|
+
const rendered = renderProps({
|
|
305
|
+
...props,
|
|
306
|
+
children: setPropsChild(props.children, "trHeight", child, TR_PR_ORDER)
|
|
307
|
+
});
|
|
308
|
+
if (rendered === trPr) return null;
|
|
309
|
+
const parsed = parsePropsXml(rendered);
|
|
310
|
+
const format = parsed ? readRowFormat(parsed) : null;
|
|
311
|
+
return format ? { trPr: rendered, format } : null;
|
|
312
|
+
}
|
|
313
|
+
export {
|
|
314
|
+
ALL_CELL_SIDES,
|
|
315
|
+
drawsOwnCellBorder,
|
|
316
|
+
editCellProps,
|
|
317
|
+
editRowHeight
|
|
318
|
+
};
|