@docen/export-docx 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,271 +1,271 @@
1
- # @docen/export-docx
2
-
3
- ![npm version](https://img.shields.io/npm/v/@docen/export-docx)
4
- ![npm downloads](https://img.shields.io/npm/dw/@docen/export-docx)
5
- ![npm license](https://img.shields.io/npm/l/@docen/export-docx)
6
-
7
- > Export TipTap/ProseMirror editor content to Microsoft Word DOCX format.
8
-
9
- ## Features
10
-
11
- - 📝 **Rich Text Support** - Full support for headings, paragraphs, and blockquotes with proper formatting
12
- - 🖼️ **Image Handling** - Automatic image sizing, positioning, and metadata extraction
13
- - 📊 **Table Support** - Complete table structure with headers, cells, colspan, and rowspan
14
- - ✅ **Lists & Tasks** - Bullet lists, numbered lists with custom start numbers, and task lists with checkboxes
15
- - 🎨 **Text Formatting** - Bold, italic, underline, strikethrough, subscript, and superscript
16
- - 🎯 **Text Styles** - Comprehensive style support including colors, backgrounds, fonts, sizes, and line heights
17
- - 🔗 **Links** - Hyperlink support with href preservation
18
- - 💻 **Code Blocks** - Syntax highlighted code blocks with language attribute support
19
- - 📁 **Collapsible Content** - Details/summary sections for expandable content
20
- - 😀 **Emoji Support** - Native emoji rendering in documents
21
- - 🧮 **Mathematical Content** - LaTeX-style formula support
22
- - ⚙️ **Configurable Options** - Customizable export options for documents, tables, styles, and horizontal rules
23
-
24
- ## Installation
25
-
26
- ```bash
27
- # Install with npm
28
- $ npm install @docen/export-docx
29
-
30
- # Install with yarn
31
- $ yarn add @docen/export-docx
32
-
33
- # Install with pnpm
34
- $ pnpm add @docen/export-docx
35
- ```
36
-
37
- ## Quick Start
38
-
39
- ```typescript
40
- import { generateDOCX } from "@docen/export-docx";
41
- import { writeFileSync } from "node:fs";
42
-
43
- // Your TipTap/ProseMirror editor content
44
- const content = {
45
- type: "doc",
46
- content: [
47
- {
48
- type: "paragraph",
49
- content: [
50
- {
51
- type: "text",
52
- marks: [{ type: "bold" }, { type: "italic" }],
53
- text: "Hello, world!",
54
- },
55
- ],
56
- },
57
- ],
58
- };
59
-
60
- // Convert to DOCX and save to file
61
- const docx = await generateDOCX(content, { outputType: "nodebuffer" });
62
- writeFileSync("document.docx", docx);
63
- ```
64
-
65
- ## API Reference
66
-
67
- ### `generateDOCX(content, options)`
68
-
69
- Converts TipTap/ProseMirror content to DOCX format.
70
-
71
- **Parameters:**
72
-
73
- - `content: JSONContent` - TipTap/ProseMirror editor content
74
- - `options: DocxExportOptions` - Export configuration options
75
-
76
- **Returns:** `Promise<OutputByType[T]>` - DOCX file data with type matching the specified outputType
77
-
78
- **Available Output Types:**
79
-
80
- - `"base64"` - Base64 encoded string
81
- - `"string"` - Text string
82
- - `"text"` - Plain text
83
- - `"binarystring"` - Binary string
84
- - `"array"` - Array of numbers
85
- - `"uint8array"` - Uint8Array
86
- - `"arraybuffer"` - ArrayBuffer
87
- - `"blob"` - Blob object
88
- - `"nodebuffer"` - Node.js Buffer
89
-
90
- **Configuration Options:**
91
-
92
- - `title` - Document title
93
- - `creator` - Document author
94
- - `description` - Document description
95
- - `outputType` - Output format (required)
96
- - `table` - Table styling defaults (alignment, spacing, borders)
97
- - `image` - Image handling options
98
- - `styles` - Document default styles (font, line height, spacing)
99
- - `horizontalRule` - Horizontal rule style
100
-
101
- ## Supported Content Types
102
-
103
- ### Text Formatting
104
-
105
- - **Bold**, _Italic_, <u>Underline</u>, ~~Strikethrough~~
106
- - ^Superscript^ and ~Subscript~
107
- - Text colors and background colors
108
- - Font families and sizes
109
- - Line heights
110
-
111
- ### Block Elements
112
-
113
- - **Headings** (H1-H6) with level attribute
114
- - **Paragraphs** with text alignment (left, right, center, justify)
115
- - **Blockquotes** (Note: Exported as indented paragraphs with left border due to DOCX format)
116
- - **Horizontal Rules** (Exported as page breaks by default)
117
- - **Code Blocks** with language support
118
-
119
- ### Lists
120
-
121
- - **Bullet Lists** - Standard unordered lists
122
- - **Numbered Lists** - Ordered lists with custom start number
123
- - **Task Lists** - Checkbox lists with checked/unchecked states
124
-
125
- ### Tables
126
-
127
- - Complete table structure with rows and cells
128
- - **Table Headers** with colspan/rowspan support
129
- - **Table Cells** with colspan/rowspan support
130
- - Cell alignment and formatting options
131
-
132
- ### Media & Embeds
133
-
134
- - **Images** with automatic sizing and positioning
135
- - **Links** (hyperlinks) with href attribute
136
- - **Emoji** rendering
137
- - **Mathematics** formulas (LaTeX-style)
138
- - **Details/Summary** collapsible sections
139
-
140
- ## Examples
141
-
142
- ### Document with Tables and Colspan/Rowspan
143
-
144
- ```typescript
145
- const content = {
146
- type: "doc",
147
- content: [
148
- {
149
- type: "table",
150
- content: [
151
- {
152
- type: "tableRow",
153
- content: [
154
- {
155
- type: "tableHeader",
156
- attrs: { colspan: 2, rowspan: 1 },
157
- content: [
158
- {
159
- type: "paragraph",
160
- content: [{ type: "text", text: "Spanning Header" }],
161
- },
162
- ],
163
- },
164
- {
165
- type: "tableCell",
166
- content: [
167
- {
168
- type: "paragraph",
169
- content: [{ type: "text", text: "Regular Cell" }],
170
- },
171
- ],
172
- },
173
- ],
174
- },
175
- ],
176
- },
177
- ],
178
- };
179
- ```
180
-
181
- ### Document with Text Styles
182
-
183
- ```typescript
184
- const content = {
185
- type: "doc",
186
- content: [
187
- {
188
- type: "paragraph",
189
- content: [
190
- {
191
- type: "text",
192
- marks: [
193
- {
194
- type: "textStyle",
195
- attrs: {
196
- color: "#FF0000",
197
- fontSize: "18px",
198
- fontFamily: "Arial",
199
- backgroundColor: "#FFFF00",
200
- },
201
- },
202
- ],
203
- text: "Red, 18px, Arial text on yellow background",
204
- },
205
- ],
206
- },
207
- ],
208
- };
209
- ```
210
-
211
- ### Document with Lists
212
-
213
- ```typescript
214
- const content = {
215
- type: "doc",
216
- content: [
217
- {
218
- type: "bulletList",
219
- content: [
220
- {
221
- type: "listItem",
222
- content: [
223
- {
224
- type: "paragraph",
225
- content: [{ type: "text", text: "First item" }],
226
- },
227
- ],
228
- },
229
- {
230
- type: "listItem",
231
- content: [
232
- {
233
- type: "paragraph",
234
- content: [{ type: "text", text: "Second item" }],
235
- },
236
- ],
237
- },
238
- ],
239
- },
240
- ],
241
- };
242
- ```
243
-
244
- ## Known Limitations
245
-
246
- ### Blockquote Structure
247
-
248
- DOCX does not have a semantic blockquote structure. Blockquotes are exported as:
249
-
250
- - Indented paragraphs (720 twips / 0.5 inch left indentation)
251
- - Left border (single line)
252
-
253
- This is a DOCX format limitation, not a bug.
254
-
255
- ### Code Marks
256
-
257
- The `code` mark is exported as monospace font (Consolas). When re-importing, it will be recognized as `textStyle` with `fontFamily: "Consolas"`, not as a `code` mark.
258
-
259
- This is intentional - we do not detect code marks from fonts during import to avoid false positives.
260
-
261
- ### Color Name Conversion
262
-
263
- Color names (like `"red"`, `"green"`, `"blue"`) are automatically converted to hex values (`"#FF0000"`, `"#008000"`, `"#0000FF"`) for DOCX compatibility.
264
-
265
- ## Contributing
266
-
267
- Contributions are welcome! Please read our [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) and submit pull requests to the [main repository](https://github.com/DemoMacro/docen).
268
-
269
- ## License
270
-
271
- - [MIT](LICENSE) &copy; [Demo Macro](https://imst.xyz/)
1
+ # @docen/export-docx
2
+
3
+ ![npm version](https://img.shields.io/npm/v/@docen/export-docx)
4
+ ![npm downloads](https://img.shields.io/npm/dw/@docen/export-docx)
5
+ ![npm license](https://img.shields.io/npm/l/@docen/export-docx)
6
+
7
+ > Export TipTap/ProseMirror editor content to Microsoft Word DOCX format.
8
+
9
+ ## Features
10
+
11
+ - 📝 **Rich Text Support** - Full support for headings, paragraphs, and blockquotes with proper formatting
12
+ - 🖼️ **Image Handling** - Automatic image sizing, positioning, and metadata extraction
13
+ - 📊 **Table Support** - Complete table structure with headers, cells, colspan, and rowspan
14
+ - ✅ **Lists & Tasks** - Bullet lists, numbered lists with custom start numbers, and task lists with checkboxes
15
+ - 🎨 **Text Formatting** - Bold, italic, underline, strikethrough, subscript, and superscript
16
+ - 🎯 **Text Styles** - Comprehensive style support including colors, backgrounds, fonts, sizes, and line heights
17
+ - 🔗 **Links** - Hyperlink support with href preservation
18
+ - 💻 **Code Blocks** - Syntax highlighted code blocks with language attribute support
19
+ - 📁 **Collapsible Content** - Details/summary sections for expandable content
20
+ - 😀 **Emoji Support** - Native emoji rendering in documents
21
+ - 🧮 **Mathematical Content** - LaTeX-style formula support
22
+ - ⚙️ **Configurable Options** - Customizable export options for documents, tables, styles, and horizontal rules
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ # Install with npm
28
+ $ npm install @docen/export-docx
29
+
30
+ # Install with yarn
31
+ $ yarn add @docen/export-docx
32
+
33
+ # Install with pnpm
34
+ $ pnpm add @docen/export-docx
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```typescript
40
+ import { generateDOCX } from "@docen/export-docx";
41
+ import { writeFileSync } from "node:fs";
42
+
43
+ // Your TipTap/ProseMirror editor content
44
+ const content = {
45
+ type: "doc",
46
+ content: [
47
+ {
48
+ type: "paragraph",
49
+ content: [
50
+ {
51
+ type: "text",
52
+ marks: [{ type: "bold" }, { type: "italic" }],
53
+ text: "Hello, world!",
54
+ },
55
+ ],
56
+ },
57
+ ],
58
+ };
59
+
60
+ // Convert to DOCX and save to file
61
+ const docx = await generateDOCX(content, { outputType: "nodebuffer" });
62
+ writeFileSync("document.docx", docx);
63
+ ```
64
+
65
+ ## API Reference
66
+
67
+ ### `generateDOCX(content, options)`
68
+
69
+ Converts TipTap/ProseMirror content to DOCX format.
70
+
71
+ **Parameters:**
72
+
73
+ - `content: JSONContent` - TipTap/ProseMirror editor content
74
+ - `options: DocxExportOptions` - Export configuration options
75
+
76
+ **Returns:** `Promise<OutputByType[T]>` - DOCX file data with type matching the specified outputType
77
+
78
+ **Available Output Types:**
79
+
80
+ - `"base64"` - Base64 encoded string
81
+ - `"string"` - Text string
82
+ - `"text"` - Plain text
83
+ - `"binarystring"` - Binary string
84
+ - `"array"` - Array of numbers
85
+ - `"uint8array"` - Uint8Array
86
+ - `"arraybuffer"` - ArrayBuffer
87
+ - `"blob"` - Blob object
88
+ - `"nodebuffer"` - Node.js Buffer
89
+
90
+ **Configuration Options:**
91
+
92
+ - `title` - Document title
93
+ - `creator` - Document author
94
+ - `description` - Document description
95
+ - `outputType` - Output format (required)
96
+ - `table` - Table styling defaults (alignment, spacing, borders)
97
+ - `image` - Image handling options
98
+ - `styles` - Document default styles (font, line height, spacing)
99
+ - `horizontalRule` - Horizontal rule style
100
+
101
+ ## Supported Content Types
102
+
103
+ ### Text Formatting
104
+
105
+ - **Bold**, _Italic_, <u>Underline</u>, ~~Strikethrough~~
106
+ - ^Superscript^ and ~Subscript~
107
+ - Text colors and background colors
108
+ - Font families and sizes
109
+ - Line heights
110
+
111
+ ### Block Elements
112
+
113
+ - **Headings** (H1-H6) with level attribute
114
+ - **Paragraphs** with text alignment (left, right, center, justify)
115
+ - **Blockquotes** (Note: Exported as indented paragraphs with left border due to DOCX format)
116
+ - **Horizontal Rules** (Exported as page breaks by default)
117
+ - **Code Blocks** with language support
118
+
119
+ ### Lists
120
+
121
+ - **Bullet Lists** - Standard unordered lists
122
+ - **Numbered Lists** - Ordered lists with custom start number
123
+ - **Task Lists** - Checkbox lists with checked/unchecked states
124
+
125
+ ### Tables
126
+
127
+ - Complete table structure with rows and cells
128
+ - **Table Headers** with colspan/rowspan support
129
+ - **Table Cells** with colspan/rowspan support
130
+ - Cell alignment and formatting options
131
+
132
+ ### Media & Embeds
133
+
134
+ - **Images** with automatic sizing and positioning
135
+ - **Links** (hyperlinks) with href attribute
136
+ - **Emoji** rendering
137
+ - **Mathematics** formulas (LaTeX-style)
138
+ - **Details/Summary** collapsible sections
139
+
140
+ ## Examples
141
+
142
+ ### Document with Tables and Colspan/Rowspan
143
+
144
+ ```typescript
145
+ const content = {
146
+ type: "doc",
147
+ content: [
148
+ {
149
+ type: "table",
150
+ content: [
151
+ {
152
+ type: "tableRow",
153
+ content: [
154
+ {
155
+ type: "tableHeader",
156
+ attrs: { colspan: 2, rowspan: 1 },
157
+ content: [
158
+ {
159
+ type: "paragraph",
160
+ content: [{ type: "text", text: "Spanning Header" }],
161
+ },
162
+ ],
163
+ },
164
+ {
165
+ type: "tableCell",
166
+ content: [
167
+ {
168
+ type: "paragraph",
169
+ content: [{ type: "text", text: "Regular Cell" }],
170
+ },
171
+ ],
172
+ },
173
+ ],
174
+ },
175
+ ],
176
+ },
177
+ ],
178
+ };
179
+ ```
180
+
181
+ ### Document with Text Styles
182
+
183
+ ```typescript
184
+ const content = {
185
+ type: "doc",
186
+ content: [
187
+ {
188
+ type: "paragraph",
189
+ content: [
190
+ {
191
+ type: "text",
192
+ marks: [
193
+ {
194
+ type: "textStyle",
195
+ attrs: {
196
+ color: "#FF0000",
197
+ fontSize: "18px",
198
+ fontFamily: "Arial",
199
+ backgroundColor: "#FFFF00",
200
+ },
201
+ },
202
+ ],
203
+ text: "Red, 18px, Arial text on yellow background",
204
+ },
205
+ ],
206
+ },
207
+ ],
208
+ };
209
+ ```
210
+
211
+ ### Document with Lists
212
+
213
+ ```typescript
214
+ const content = {
215
+ type: "doc",
216
+ content: [
217
+ {
218
+ type: "bulletList",
219
+ content: [
220
+ {
221
+ type: "listItem",
222
+ content: [
223
+ {
224
+ type: "paragraph",
225
+ content: [{ type: "text", text: "First item" }],
226
+ },
227
+ ],
228
+ },
229
+ {
230
+ type: "listItem",
231
+ content: [
232
+ {
233
+ type: "paragraph",
234
+ content: [{ type: "text", text: "Second item" }],
235
+ },
236
+ ],
237
+ },
238
+ ],
239
+ },
240
+ ],
241
+ };
242
+ ```
243
+
244
+ ## Known Limitations
245
+
246
+ ### Blockquote Structure
247
+
248
+ DOCX does not have a semantic blockquote structure. Blockquotes are exported as:
249
+
250
+ - Indented paragraphs (720 twips / 0.5 inch left indentation)
251
+ - Left border (single line)
252
+
253
+ This is a DOCX format limitation, not a bug.
254
+
255
+ ### Code Marks
256
+
257
+ The `code` mark is exported as monospace font (Consolas). When re-importing, it will be recognized as `textStyle` with `fontFamily: "Consolas"`, not as a `code` mark.
258
+
259
+ This is intentional - we do not detect code marks from fonts during import to avoid false positives.
260
+
261
+ ### Color Name Conversion
262
+
263
+ Color names (like `"red"`, `"green"`, `"blue"`) are automatically converted to hex values (`"#FF0000"`, `"#008000"`, `"#0000FF"`) for DOCX compatibility.
264
+
265
+ ## Contributing
266
+
267
+ Contributions are welcome! Please read our [Contributor Covenant](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) and submit pull requests to the [main repository](https://github.com/DemoMacro/docen).
268
+
269
+ ## License
270
+
271
+ - [MIT](LICENSE) &copy; [Demo Macro](https://imst.xyz/)
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ExternalHyperlink, FileChild, IFloating, IHorizontalPositionOptions, IImageOptions, IParagraphOptions, IParagraphStyleOptions, IPropertiesOptions, ISectionOptions, ITableCellOptions, ITableOfContentsOptions, ITableOptions, ITableRowOptions, IVerticalPositionOptions, ImageRun, OutputByType, OutputType, PositiveUniversalMeasure as PositiveUniversalMeasure$1, Table, TableCell, TableRow, TextRun } from "docx";
1
+ import { ExternalHyperlink, FileChild, IImageOptions, IParagraphOptions, IParagraphStyleOptions, IPropertiesOptions, ISectionOptions, ITableCellOptions, ITableOfContentsOptions, ITableOptions, ITableRowOptions, ImageRun, OutputByType, OutputType, PositiveUniversalMeasure as PositiveUniversalMeasure$1, Table, TableCell, TableRow, TextRun } from "docx";
2
2
  import { ImageMeta } from "image-meta";
3
3
 
4
4
  //#region ../../node_modules/.pnpm/orderedmap@2.1.1/node_modules/orderedmap/dist/index.d.ts
@@ -3672,7 +3672,7 @@ point into textblock nodes. It can be empty (a regular cursor
3672
3672
  position).
3673
3673
  */
3674
3674
  //#endregion
3675
- //#region ../../node_modules/.pnpm/@tiptap+core@3.20.1_@tiptap+pm@3.20.1/node_modules/@tiptap/core/dist/index.d.ts
3675
+ //#region ../../node_modules/.pnpm/@tiptap+core@3.20.0_@tiptap+pm@3.20.0/node_modules/@tiptap/core/dist/index.d.ts
3676
3676
  type StringKeyOf<T> = Extract<keyof T, string>;
3677
3677
  type CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[] ? T[EventName] : [T[EventName]];
3678
3678
  type CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (...props: CallbackType<T, EventName>) => any;
@@ -6630,10 +6630,14 @@ declare function createFloatingOptions(): {
6630
6630
  };
6631
6631
  /**
6632
6632
  * Get image width with priority: node attrs > image meta > calculated > default
6633
+ *
6634
+ * Note: maxWidth constraint only applies to inline (non-floating) images.
6635
+ * Floating images maintain their original dimensions.
6633
6636
  */
6634
6637
  declare function getImageWidth(node: {
6635
6638
  attrs?: {
6636
6639
  width?: number | null;
6640
+ floating?: any;
6637
6641
  };
6638
6642
  }, imageMeta?: {
6639
6643
  width?: number;
@@ -6641,10 +6645,15 @@ declare function getImageWidth(node: {
6641
6645
  }, maxWidth?: number | PositiveUniversalMeasure$1): number;
6642
6646
  /**
6643
6647
  * Get image height with priority: node attrs > image meta > calculated > default
6648
+ *
6649
+ * Note: maxWidth constraint only applies to inline (non-floating) images.
6650
+ * Floating images maintain their original dimensions and aspect ratio.
6644
6651
  */
6645
6652
  declare function getImageHeight(node: {
6646
6653
  attrs?: {
6647
6654
  height?: number | null;
6655
+ width?: number | null;
6656
+ floating?: any;
6648
6657
  };
6649
6658
  }, width: number, imageMeta?: {
6650
6659
  width?: number;
@@ -6754,12 +6763,225 @@ declare function convertDocument(node: JSONContent, params: {
6754
6763
  declare function convertNode(node: JSONContent, options: DocxExportOptions, effectiveContentWidth: number): Promise<FileChild | FileChild[] | null>;
6755
6764
  //#endregion
6756
6765
  //#region ../extensions/dist/types.d.mts
6766
+ //#region ../../node_modules/.pnpm/docx@9.6.1/node_modules/docx/dist/index.d.ts
6767
+ declare const CompoundLine: {
6768
+ readonly SINGLE: "sng";
6769
+ readonly DOUBLE: "dbl";
6770
+ readonly THICK_THIN: "thickThin";
6771
+ readonly THIN_THICK: "thinThick";
6772
+ readonly TRI: "tri";
6773
+ };
6774
+ declare type CoreImageOptions = {
6775
+ readonly transformation: IMediaTransformation;
6776
+ readonly floating?: IFloating;
6777
+ readonly altText?: DocPropertiesOptions;
6778
+ readonly outline?: OutlineOptions;
6779
+ readonly solidFill?: SolidFillOptions;
6780
+ };
6781
+ declare type DocPropertiesOptions = {
6782
+ readonly name: string;
6783
+ readonly description?: string;
6784
+ readonly title?: string;
6785
+ readonly id?: string;
6786
+ };
6787
+ declare const HorizontalPositionAlign: {
6788
+ readonly CENTER: "center";
6789
+ readonly INSIDE: "inside";
6790
+ readonly LEFT: "left";
6791
+ readonly OUTSIDE: "outside";
6792
+ readonly RIGHT: "right";
6793
+ };
6794
+ declare const HorizontalPositionRelativeFrom: {
6795
+ readonly CHARACTER: "character";
6796
+ readonly COLUMN: "column";
6797
+ readonly INSIDE_MARGIN: "insideMargin";
6798
+ readonly LEFT_MARGIN: "leftMargin";
6799
+ readonly MARGIN: "margin";
6800
+ readonly OUTSIDE_MARGIN: "outsideMargin";
6801
+ readonly PAGE: "page";
6802
+ readonly RIGHT_MARGIN: "rightMargin";
6803
+ };
6804
+ declare type IDistance = {
6805
+ readonly distT?: number;
6806
+ readonly distB?: number;
6807
+ readonly distL?: number;
6808
+ readonly distR?: number;
6809
+ };
6810
+ declare type IFloating = {
6811
+ readonly horizontalPosition: IHorizontalPositionOptions;
6812
+ readonly verticalPosition: IVerticalPositionOptions;
6813
+ readonly allowOverlap?: boolean;
6814
+ readonly lockAnchor?: boolean;
6815
+ readonly behindDocument?: boolean;
6816
+ readonly layoutInCell?: boolean;
6817
+ readonly margins?: IMargins;
6818
+ readonly wrap?: ITextWrapping;
6819
+ readonly zIndex?: number;
6820
+ };
6821
+ declare type IHorizontalPositionOptions = {
6822
+ readonly relative?: (typeof HorizontalPositionRelativeFrom)[keyof typeof HorizontalPositionRelativeFrom];
6823
+ readonly align?: (typeof HorizontalPositionAlign)[keyof typeof HorizontalPositionAlign];
6824
+ readonly offset?: number;
6825
+ };
6826
+ declare type IImageOptions$1 = (RegularImageOptions | SvgMediaOptions) & CoreImageOptions;
6827
+ declare type IMargins = {
6828
+ readonly left?: number;
6829
+ readonly bottom?: number;
6830
+ readonly top?: number;
6831
+ readonly right?: number;
6832
+ };
6833
+ declare type IMediaTransformation = {
6834
+ readonly offset?: {
6835
+ readonly top?: number;
6836
+ readonly left?: number;
6837
+ };
6838
+ readonly width: number;
6839
+ readonly height: number;
6840
+ readonly flip?: {
6841
+ readonly vertical?: boolean;
6842
+ readonly horizontal?: boolean;
6843
+ };
6844
+ readonly rotation?: number;
6845
+ };
6846
+ declare type ITextWrapping = {
6847
+ readonly type: (typeof TextWrappingType)[keyof typeof TextWrappingType];
6848
+ readonly side?: (typeof TextWrappingSide)[keyof typeof TextWrappingSide];
6849
+ readonly margins?: IDistance;
6850
+ };
6851
+ declare type IVerticalPositionOptions = {
6852
+ readonly relative?: (typeof VerticalPositionRelativeFrom)[keyof typeof VerticalPositionRelativeFrom];
6853
+ readonly align?: (typeof VerticalPositionAlign)[keyof typeof VerticalPositionAlign];
6854
+ readonly offset?: number;
6855
+ };
6856
+ declare const LineCap: {
6857
+ readonly ROUND: "rnd";
6858
+ readonly SQUARE: "sq";
6859
+ readonly FLAT: "flat";
6860
+ };
6861
+ declare type OutlineAttributes = {
6862
+ readonly width?: number;
6863
+ readonly cap?: keyof typeof LineCap;
6864
+ readonly compoundLine?: keyof typeof CompoundLine;
6865
+ readonly align?: keyof typeof PenAlignment;
6866
+ };
6867
+ declare type OutlineFillProperties = OutlineNoFill | OutlineSolidFill;
6868
+ declare type OutlineNoFill = {
6869
+ readonly type: "noFill";
6870
+ };
6871
+ declare type OutlineOptions = OutlineAttributes & OutlineFillProperties;
6872
+ declare type OutlineRgbSolidFill = {
6873
+ readonly type: "solidFill";
6874
+ readonly solidFillType: "rgb";
6875
+ readonly value: string;
6876
+ };
6877
+ declare type OutlineSchemeSolidFill = {
6878
+ readonly type: "solidFill";
6879
+ readonly solidFillType: "scheme";
6880
+ readonly value: (typeof SchemeColor)[keyof typeof SchemeColor];
6881
+ };
6882
+ declare type OutlineSolidFill = OutlineRgbSolidFill | OutlineSchemeSolidFill;
6883
+ declare const PenAlignment: {
6884
+ readonly CENTER: "ctr";
6885
+ readonly INSET: "in";
6886
+ };
6887
+ declare type RegularImageOptions = {
6888
+ readonly type: "jpg" | "png" | "gif" | "bmp";
6889
+ readonly data: Buffer | string | Uint8Array | ArrayBuffer;
6890
+ };
6891
+ declare type RgbColorOptions = {
6892
+ readonly type: "rgb";
6893
+ readonly value: string;
6894
+ };
6895
+ declare const SchemeColor: {
6896
+ readonly BG1: "bg1";
6897
+ readonly TX1: "tx1";
6898
+ readonly BG2: "bg2";
6899
+ readonly TX2: "tx2";
6900
+ readonly ACCENT1: "accent1";
6901
+ readonly ACCENT2: "accent2";
6902
+ readonly ACCENT3: "accent3";
6903
+ readonly ACCENT4: "accent4";
6904
+ readonly ACCENT5: "accent5";
6905
+ readonly ACCENT6: "accent6";
6906
+ readonly HLINK: "hlink";
6907
+ readonly FOLHLINK: "folHlink";
6908
+ readonly DK1: "dk1";
6909
+ readonly LT1: "lt1";
6910
+ readonly DK2: "dk2";
6911
+ readonly LT2: "lt2";
6912
+ readonly PHCLR: "phClr";
6913
+ };
6914
+ declare type SchemeColorOptions = {
6915
+ readonly type: "scheme";
6916
+ readonly value: (typeof SchemeColor)[keyof typeof SchemeColor];
6917
+ };
6918
+ declare type SolidFillOptions = RgbColorOptions | SchemeColorOptions;
6919
+ declare type SvgMediaOptions = {
6920
+ readonly type: "svg";
6921
+ readonly data: Buffer | string | Uint8Array | ArrayBuffer;
6922
+ readonly fallback: RegularImageOptions;
6923
+ };
6924
+ declare const TextWrappingSide: {
6925
+ readonly BOTH_SIDES: "bothSides";
6926
+ readonly LEFT: "left";
6927
+ readonly RIGHT: "right";
6928
+ readonly LARGEST: "largest";
6929
+ };
6930
+ declare const TextWrappingType: {
6931
+ readonly NONE: 0;
6932
+ readonly SQUARE: 1;
6933
+ readonly TIGHT: 2;
6934
+ readonly TOP_AND_BOTTOM: 3;
6935
+ };
6936
+ declare const VerticalPositionAlign: {
6937
+ readonly BOTTOM: "bottom";
6938
+ readonly CENTER: "center";
6939
+ readonly INSIDE: "inside";
6940
+ readonly OUTSIDE: "outside";
6941
+ readonly TOP: "top";
6942
+ };
6943
+ declare const VerticalPositionRelativeFrom: {
6944
+ readonly BOTTOM_MARGIN: "bottomMargin";
6945
+ readonly INSIDE_MARGIN: "insideMargin";
6946
+ readonly LINE: "line";
6947
+ readonly MARGIN: "margin";
6948
+ readonly OUTSIDE_MARGIN: "outsideMargin";
6949
+ readonly PAGE: "page";
6950
+ readonly PARAGRAPH: "paragraph";
6951
+ readonly TOP_MARGIN: "topMargin";
6952
+ }; //#endregion
6757
6953
  //#region src/types.d.ts
6954
+ /**
6955
+ * Border definition (compatible with docx.js BorderOptions)
6956
+ * Used by paragraphs, table cells, and blockquotes
6957
+ */
6958
+ interface Border {
6959
+ /** Border color (hex without #, e.g., "FF0000" or "auto") */
6960
+ color?: string;
6961
+ /** Border size (eighth-points, 1/8 pt) */
6962
+ size?: number;
6963
+ /** Border style */
6964
+ style?: "single" | "dashed" | "dotted" | "double" | "dotDash" | "dotDotDash" | "none";
6965
+ /** Space between border and content (points) */
6966
+ space?: number;
6967
+ }
6968
+ /**
6969
+ * Shading definition (compatible with docx.js ShadingOptions)
6970
+ * Used for paragraph and table cell background colors
6971
+ */
6972
+ interface Shading {
6973
+ /** Fill color (hex without #, e.g., "FF0000") */
6974
+ fill?: string;
6975
+ /** Pattern color (hex without #) */
6976
+ color?: string;
6977
+ /** Shading pattern type (e.g., "clear", "percent-10") */
6978
+ type?: string;
6979
+ }
6758
6980
  type ImageFloatingOptions = {
6759
6981
  horizontalPosition: IHorizontalPositionOptions;
6760
6982
  verticalPosition: IVerticalPositionOptions;
6761
6983
  } & Partial<Omit<IFloating, "horizontalPosition" | "verticalPosition">>;
6762
- type ImageOutlineOptions = IImageOptions["outline"];
6984
+ type ImageOutlineOptions = IImageOptions$1["outline"];
6763
6985
  interface TextNode {
6764
6986
  type: "text";
6765
6987
  text: string;
@@ -6797,6 +7019,11 @@ interface ParagraphNode extends JSONContent {
6797
7019
  indentFirstLine?: string;
6798
7020
  spacingBefore?: string;
6799
7021
  spacingAfter?: string;
7022
+ shading?: Shading;
7023
+ borderTop?: Border;
7024
+ borderBottom?: Border;
7025
+ borderLeft?: Border;
7026
+ borderRight?: Border;
6800
7027
  };
6801
7028
  content?: Array<TextNode | HardBreakNode | ImageNode>;
6802
7029
  }
@@ -6810,6 +7037,11 @@ interface HeadingNode extends JSONContent {
6810
7037
  spacingBefore?: string;
6811
7038
  spacingAfter?: string;
6812
7039
  textAlign?: "left" | "right" | "center" | "justify";
7040
+ shading?: Shading;
7041
+ borderTop?: Border;
7042
+ borderBottom?: Border;
7043
+ borderLeft?: Border;
7044
+ borderRight?: Border;
6813
7045
  };
6814
7046
  content?: Array<TextNode | HardBreakNode>;
6815
7047
  }
@@ -6855,11 +7087,6 @@ interface TaskItemNode extends JSONContent {
6855
7087
  };
6856
7088
  content?: Array<ParagraphNode>;
6857
7089
  }
6858
- interface TableCellBorder {
6859
- color?: string;
6860
- width?: number;
6861
- style?: "solid" | "dashed" | "dotted" | "double" | "none";
6862
- }
6863
7090
  interface TableNode extends JSONContent {
6864
7091
  type: "table";
6865
7092
  attrs?: {
@@ -6885,10 +7112,10 @@ interface TableCellNode extends JSONContent {
6885
7112
  colwidth?: number[] | null;
6886
7113
  backgroundColor?: string | null;
6887
7114
  verticalAlign?: "top" | "middle" | "bottom" | null;
6888
- borderTop?: TableCellBorder | null;
6889
- borderBottom?: TableCellBorder | null;
6890
- borderLeft?: TableCellBorder | null;
6891
- borderRight?: TableCellBorder | null;
7115
+ borderTop?: Border;
7116
+ borderBottom?: Border;
7117
+ borderLeft?: Border;
7118
+ borderRight?: Border;
6892
7119
  };
6893
7120
  content?: Array<ParagraphNode>;
6894
7121
  }
@@ -6900,10 +7127,10 @@ interface TableHeaderNode extends JSONContent {
6900
7127
  colwidth?: number[] | null;
6901
7128
  backgroundColor?: string | null;
6902
7129
  verticalAlign?: "top" | "middle" | "bottom" | null;
6903
- borderTop?: TableCellBorder | null;
6904
- borderBottom?: TableCellBorder | null;
6905
- borderLeft?: TableCellBorder | null;
6906
- borderRight?: TableCellBorder | null;
7130
+ borderTop?: Border;
7131
+ borderBottom?: Border;
7132
+ borderLeft?: Border;
7133
+ borderRight?: Border;
6907
7134
  };
6908
7135
  content?: Array<ParagraphNode>;
6909
7136
  }
@@ -7623,26 +7850,30 @@ declare function createStringValidator<T extends string>(validValues: readonly T
7623
7850
  * Calculate effective content width from document options
7624
7851
  */
7625
7852
  declare function calculateEffectiveContentWidth(options?: DocxExportOptions): number;
7626
- //#endregion
7627
- //#region src/utils/paragraph.d.ts
7628
7853
  /**
7629
- * Apply paragraph style attributes to options
7854
+ * Convert Border to docx.js format
7630
7855
  */
7631
- declare const applyParagraphStyleAttributes: <T extends Record<string, unknown>>(options: T, attrs?: ParagraphNode["attrs"]) => T;
7632
- //#endregion
7633
- //#region src/utils/table.d.ts
7856
+ declare function convertBorder(border?: Border): {
7857
+ color?: string;
7858
+ size?: number;
7859
+ style?: string;
7860
+ space?: number;
7861
+ } | undefined;
7634
7862
  /**
7635
- * Convert TipTap border to DOCX border format
7636
- *
7637
- * @param border - TipTap table cell border definition
7638
- * @returns DOCX border options or undefined if no border
7863
+ * Convert Shading to docx.js format
7639
7864
  */
7640
- declare function convertBorder(border: TableCellBorder | null | undefined): {
7865
+ declare function convertShading(shading?: Shading): {
7866
+ fill?: string;
7641
7867
  color?: string;
7642
- size?: number;
7643
- style: "single" | "dashed" | "dotted" | "double" | "none";
7868
+ val?: string;
7644
7869
  } | undefined;
7645
7870
  //#endregion
7871
+ //#region src/utils/paragraph.d.ts
7872
+ /**
7873
+ * Apply paragraph style attributes to options
7874
+ */
7875
+ declare const applyParagraphStyleAttributes: <T extends Record<string, unknown>>(options: T, attrs?: ParagraphNode["attrs"]) => T;
7876
+ //#endregion
7646
7877
  //#region src/converters/image.d.ts
7647
7878
  /**
7648
7879
  * Convert TipTap image node to DOCX ImageRun
@@ -7816,4 +8047,4 @@ declare function convertDetailsSummary(node: DetailsSummaryNode, params: {
7816
8047
  /** Export options for details styling */options?: DocxExportOptions["details"];
7817
8048
  }): IParagraphOptions;
7818
8049
  //#endregion
7819
- export { BlockNode, BlockquoteNode, BulletListNode, CHECKBOX_SYMBOLS, COLOR_NAME_TO_HEX, CodeBlockNode, DEFAULT_CODE_FONT, DOCX_DPI, DOCX_STYLE_NAMES, DetailsContentNode, DetailsNode, DetailsSummaryNode, DocumentNode, DocxExportOptions, DocxImageExportHandler, EMUS_PER_INCH, HALF_POINTS_PER_PIXEL, HardBreakNode, HeadingNode, HorizontalRuleNode, ImageFloatingOptions, ImageNode, ImageOutlineOptions, type JSONContent, ListItemNode, ListOptions, Mark, OrderedListNode, PAGE_DIMENSIONS, PIXELS_PER_HALF_POINT, ParagraphNode, PositiveUniversalMeasure, TEXT_ALIGN_MAP, TWIPS_PER_INCH, TableCellBorder, TableCellNode, TableHeaderNode, TableNode, TableRowNode, TaskItemNode, TaskListNode, TextContent, TextNode, applyParagraphStyleAttributes, applyTableMargins, calculateEffectiveContentWidth, convertBlockquote, convertBorder, convertBulletList, convertCodeBlock, convertColorToHex, convertCssLengthToPixels, convertDetailsSummary, convertDocument, convertEmuStringToPixels, convertEmuToPixels, convertHardBreak, convertHeading, convertHorizontalRule, convertImage, convertList, convertListItem, convertMeasureToInches, convertMeasureToPixels, convertNode, convertOrderedList, convertParagraph, convertPixelsToEmu, convertPixelsToTwip, convertTable, convertTableCell, convertTableHeader, convertTableRow, convertTaskItem, convertTaskList, convertText, convertTextNodes, convertToDocxImageType, convertTwipToCssString, convertTwipToPixels, createFloatingOptions, createStringValidator, findChild, findDeepChild, findDeepChildren, generateDOCX, getImageDataAndMeta, getImageHeight, getImageTypeFromSrc, getImageWidth, normalizeHexColor, parseTwipAttr };
8050
+ export { BlockNode, BlockquoteNode, Border, BulletListNode, CHECKBOX_SYMBOLS, COLOR_NAME_TO_HEX, CodeBlockNode, DEFAULT_CODE_FONT, DOCX_DPI, DOCX_STYLE_NAMES, DetailsContentNode, DetailsNode, DetailsSummaryNode, DocumentNode, DocxExportOptions, DocxImageExportHandler, EMUS_PER_INCH, HALF_POINTS_PER_PIXEL, HardBreakNode, HeadingNode, HorizontalRuleNode, ImageFloatingOptions, ImageNode, ImageOutlineOptions, type JSONContent, ListItemNode, ListOptions, Mark, OrderedListNode, PAGE_DIMENSIONS, PIXELS_PER_HALF_POINT, ParagraphNode, PositiveUniversalMeasure, Shading, TEXT_ALIGN_MAP, TWIPS_PER_INCH, TableCellNode, TableHeaderNode, TableNode, TableRowNode, TaskItemNode, TaskListNode, TextContent, TextNode, applyParagraphStyleAttributes, applyTableMargins, calculateEffectiveContentWidth, convertBlockquote, convertBorder, convertBulletList, convertCodeBlock, convertColorToHex, convertCssLengthToPixels, convertDetailsSummary, convertDocument, convertEmuStringToPixels, convertEmuToPixels, convertHardBreak, convertHeading, convertHorizontalRule, convertImage, convertList, convertListItem, convertMeasureToInches, convertMeasureToPixels, convertNode, convertOrderedList, convertParagraph, convertPixelsToEmu, convertPixelsToTwip, convertShading, convertTable, convertTableCell, convertTableHeader, convertTableRow, convertTaskItem, convertTaskList, convertText, convertTextNodes, convertToDocxImageType, convertTwipToCssString, convertTwipToPixels, createFloatingOptions, createStringValidator, findChild, findDeepChild, findDeepChildren, generateDOCX, getImageDataAndMeta, getImageHeight, getImageTypeFromSrc, getImageWidth, normalizeHexColor, parseTwipAttr };
package/dist/index.mjs CHANGED
@@ -438,6 +438,29 @@ function calculateEffectiveContentWidth(options) {
438
438
  const effectiveWidth = pageWidth - marginLeft - marginRight;
439
439
  return Math.max(convertTwipToPixels(effectiveWidth), 96);
440
440
  }
441
+ /**
442
+ * Convert Border to docx.js format
443
+ */
444
+ function convertBorder(border) {
445
+ if (!border) return void 0;
446
+ const docxBorder = {};
447
+ if (border.color) docxBorder.color = border.color.replace("#", "");
448
+ if (border.size !== void 0) docxBorder.size = border.size;
449
+ if (border.style) docxBorder.style = border.style;
450
+ if (border.space !== void 0) docxBorder.space = border.space;
451
+ return Object.keys(docxBorder).length > 0 ? docxBorder : void 0;
452
+ }
453
+ /**
454
+ * Convert Shading to docx.js format
455
+ */
456
+ function convertShading(shading) {
457
+ if (!shading || !shading.fill) return void 0;
458
+ const docxShading = {};
459
+ if (shading.fill) docxShading.fill = shading.fill.replace("#", "");
460
+ if (shading.color) docxShading.color = shading.color.replace("#", "");
461
+ docxShading.val = shading.type || "clear";
462
+ return docxShading;
463
+ }
441
464
  //#endregion
442
465
  //#region src/utils/image.ts
443
466
  const DEFAULT_MAX_IMAGE_WIDTH_PIXELS = 6.5 * 96;
@@ -525,18 +548,42 @@ function createFloatingOptions() {
525
548
  }
526
549
  /**
527
550
  * Get image width with priority: node attrs > image meta > calculated > default
551
+ *
552
+ * Note: maxWidth constraint only applies to inline (non-floating) images.
553
+ * Floating images maintain their original dimensions.
528
554
  */
529
555
  function getImageWidth(node, imageMeta, maxWidth) {
530
- if (node.attrs?.width !== void 0 && node.attrs?.width !== null) return node.attrs.width;
556
+ if (node.attrs?.width !== void 0 && node.attrs?.width !== null) {
557
+ const requestedWidth = node.attrs.width;
558
+ if (!node.attrs.floating && maxWidth) {
559
+ const maxWidthPixels = maxWidth !== void 0 ? convertMeasureToPixels(maxWidth) : void 0;
560
+ if (maxWidthPixels && requestedWidth > maxWidthPixels) return maxWidthPixels;
561
+ }
562
+ return requestedWidth;
563
+ }
531
564
  const maxWidthPixels = maxWidth !== void 0 ? convertMeasureToPixels(maxWidth) : void 0;
532
565
  if (imageMeta?.width && imageMeta?.height) return calculateDisplaySize(imageMeta, maxWidthPixels).width;
533
566
  return maxWidthPixels || DEFAULT_MAX_IMAGE_WIDTH_PIXELS;
534
567
  }
535
568
  /**
536
569
  * Get image height with priority: node attrs > image meta > calculated > default
570
+ *
571
+ * Note: maxWidth constraint only applies to inline (non-floating) images.
572
+ * Floating images maintain their original dimensions and aspect ratio.
537
573
  */
538
574
  function getImageHeight(node, width, imageMeta, maxWidth) {
539
- if (node.attrs?.height !== void 0 && node.attrs?.height !== null) return node.attrs.height;
575
+ if (node.attrs?.height !== void 0 && node.attrs?.height !== null) {
576
+ const requestedHeight = node.attrs.height;
577
+ if (!node.attrs.floating && maxWidth && node.attrs?.width) {
578
+ const maxWidthPixels = maxWidth !== void 0 ? convertMeasureToPixels(maxWidth) : void 0;
579
+ const requestedWidth = node.attrs.width;
580
+ if (maxWidthPixels && requestedWidth > maxWidthPixels) {
581
+ const scaleFactor = maxWidthPixels / requestedWidth;
582
+ return Math.round(requestedHeight * scaleFactor);
583
+ }
584
+ }
585
+ return requestedHeight;
586
+ }
540
587
  const maxWidthPixels = maxWidth !== void 0 ? convertMeasureToPixels(maxWidth) : void 0;
541
588
  if (imageMeta?.width && imageMeta?.height) return calculateDisplaySize(imageMeta, maxWidthPixels).height;
542
589
  return Math.round(width * .75);
@@ -576,52 +623,27 @@ async function getImageDataAndMeta(url) {
576
623
  */
577
624
  const applyParagraphStyleAttributes = (options, attrs) => {
578
625
  if (!attrs) return options;
579
- let result = { ...options };
580
- if (attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine) result = {
581
- ...result,
582
- indent: {
583
- ...attrs.indentLeft && { left: convertPixelsToTwip(convertCssLengthToPixels(attrs.indentLeft)) },
584
- ...attrs.indentRight && { right: convertPixelsToTwip(convertCssLengthToPixels(attrs.indentRight)) },
585
- ...attrs.indentFirstLine && { firstLine: convertPixelsToTwip(convertCssLengthToPixels(attrs.indentFirstLine)) }
586
- }
626
+ const result = { ...options };
627
+ if (attrs.indentLeft || attrs.indentRight || attrs.indentFirstLine) result.indent = {
628
+ ...attrs.indentLeft && { left: convertPixelsToTwip(convertCssLengthToPixels(attrs.indentLeft)) },
629
+ ...attrs.indentRight && { right: convertPixelsToTwip(convertCssLengthToPixels(attrs.indentRight)) },
630
+ ...attrs.indentFirstLine && { firstLine: convertPixelsToTwip(convertCssLengthToPixels(attrs.indentFirstLine)) }
587
631
  };
588
- if (attrs.spacingBefore || attrs.spacingAfter) result = {
589
- ...result,
590
- spacing: {
591
- ...attrs.spacingBefore && { before: convertPixelsToTwip(convertCssLengthToPixels(attrs.spacingBefore)) },
592
- ...attrs.spacingAfter && { after: convertPixelsToTwip(convertCssLengthToPixels(attrs.spacingAfter)) }
593
- }
632
+ if (attrs.spacingBefore || attrs.spacingAfter) result.spacing = {
633
+ ...attrs.spacingBefore && { before: convertPixelsToTwip(convertCssLengthToPixels(attrs.spacingBefore)) },
634
+ ...attrs.spacingAfter && { after: convertPixelsToTwip(convertCssLengthToPixels(attrs.spacingAfter)) }
594
635
  };
595
- if (attrs.textAlign) result = {
596
- ...result,
597
- alignment: TEXT_ALIGN_MAP.tiptapToDocx[attrs.textAlign]
636
+ if (attrs.textAlign) result.alignment = TEXT_ALIGN_MAP.tiptapToDocx[attrs.textAlign];
637
+ if (attrs.shading) result.shading = convertShading(attrs.shading);
638
+ if (attrs.borderTop || attrs.borderBottom || attrs.borderLeft || attrs.borderRight) result.border = {
639
+ ...attrs.borderTop && { top: convertBorder(attrs.borderTop) },
640
+ ...attrs.borderBottom && { bottom: convertBorder(attrs.borderBottom) },
641
+ ...attrs.borderLeft && { left: convertBorder(attrs.borderLeft) },
642
+ ...attrs.borderRight && { right: convertBorder(attrs.borderRight) }
598
643
  };
599
644
  return result;
600
645
  };
601
646
  //#endregion
602
- //#region src/utils/table.ts
603
- /**
604
- * Convert TipTap border to DOCX border format
605
- *
606
- * @param border - TipTap table cell border definition
607
- * @returns DOCX border options or undefined if no border
608
- */
609
- function convertBorder(border) {
610
- if (!border) return void 0;
611
- const docxStyle = border.style ? {
612
- solid: "single",
613
- dashed: "dashed",
614
- dotted: "dotted",
615
- double: "double",
616
- none: "none"
617
- }[border.style] || "single" : "single";
618
- return {
619
- color: border.color?.replace("#", "") || "auto",
620
- size: border.width ? border.width * 6 : 4,
621
- style: docxStyle
622
- };
623
- }
624
- //#endregion
625
647
  //#region src/converters/text.ts
626
648
  /**
627
649
  * Convert TipTap text node to DOCX TextRun or ExternalHyperlink
@@ -909,13 +931,16 @@ async function convertTableCell(node, params) {
909
931
  }
910
932
  if (node.attrs?.backgroundColor) cellOptions.shading = { fill: node.attrs.backgroundColor.replace("#", "") };
911
933
  if (node.attrs?.verticalAlign) cellOptions.verticalAlign = node.attrs.verticalAlign === "middle" ? "center" : node.attrs.verticalAlign;
912
- const borders = {
913
- top: convertBorder(node.attrs?.borderTop),
914
- bottom: convertBorder(node.attrs?.borderBottom),
915
- left: convertBorder(node.attrs?.borderLeft),
916
- right: convertBorder(node.attrs?.borderRight)
917
- };
918
- if (borders.top || borders.bottom || borders.left || borders.right) cellOptions.borders = borders;
934
+ const borders = {};
935
+ const top = convertBorder(node.attrs?.borderTop);
936
+ if (top) borders.top = top;
937
+ const bottom = convertBorder(node.attrs?.borderBottom);
938
+ if (bottom) borders.bottom = bottom;
939
+ const left = convertBorder(node.attrs?.borderLeft);
940
+ if (left) borders.left = left;
941
+ const right = convertBorder(node.attrs?.borderRight);
942
+ if (right) borders.right = right;
943
+ if (Object.keys(borders).length > 0) cellOptions.borders = borders;
919
944
  return new TableCell(cellOptions);
920
945
  }
921
946
  //#endregion
@@ -949,13 +974,16 @@ async function convertTableHeader(node, params) {
949
974
  }
950
975
  if (node.attrs?.backgroundColor) headerCellOptions.shading = { fill: node.attrs.backgroundColor.replace("#", "") };
951
976
  if (node.attrs?.verticalAlign) headerCellOptions.verticalAlign = node.attrs.verticalAlign === "middle" ? "center" : node.attrs.verticalAlign;
952
- const borders = {
953
- top: convertBorder(node.attrs?.borderTop),
954
- bottom: convertBorder(node.attrs?.borderBottom),
955
- left: convertBorder(node.attrs?.borderLeft),
956
- right: convertBorder(node.attrs?.borderRight)
957
- };
958
- if (borders.top || borders.bottom || borders.left || borders.right) headerCellOptions.borders = borders;
977
+ const borders = {};
978
+ const top = convertBorder(node.attrs?.borderTop);
979
+ if (top) borders.top = top;
980
+ const bottom = convertBorder(node.attrs?.borderBottom);
981
+ if (bottom) borders.bottom = bottom;
982
+ const left = convertBorder(node.attrs?.borderLeft);
983
+ if (left) borders.left = left;
984
+ const right = convertBorder(node.attrs?.borderRight);
985
+ if (right) borders.right = right;
986
+ if (Object.keys(borders).length > 0) headerCellOptions.borders = borders;
959
987
  return new TableCell(headerCellOptions);
960
988
  }
961
989
  //#endregion
@@ -1016,6 +1044,7 @@ async function convertTable(node, params) {
1016
1044
  const { options } = params;
1017
1045
  let tableOptions = {
1018
1046
  rows: await Promise.all((node.content || []).map((row) => convertTableRow(row, params))),
1047
+ ...options?.style?.id && { style: options.style.id },
1019
1048
  ...options?.run
1020
1049
  };
1021
1050
  tableOptions = applyTableMargins(tableOptions, node);
@@ -1239,11 +1268,13 @@ async function generateDOCX(docJson, options) {
1239
1268
  * Convert document content to DOCX elements
1240
1269
  */
1241
1270
  async function convertDocument(node, params) {
1242
- const elements = [];
1243
- if (!node || !Array.isArray(node.content)) return elements;
1271
+ if (!node || !Array.isArray(node.content)) return [];
1244
1272
  const effectiveContentWidth = calculateEffectiveContentWidth(params.options);
1245
- for (const childNode of node.content) {
1246
- const element = await convertNode(childNode, params.options, effectiveContentWidth);
1273
+ const convertedElements = await Promise.all(node.content.map((childNode) => convertNode(childNode, params.options, effectiveContentWidth)));
1274
+ const elements = [];
1275
+ for (let i = 0; i < convertedElements.length; i++) {
1276
+ const element = convertedElements[i];
1277
+ const childNode = node.content[i];
1247
1278
  if (Array.isArray(element)) elements.push(...element);
1248
1279
  else if (element) {
1249
1280
  elements.push(element);
@@ -1267,10 +1298,16 @@ async function convertNode(node, options, effectiveContentWidth) {
1267
1298
  if (Array.isArray(dataResult)) {
1268
1299
  const styleId = getStyleIdByNodeType(node.type, options);
1269
1300
  return dataResult.map((paragraphOptions) => {
1301
+ if (!styleId) return new Paragraph(paragraphOptions);
1270
1302
  return new Paragraph(applyStyleReference(paragraphOptions, styleId));
1271
1303
  });
1272
1304
  }
1273
- return createDOCXObject(applyStyleReference(dataResult, getStyleIdByNodeType(node.type, options)));
1305
+ let styleId = getStyleIdByNodeType(node.type, options);
1306
+ if (!styleId && node.type === "paragraph" && node.content) {
1307
+ if (node.content.length > 0 && node.content.every((child) => child.type === "image")) styleId = options.image?.style?.id;
1308
+ }
1309
+ if (!styleId) return createDOCXObject(dataResult);
1310
+ return createDOCXObject(applyStyleReference(dataResult, styleId));
1274
1311
  }
1275
1312
  /**
1276
1313
  * Layer 1: Data Transformation
@@ -1289,11 +1326,11 @@ async function convertNodeData(node, options, effectiveContentWidth) {
1289
1326
  case "heading": return convertHeading(node);
1290
1327
  case "blockquote": return convertBlockquote(node);
1291
1328
  case "codeBlock": return convertCodeBlock(node);
1292
- case "image": return { children: [await convertImage(node, {
1329
+ case "image": return applyStyleReference({ children: [await convertImage(node, {
1293
1330
  maxWidth: effectiveContentWidth,
1294
1331
  options: options.image?.run,
1295
1332
  handler: options.image?.handler
1296
- })] };
1333
+ })] }, getStyleIdByNodeType("image", options));
1297
1334
  case "table": return await convertTable(node, { options: options.table });
1298
1335
  case "bulletList": return await convertList(node, { listType: "bullet" });
1299
1336
  case "orderedList": return await convertList(node, { listType: "ordered" });
@@ -1414,4 +1451,4 @@ function createDOCXObject(options) {
1414
1451
  return new Paragraph(options);
1415
1452
  }
1416
1453
  //#endregion
1417
- export { CHECKBOX_SYMBOLS, COLOR_NAME_TO_HEX, DEFAULT_CODE_FONT, DOCX_DPI, DOCX_STYLE_NAMES, EMUS_PER_INCH, HALF_POINTS_PER_PIXEL, PAGE_DIMENSIONS, PIXELS_PER_HALF_POINT, TEXT_ALIGN_MAP, TWIPS_PER_INCH, applyParagraphStyleAttributes, applyTableMargins, calculateEffectiveContentWidth, convertBlockquote, convertBorder, convertBulletList, convertCodeBlock, convertColorToHex, convertCssLengthToPixels, convertDetailsSummary, convertDocument, convertEmuStringToPixels, convertEmuToPixels, convertHardBreak, convertHeading, convertHorizontalRule, convertImage, convertList, convertListItem, convertMeasureToInches, convertMeasureToPixels, convertNode, convertOrderedList, convertParagraph, convertPixelsToEmu, convertPixelsToTwip, convertTable, convertTableCell, convertTableHeader, convertTableRow, convertTaskItem, convertTaskList, convertText, convertTextNodes, convertToDocxImageType, convertTwipToCssString, convertTwipToPixels, createFloatingOptions, createStringValidator, findChild, findDeepChild, findDeepChildren, generateDOCX, getImageDataAndMeta, getImageHeight, getImageTypeFromSrc, getImageWidth, normalizeHexColor, parseTwipAttr };
1454
+ export { CHECKBOX_SYMBOLS, COLOR_NAME_TO_HEX, DEFAULT_CODE_FONT, DOCX_DPI, DOCX_STYLE_NAMES, EMUS_PER_INCH, HALF_POINTS_PER_PIXEL, PAGE_DIMENSIONS, PIXELS_PER_HALF_POINT, TEXT_ALIGN_MAP, TWIPS_PER_INCH, applyParagraphStyleAttributes, applyTableMargins, calculateEffectiveContentWidth, convertBlockquote, convertBorder, convertBulletList, convertCodeBlock, convertColorToHex, convertCssLengthToPixels, convertDetailsSummary, convertDocument, convertEmuStringToPixels, convertEmuToPixels, convertHardBreak, convertHeading, convertHorizontalRule, convertImage, convertList, convertListItem, convertMeasureToInches, convertMeasureToPixels, convertNode, convertOrderedList, convertParagraph, convertPixelsToEmu, convertPixelsToTwip, convertShading, convertTable, convertTableCell, convertTableHeader, convertTableRow, convertTaskItem, convertTaskList, convertText, convertTextNodes, convertToDocxImageType, convertTwipToCssString, convertTwipToPixels, createFloatingOptions, createStringValidator, findChild, findDeepChild, findDeepChildren, generateDOCX, getImageDataAndMeta, getImageHeight, getImageTypeFromSrc, getImageWidth, normalizeHexColor, parseTwipAttr };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docen/export-docx",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "A powerful TipTap/ProseMirror extension that converts editor content to Microsoft Word DOCX format",
5
5
  "keywords": [
6
6
  "converter",
@@ -54,9 +54,9 @@
54
54
  "ofetch": "1.5.1"
55
55
  },
56
56
  "devDependencies": {
57
- "@tiptap/core": "3.20.1",
58
- "@docen/utils": "0.0.11",
59
- "@docen/extensions": "0.0.11"
57
+ "@tiptap/core": "3.20.0",
58
+ "@docen/extensions": "0.0.13",
59
+ "@docen/utils": "0.0.13"
60
60
  },
61
61
  "scripts": {
62
62
  "dev": "basis build --stub",