docx-kit 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +520 -1
- package/dist/browser.d.ts +1770 -1
- package/dist/browser.js +2333 -1
- package/dist/compileDocument-DIFdjo67.mjs +1051 -0
- package/dist/{compileDocument-BvuU28Yj.js → compileDocument-Vy7rz9_8.js} +330 -29
- package/dist/errors-c9CC63Ti.mjs +63 -0
- package/dist/fs-BuKdtuxB.js +30 -0
- package/dist/{image-6yS25Ggr.js → image-CdfLh9GO.js} +3 -3
- package/dist/image-CdfLh9GO.mjs +76 -0
- package/dist/{index.d.ts → node.d.mts} +962 -177
- package/dist/node.mjs +2362 -0
- package/dist/pack-Bkdfq0_u.mjs +79 -0
- package/dist/{pack-0Jsv03Sk.js → pack-u3tMQYCL.js} +1 -1
- package/package.json +11 -7
- package/dist/index.js +0 -683
- package/dist/node.d.ts +0 -77
- package/dist/node.js +0 -30
- /package/dist/{fs-DF8ug9Wi.js → fs-DF8ug9Wi.mjs} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { n as dataUrlToUint8Array, t as createImageRun } from "./image-CdfLh9GO.js";
|
|
1
2
|
import { t as DocxKitError } from "./errors-c9CC63Ti.js";
|
|
2
|
-
import {
|
|
3
|
-
import { AlignmentType, Document, HeadingLevel, PageBreak, Paragraph, ShadingType, Table, TableCell, TableRow, TextRun, VerticalAlign, WidthType } from "docx";
|
|
3
|
+
import { AlignmentType, BorderStyle, Document, ExternalHyperlink, Footer, Header, HeadingLevel, HighlightColor, LevelFormat, PageBreak, Paragraph, ShadingType, Table, TableCell, TableRow, TextRun, VerticalAlign, WidthType } from "docx";
|
|
4
4
|
//#region src/style/normalizeStyle.ts
|
|
5
5
|
/**
|
|
6
6
|
* Style normalization — merge multiple style sources into one resolved rule.
|
|
@@ -251,9 +251,40 @@ function toTwip(value) {
|
|
|
251
251
|
* @module compiler/compileStyle
|
|
252
252
|
*/
|
|
253
253
|
/**
|
|
254
|
+
* Compile all border properties from a `DocxStyleRule`.
|
|
255
|
+
*
|
|
256
|
+
* Returns `undefined` if no borders are specified.
|
|
257
|
+
*
|
|
258
|
+
* @param style - — The resolved style rule
|
|
259
|
+
* @returns Border options for `docx` or `undefined`
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```ts
|
|
263
|
+
* compileBorder({ border: { style: 'single', color: '#333', width: '1pt' } })
|
|
264
|
+
* // => { top: {...}, right: {...}, bottom: {...}, left: {...} }
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
function compileBorder(style) {
|
|
268
|
+
const b = style.border;
|
|
269
|
+
const bt = style.borderTop ?? b;
|
|
270
|
+
const br = style.borderRight ?? b;
|
|
271
|
+
const bb = style.borderBottom ?? b;
|
|
272
|
+
const bl = style.borderLeft ?? b;
|
|
273
|
+
if (!bt && !br && !bb && !bl) return;
|
|
274
|
+
return {
|
|
275
|
+
bottom: bb ? compileSingleBorder(bb) : void 0,
|
|
276
|
+
left: bl ? compileSingleBorder(bl) : void 0,
|
|
277
|
+
right: br ? compileSingleBorder(br) : void 0,
|
|
278
|
+
top: bt ? compileSingleBorder(bt) : void 0
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
254
282
|
* Compile a `DocxStyleRule` into `docx` table cell options
|
|
255
283
|
* (vertical alignment, margins, shading).
|
|
256
284
|
*
|
|
285
|
+
* Applies sensible default cell padding (5 pt left/right, 2 pt top/bottom)
|
|
286
|
+
* when no margins are explicitly set.
|
|
287
|
+
*
|
|
257
288
|
* @param style - — The resolved style rule
|
|
258
289
|
* @returns Options object suitable for `new TableCell(...)`
|
|
259
290
|
*
|
|
@@ -265,13 +296,23 @@ function toTwip(value) {
|
|
|
265
296
|
*/
|
|
266
297
|
function compileCellStyle(style) {
|
|
267
298
|
const shorthand = style.margin == null ? void 0 : parseShorthandTwip(style.margin);
|
|
299
|
+
const bottom = toTwip(style.marginBottom) ?? shorthand?.bottom;
|
|
300
|
+
const left = toTwip(style.marginLeft) ?? shorthand?.left;
|
|
301
|
+
const right = toTwip(style.marginRight) ?? shorthand?.right;
|
|
302
|
+
const top = toTwip(style.marginTop) ?? shorthand?.top;
|
|
303
|
+
const hasAnyMargin = bottom != null || left != null || right != null || top != null;
|
|
268
304
|
return {
|
|
269
305
|
verticalAlign: compileVerticalAlign(style.verticalAlign),
|
|
270
|
-
margins: {
|
|
271
|
-
bottom:
|
|
272
|
-
left:
|
|
273
|
-
right:
|
|
274
|
-
top:
|
|
306
|
+
margins: hasAnyMargin ? {
|
|
307
|
+
bottom: bottom ?? 0,
|
|
308
|
+
left: left ?? 0,
|
|
309
|
+
right: right ?? 0,
|
|
310
|
+
top: top ?? 0
|
|
311
|
+
} : {
|
|
312
|
+
bottom: 0,
|
|
313
|
+
left: 100,
|
|
314
|
+
right: 100,
|
|
315
|
+
top: 0
|
|
275
316
|
},
|
|
276
317
|
shading: style.backgroundColor == null ? void 0 : {
|
|
277
318
|
fill: normalizeColor(style.backgroundColor),
|
|
@@ -300,6 +341,12 @@ function compileColumnWidth(width) {
|
|
|
300
341
|
type: WidthType.PERCENTAGE
|
|
301
342
|
};
|
|
302
343
|
}
|
|
344
|
+
/** Compile paragraph-level borders (separate from cell borders). */
|
|
345
|
+
function compileParagraphBorder(style) {
|
|
346
|
+
const borders = compileBorder(style);
|
|
347
|
+
if (!borders) return {};
|
|
348
|
+
return { border: borders };
|
|
349
|
+
}
|
|
303
350
|
/**
|
|
304
351
|
* Compile a `DocxStyleRule` into `docx` paragraph-level options
|
|
305
352
|
* (alignment, indent, spacing).
|
|
@@ -318,7 +365,11 @@ function compileParagraphStyle(style) {
|
|
|
318
365
|
const indent = resolveIndent(style);
|
|
319
366
|
return {
|
|
320
367
|
alignment: compileAlignment(style.textAlign),
|
|
368
|
+
...compileParagraphBorder(style),
|
|
321
369
|
indent,
|
|
370
|
+
keepLines: style.keepLines,
|
|
371
|
+
keepNext: style.keepNext,
|
|
372
|
+
pageBreakBefore: style.pageBreakBefore,
|
|
322
373
|
spacing
|
|
323
374
|
};
|
|
324
375
|
}
|
|
@@ -341,11 +392,16 @@ function compileTextStyle(style) {
|
|
|
341
392
|
const result = {
|
|
342
393
|
allCaps: style.allCaps,
|
|
343
394
|
bold: style.fontWeight === "bold" || Number(style.fontWeight) >= 600,
|
|
395
|
+
characterSpacing: style.characterSpacing,
|
|
344
396
|
color: normalizeColor(style.color),
|
|
345
397
|
font: style.fontFamily,
|
|
398
|
+
highlight: compileHighlight(style.highlight),
|
|
346
399
|
italics: style.fontStyle === "italic",
|
|
347
400
|
size: toPtHalf(style.fontSize),
|
|
401
|
+
smallCaps: style.smallCaps,
|
|
348
402
|
strike: style.strike,
|
|
403
|
+
subScript: style.subScript,
|
|
404
|
+
superScript: style.superScript,
|
|
349
405
|
underline: style.underline ? {} : void 0
|
|
350
406
|
};
|
|
351
407
|
if (style.docx) Object.assign(result, style.docx);
|
|
@@ -360,12 +416,39 @@ function compileAlignment(value) {
|
|
|
360
416
|
if (value === "justify") return AlignmentType.JUSTIFIED;
|
|
361
417
|
return AlignmentType.LEFT;
|
|
362
418
|
}
|
|
419
|
+
/**
|
|
420
|
+
* Map a CSS-like border-style string to a `docx` `BorderStyle` enum value.
|
|
421
|
+
*/
|
|
422
|
+
function compileBorderStyle(style) {
|
|
423
|
+
if (style === "dashed") return BorderStyle.DASHED;
|
|
424
|
+
if (style === "dotted") return BorderStyle.DOTTED;
|
|
425
|
+
if (style === "double") return BorderStyle.DOUBLE;
|
|
426
|
+
if (style === "none") return BorderStyle.NONE;
|
|
427
|
+
return BorderStyle.SINGLE;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Map a highlight color name to `docx` `HighlightColor`.
|
|
431
|
+
*/
|
|
432
|
+
function compileHighlight(color) {
|
|
433
|
+
if (!color || color === "none") return;
|
|
434
|
+
return HighlightColor[color.toUpperCase()];
|
|
435
|
+
}
|
|
363
436
|
/** Convert line-height multiplier to twips. 1 → 240 (single-spacing). */
|
|
364
437
|
function compileLineHeight(value) {
|
|
365
438
|
if (typeof value === "number") return Math.round(value * 240);
|
|
366
439
|
return toTwip(value);
|
|
367
440
|
}
|
|
368
441
|
/**
|
|
442
|
+
* Compile a single border side into `docx` border options.
|
|
443
|
+
*/
|
|
444
|
+
function compileSingleBorder(rule) {
|
|
445
|
+
return {
|
|
446
|
+
color: normalizeColor(rule.color),
|
|
447
|
+
size: rule.width == null ? 1 : toTwip(rule.width),
|
|
448
|
+
style: compileBorderStyle(rule.style)
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
369
452
|
* Map the CSS-like vertical-align to a `docx` `VerticalAlign` enum value.
|
|
370
453
|
*/
|
|
371
454
|
function compileVerticalAlign(value) {
|
|
@@ -449,9 +532,13 @@ const HEADING_MAP = {
|
|
|
449
532
|
*/
|
|
450
533
|
async function compileNode(ctx) {
|
|
451
534
|
switch (ctx.node.type) {
|
|
535
|
+
case "bulletList": return compileBulletList(ctx.node, ctx.config);
|
|
452
536
|
case "heading": return compileHeading(ctx.node, ctx.config);
|
|
537
|
+
case "hyperlink": return compileHyperlink(ctx.node, ctx.config);
|
|
453
538
|
case "image": return compileImage(ctx.node, ctx.config);
|
|
539
|
+
case "numberedList": return compileNumberedList(ctx.node, ctx.config);
|
|
454
540
|
case "pageBreak": return new Paragraph({ children: [new PageBreak()] });
|
|
541
|
+
case "sectionBreak": throw new DocxKitError("UNKNOWN_NODE_TYPE", "Section break nodes must be handled at the document compilation level");
|
|
455
542
|
case "paragraph": return compileParagraph(ctx.node, ctx.config);
|
|
456
543
|
case "table": return compileTable(ctx.node, ctx.config);
|
|
457
544
|
case "plugin": {
|
|
@@ -498,22 +585,58 @@ function compileHeading(node, config) {
|
|
|
498
585
|
});
|
|
499
586
|
}
|
|
500
587
|
/**
|
|
588
|
+
* Compile a hyperlink node into an `ExternalHyperlink` containing `TextRun`s.
|
|
589
|
+
*/
|
|
590
|
+
function compileHyperlink(node, config) {
|
|
591
|
+
const style = resolveStyle({
|
|
592
|
+
className: node.className,
|
|
593
|
+
inline: node.style,
|
|
594
|
+
styles: config.styles
|
|
595
|
+
});
|
|
596
|
+
return new Paragraph({ children: [new ExternalHyperlink({
|
|
597
|
+
children: node.children.map((child) => {
|
|
598
|
+
if (typeof child === "string") return new TextRun({
|
|
599
|
+
text: child,
|
|
600
|
+
...compileTextStyle(style)
|
|
601
|
+
});
|
|
602
|
+
return new TextRun({
|
|
603
|
+
text: child.text,
|
|
604
|
+
...compileTextStyle(resolveStyle({
|
|
605
|
+
base: style,
|
|
606
|
+
className: child.className,
|
|
607
|
+
inline: child.style,
|
|
608
|
+
styles: config.styles
|
|
609
|
+
}))
|
|
610
|
+
});
|
|
611
|
+
}),
|
|
612
|
+
link: node.url
|
|
613
|
+
})] });
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
501
616
|
* Compile an image node into a `docx` Paragraph containing an `ImageRun`.
|
|
502
617
|
*
|
|
503
618
|
* Handles image data normalization (Blob → Uint8Array), auto format detection,
|
|
504
619
|
* floating layout, and size transformations.
|
|
505
620
|
*/
|
|
506
|
-
async function compileImage(node,
|
|
621
|
+
async function compileImage(node, config) {
|
|
507
622
|
if (node.data == null || typeof node.data === "string" && node.data.length === 0) throw new DocxKitError("IMAGE_INVALID_DATA", "Image data is empty or null");
|
|
508
623
|
const data = await normalizeImageData(node.data);
|
|
509
624
|
const imageType = node.imageType ?? "png";
|
|
510
|
-
return new Paragraph({
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
625
|
+
return new Paragraph({
|
|
626
|
+
...compileParagraphStyle(resolveStyle({
|
|
627
|
+
base: config.defaults?.image,
|
|
628
|
+
className: node.className,
|
|
629
|
+
inline: node.style,
|
|
630
|
+
styles: config.styles
|
|
631
|
+
})),
|
|
632
|
+
children: [createImageRun({
|
|
633
|
+
data,
|
|
634
|
+
floating: compileFloating(node.floating),
|
|
635
|
+
height: toPx(node.height),
|
|
636
|
+
type: imageType,
|
|
637
|
+
width: toPx(node.width)
|
|
638
|
+
})]
|
|
639
|
+
});
|
|
517
640
|
}
|
|
518
641
|
/**
|
|
519
642
|
* Compile a paragraph node into a `docx` Paragraph.
|
|
@@ -546,6 +669,104 @@ function compileParagraph(node, config) {
|
|
|
546
669
|
children
|
|
547
670
|
});
|
|
548
671
|
}
|
|
672
|
+
const numberingConfigMap = /* @__PURE__ */ new Map();
|
|
673
|
+
let numberingCounter = 0;
|
|
674
|
+
/** Reset numbering state between compilations. */
|
|
675
|
+
function resetNumberingState() {
|
|
676
|
+
numberingConfigMap.clear();
|
|
677
|
+
numberingCounter = 0;
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Generate a numbering config entry for a bullet list
|
|
681
|
+
* and return Paragraphs with numbering references.
|
|
682
|
+
*/
|
|
683
|
+
function compileBulletList(node, config) {
|
|
684
|
+
const ref = `bullet-${++numberingCounter}`;
|
|
685
|
+
const bullet = node.bullet ?? "•";
|
|
686
|
+
numberingConfigMap.set(ref, {
|
|
687
|
+
reference: ref,
|
|
688
|
+
levels: [{
|
|
689
|
+
alignment: AlignmentType.LEFT,
|
|
690
|
+
format: LevelFormat.BULLET,
|
|
691
|
+
level: node.level ?? 0,
|
|
692
|
+
text: bullet
|
|
693
|
+
}]
|
|
694
|
+
});
|
|
695
|
+
const style = resolveStyle({
|
|
696
|
+
className: node.className,
|
|
697
|
+
inline: node.style,
|
|
698
|
+
styles: config.styles
|
|
699
|
+
});
|
|
700
|
+
return node.items.map((item) => {
|
|
701
|
+
const text = typeof item === "string" ? item : item.text;
|
|
702
|
+
const itemStyle = typeof item === "object" ? resolveStyle({
|
|
703
|
+
base: style,
|
|
704
|
+
className: item.className,
|
|
705
|
+
inline: item.style,
|
|
706
|
+
styles: config.styles
|
|
707
|
+
}) : style;
|
|
708
|
+
return new Paragraph({
|
|
709
|
+
...compileParagraphStyle(itemStyle),
|
|
710
|
+
numbering: {
|
|
711
|
+
level: node.level ?? 0,
|
|
712
|
+
reference: ref
|
|
713
|
+
},
|
|
714
|
+
children: [new TextRun({
|
|
715
|
+
text,
|
|
716
|
+
...compileTextStyle(itemStyle)
|
|
717
|
+
})]
|
|
718
|
+
});
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Generate a numbering config entry for an ordered list
|
|
723
|
+
* and return Paragraphs with numbering references.
|
|
724
|
+
*/
|
|
725
|
+
function compileNumberedList(node, config) {
|
|
726
|
+
const ref = `numbered-${++numberingCounter}`;
|
|
727
|
+
const formatMap = {
|
|
728
|
+
decimal: LevelFormat.DECIMAL,
|
|
729
|
+
lowerLetter: LevelFormat.LOWER_LETTER,
|
|
730
|
+
lowerRoman: LevelFormat.LOWER_ROMAN,
|
|
731
|
+
upperLetter: LevelFormat.UPPER_LETTER,
|
|
732
|
+
upperRoman: LevelFormat.UPPER_ROMAN
|
|
733
|
+
};
|
|
734
|
+
numberingConfigMap.set(ref, {
|
|
735
|
+
reference: ref,
|
|
736
|
+
levels: [{
|
|
737
|
+
alignment: AlignmentType.LEFT,
|
|
738
|
+
level: node.level ?? 0,
|
|
739
|
+
start: node.start ?? 1,
|
|
740
|
+
text: "%1.",
|
|
741
|
+
format: formatMap[node.numberingFormat ?? "decimal"] ?? LevelFormat.DECIMAL
|
|
742
|
+
}]
|
|
743
|
+
});
|
|
744
|
+
const style = resolveStyle({
|
|
745
|
+
className: node.className,
|
|
746
|
+
inline: node.style,
|
|
747
|
+
styles: config.styles
|
|
748
|
+
});
|
|
749
|
+
return node.items.map((item) => {
|
|
750
|
+
const text = typeof item === "string" ? item : item.text;
|
|
751
|
+
const itemStyle = typeof item === "object" ? resolveStyle({
|
|
752
|
+
base: style,
|
|
753
|
+
className: item.className,
|
|
754
|
+
inline: item.style,
|
|
755
|
+
styles: config.styles
|
|
756
|
+
}) : style;
|
|
757
|
+
return new Paragraph({
|
|
758
|
+
...compileParagraphStyle(itemStyle),
|
|
759
|
+
numbering: {
|
|
760
|
+
level: node.level ?? 0,
|
|
761
|
+
reference: ref
|
|
762
|
+
},
|
|
763
|
+
children: [new TextRun({
|
|
764
|
+
text,
|
|
765
|
+
...compileTextStyle(itemStyle)
|
|
766
|
+
})]
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
}
|
|
549
770
|
/**
|
|
550
771
|
* Compile a table node into a `docx` Table.
|
|
551
772
|
*
|
|
@@ -560,15 +781,24 @@ function compileTable(node, _config) {
|
|
|
560
781
|
children: node.columns.map((col) => new TableCell({
|
|
561
782
|
...compileCellStyle(node.headerCellStyle ?? {}),
|
|
562
783
|
children: [new Paragraph(String(col.title))],
|
|
784
|
+
...col.colSpan && col.colSpan > 1 ? { columnSpan: col.colSpan } : {},
|
|
563
785
|
width: compileColumnWidth(col.width)
|
|
564
786
|
}))
|
|
565
787
|
}));
|
|
566
788
|
rows.push(...node.data.map((row, rowIndex) => new TableRow({ children: node.columns.map((col) => {
|
|
567
789
|
const raw = row[col.key];
|
|
568
790
|
const rendered = col.render ? col.render(raw, row, rowIndex) : String(raw ?? "");
|
|
791
|
+
const cellColSpan = row[`_${col.key}_colSpan`] ?? col.colSpan;
|
|
792
|
+
const isEvenRow = rowIndex % 2 === 1;
|
|
793
|
+
const baseCellStyle = node.cellStyle ? { ...compileCellStyle(node.cellStyle) } : {};
|
|
569
794
|
return new TableCell({
|
|
570
|
-
...
|
|
795
|
+
...baseCellStyle,
|
|
796
|
+
...node.striped && isEvenRow && baseCellStyle.shading ? { shading: {
|
|
797
|
+
...baseCellStyle.shading,
|
|
798
|
+
fill: "F2F2F2"
|
|
799
|
+
} } : {},
|
|
571
800
|
children: [new Paragraph(String(rendered))],
|
|
801
|
+
...cellColSpan && cellColSpan > 1 ? { columnSpan: cellColSpan } : {},
|
|
572
802
|
width: compileColumnWidth(col.width)
|
|
573
803
|
});
|
|
574
804
|
}) })));
|
|
@@ -616,7 +846,10 @@ async function normalizeImageData(data) {
|
|
|
616
846
|
*
|
|
617
847
|
* This is the top-level compilation entry point. It iterates over all block
|
|
618
848
|
* nodes, compiles each one via {@link compileNode}, and wraps them in a
|
|
619
|
-
*
|
|
849
|
+
* `Document` with page properties and metadata.
|
|
850
|
+
*
|
|
851
|
+
* Multi-section support: {@link SectionBreakNode} markers split content
|
|
852
|
+
* into separate sections, each with its own page setup, headers, and footers.
|
|
620
853
|
*
|
|
621
854
|
* @module compiler/compileDocument
|
|
622
855
|
*/
|
|
@@ -626,6 +859,9 @@ async function normalizeImageData(data) {
|
|
|
626
859
|
* This is the primary compilation pipeline — nodes flow through
|
|
627
860
|
* `compileNode` → individual sub-compilers → `docx` objects → `Document`.
|
|
628
861
|
*
|
|
862
|
+
* If `SectionBreakNode` markers are present, content is split into
|
|
863
|
+
* multiple sections, each with its own page setup, headers, and footers.
|
|
864
|
+
*
|
|
629
865
|
* @param options - — Compilation options with config, nodes, and plugins
|
|
630
866
|
* @returns A `docx` `Document` ready for packaging
|
|
631
867
|
*
|
|
@@ -642,16 +878,35 @@ async function normalizeImageData(data) {
|
|
|
642
878
|
* ```
|
|
643
879
|
*/
|
|
644
880
|
async function compileDocument(options) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
881
|
+
resetNumberingState();
|
|
882
|
+
const sectionGroups = [{ nodes: [] }];
|
|
883
|
+
for (const node of options.nodes) if (node.type === "sectionBreak") sectionGroups.push({
|
|
884
|
+
config: node.config,
|
|
885
|
+
nodes: []
|
|
886
|
+
});
|
|
887
|
+
else sectionGroups[sectionGroups.length - 1].nodes.push(node);
|
|
888
|
+
const sections = [];
|
|
889
|
+
for (const [i, group] of sectionGroups.entries()) {
|
|
890
|
+
const children = [];
|
|
891
|
+
for (const node of group.nodes) {
|
|
892
|
+
const compiled = await compileNode({
|
|
893
|
+
config: options.config,
|
|
894
|
+
node,
|
|
895
|
+
plugins: options.plugins
|
|
896
|
+
});
|
|
897
|
+
if (Array.isArray(compiled)) children.push(...compiled);
|
|
898
|
+
else children.push(compiled);
|
|
899
|
+
}
|
|
900
|
+
const sectionConfig = group.config;
|
|
901
|
+
const pageConfig = i === 0 ? options.config : mergePageConfig(options.config, sectionConfig);
|
|
902
|
+
sections.push({
|
|
903
|
+
...compileSectionProperties(pageConfig),
|
|
904
|
+
children,
|
|
905
|
+
footers: compileFooters(i === 0 ? void 0 : sectionConfig?.footer, options.config),
|
|
906
|
+
headers: compileHeaders(i === 0 ? void 0 : sectionConfig?.header, options.config)
|
|
651
907
|
});
|
|
652
|
-
if (Array.isArray(compiled)) children.push(...compiled);
|
|
653
|
-
else children.push(compiled);
|
|
654
908
|
}
|
|
909
|
+
const numberingConfig = numberingConfigMap.size > 0 ? Array.from(numberingConfigMap.values()) : void 0;
|
|
655
910
|
return new Document({
|
|
656
911
|
creator: options.config.metadata?.creator,
|
|
657
912
|
description: options.config.metadata?.description,
|
|
@@ -659,13 +914,43 @@ async function compileDocument(options) {
|
|
|
659
914
|
lastModifiedBy: options.config.metadata?.lastModifiedBy,
|
|
660
915
|
subject: options.config.metadata?.subject,
|
|
661
916
|
title: options.config.metadata?.title,
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
children
|
|
665
|
-
}]
|
|
917
|
+
...numberingConfig ? { numbering: { config: numberingConfig } } : {},
|
|
918
|
+
sections
|
|
666
919
|
});
|
|
667
920
|
}
|
|
668
921
|
/**
|
|
922
|
+
* Compile section footers into `docx` `Footer` objects.
|
|
923
|
+
*
|
|
924
|
+
* @returns `{ default?, first?, even? }` or `undefined` if no footers defined
|
|
925
|
+
*/
|
|
926
|
+
function compileFooters(config, _docConfig) {
|
|
927
|
+
if (!config) return;
|
|
928
|
+
const result = {};
|
|
929
|
+
if (config.default) result.default = new Footer({ children: compileHeaderFooterChildren(config.default) });
|
|
930
|
+
if (config.first) result.first = new Footer({ children: compileHeaderFooterChildren(config.first) });
|
|
931
|
+
if (config.even) result.even = new Footer({ children: compileHeaderFooterChildren(config.even) });
|
|
932
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Compile a `HeaderFooterContent` into a list of `docx` `Paragraph`s.
|
|
936
|
+
*/
|
|
937
|
+
function compileHeaderFooterChildren(content) {
|
|
938
|
+
return content.children.map((text) => new Paragraph(text));
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Compile section headers into `docx` `Header` objects.
|
|
942
|
+
*
|
|
943
|
+
* @returns `{ default?, first?, even? }` or `undefined` if no headers defined
|
|
944
|
+
*/
|
|
945
|
+
function compileHeaders(config, _docConfig) {
|
|
946
|
+
if (!config) return;
|
|
947
|
+
const result = {};
|
|
948
|
+
if (config.default) result.default = new Header({ children: compileHeaderFooterChildren(config.default) });
|
|
949
|
+
if (config.first) result.first = new Header({ children: compileHeaderFooterChildren(config.first) });
|
|
950
|
+
if (config.even) result.even = new Header({ children: compileHeaderFooterChildren(config.even) });
|
|
951
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
669
954
|
* Compile a single page margin value (shorthand or explicit) into twips.
|
|
670
955
|
*/
|
|
671
956
|
function compilePageMargin(margin) {
|
|
@@ -689,6 +974,22 @@ function compileSectionProperties(config) {
|
|
|
689
974
|
} } };
|
|
690
975
|
}
|
|
691
976
|
/**
|
|
977
|
+
* Merge document-level page config with per-section overrides.
|
|
978
|
+
*
|
|
979
|
+
* Section config wins when both are specified; falls back to document
|
|
980
|
+
* defaults for any property not set in the section.
|
|
981
|
+
*/
|
|
982
|
+
function mergePageConfig(docConfig, sectionConfig) {
|
|
983
|
+
if (!sectionConfig?.page) return docConfig;
|
|
984
|
+
return {
|
|
985
|
+
...docConfig,
|
|
986
|
+
page: {
|
|
987
|
+
...docConfig.page,
|
|
988
|
+
...sectionConfig.page
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
692
993
|
* Page size presets in twips (width × height).
|
|
693
994
|
*
|
|
694
995
|
* Values sourced from the OOXML specification.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Error handling types and classes for docx-kit.
|
|
4
|
+
*
|
|
5
|
+
* @module errors
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Well-known error codes used throughout the library.
|
|
9
|
+
*
|
|
10
|
+
* Consumers can match against these codes for structured error handling.
|
|
11
|
+
*/
|
|
12
|
+
const ERROR_CODES = {
|
|
13
|
+
/** Document export failed. */
|
|
14
|
+
EXPORT_FAILED: "EXPORT_FAILED",
|
|
15
|
+
/** Image data is empty, corrupt, or unsupported. */
|
|
16
|
+
IMAGE_INVALID_DATA: "IMAGE_INVALID_DATA",
|
|
17
|
+
/** A plugin node referenced an unregistered plugin. */
|
|
18
|
+
PLUGIN_NOT_REGISTERED: "PLUGIN_NOT_REGISTERED",
|
|
19
|
+
/** A plugin's `render()` method threw an error. */
|
|
20
|
+
PLUGIN_RENDER_FAILED: "PLUGIN_RENDER_FAILED",
|
|
21
|
+
/** A `className` referenced a stylesheet key that doesn't exist. */
|
|
22
|
+
STYLE_UNKNOWN_CLASS: "STYLE_UNKNOWN_CLASS",
|
|
23
|
+
/** Table was created with no columns. */
|
|
24
|
+
TABLE_INVALID_COLUMNS: "TABLE_INVALID_COLUMNS",
|
|
25
|
+
/** Encountered a node type that has no registered compiler. */
|
|
26
|
+
UNKNOWN_NODE_TYPE: "UNKNOWN_NODE_TYPE"
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Structured error class for docx-kit.
|
|
30
|
+
*
|
|
31
|
+
* Carries a machine-readable `code`, a human-readable `message`,
|
|
32
|
+
* and optionally the underlying `cause`.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* try {
|
|
37
|
+
* await doc.save('output.docx')
|
|
38
|
+
* } catch (err) {
|
|
39
|
+
* if (err instanceof DocxKitError && err.code === ERROR_CODES.EXPORT_FAILED) {
|
|
40
|
+
* console.error('Export failed:', err.message)
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
var DocxKitError = class extends Error {
|
|
46
|
+
/** The underlying error that caused this failure (if any). */
|
|
47
|
+
cause;
|
|
48
|
+
/** Machine-readable error code. */
|
|
49
|
+
code;
|
|
50
|
+
/**
|
|
51
|
+
* @param code - — Known error code from {@link ERROR_CODES}
|
|
52
|
+
* @param message - — Human-readable description
|
|
53
|
+
* @param cause - — Optional underlying error
|
|
54
|
+
*/
|
|
55
|
+
constructor(code, message, cause) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "DocxKitError";
|
|
58
|
+
this.code = code;
|
|
59
|
+
this.cause = cause;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
export { ERROR_CODES as n, DocxKitError as t };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Packer } from "docx";
|
|
2
|
+
//#region src/node/fs.ts
|
|
3
|
+
/**
|
|
4
|
+
* Node.js filesystem utilities for saving documents to disk.
|
|
5
|
+
*
|
|
6
|
+
* @module node/fs
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Save a compiled document to a file on disk.
|
|
10
|
+
*
|
|
11
|
+
* Uses `Packer.toBuffer()` to produce the .docx binary, then
|
|
12
|
+
* writes via `node:fs/promises`. This function is **Node.js only**.
|
|
13
|
+
*
|
|
14
|
+
* @param doc - — A compiled `docx` `Document` instance
|
|
15
|
+
* @param filename - — Output file path (e.g. `"report.docx"`)
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { saveDocument } from 'docx-kit/node'
|
|
20
|
+
*
|
|
21
|
+
* const doc = await compileDocument({ ... })
|
|
22
|
+
* await saveDocument(doc, 'report.docx')
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
async function saveDocument(doc, filename) {
|
|
26
|
+
const { writeFile } = await import("node:fs/promises");
|
|
27
|
+
await writeFile(filename, await Packer.toBuffer(doc));
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { saveDocument };
|
|
@@ -46,8 +46,8 @@ async function dataUrlToUint8Array(dataUrl) {
|
|
|
46
46
|
/**
|
|
47
47
|
* Shared image utilities for internal use by compilers and plugins.
|
|
48
48
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
* Bridges the `docx` library's `IImageOptions` with `docx-kit`'s
|
|
50
|
+
* own `CreateImageRunOptions`, confining type casts to this single module.
|
|
51
51
|
*
|
|
52
52
|
* @module utils/image
|
|
53
53
|
*/
|
|
@@ -56,7 +56,7 @@ async function dataUrlToUint8Array(dataUrl) {
|
|
|
56
56
|
*
|
|
57
57
|
* All paths that construct an `ImageRun` should use this factory
|
|
58
58
|
* instead of calling `new ImageRun(...)` directly, ensuring that
|
|
59
|
-
*
|
|
59
|
+
* type casts are confined to a single audited function.
|
|
60
60
|
*
|
|
61
61
|
* @param options - — Image data and layout options
|
|
62
62
|
* @returns A configured `ImageRun` instance
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ImageRun } from "docx";
|
|
2
|
+
//#region src/utils/dataUrl.ts
|
|
3
|
+
/**
|
|
4
|
+
* Cross-platform base64 data-URL decoder.
|
|
5
|
+
*
|
|
6
|
+
* Auto-detects the runtime environment and uses the appropriate
|
|
7
|
+
* implementation:
|
|
8
|
+
* - **Node.js**: `Buffer.from(base64, 'base64')`
|
|
9
|
+
* - **Browser**: `atob(base64)` + manual byte population
|
|
10
|
+
*
|
|
11
|
+
* This is the internal shared version used by the compiler and
|
|
12
|
+
* built-in plugins. For platform-specific direct access, import
|
|
13
|
+
* from `'docx-kit/node'` or `'docx-kit/browser'`.
|
|
14
|
+
*
|
|
15
|
+
* @module utils/dataUrl
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Decode a base64 data-URL to raw bytes (works in both browser & Node.js).
|
|
19
|
+
*
|
|
20
|
+
* Strips the `"data:*;base64,"` prefix and decodes using the appropriate
|
|
21
|
+
* runtime API.
|
|
22
|
+
*
|
|
23
|
+
* @param dataUrl - — A base64 data-URI string (e.g. `"data:image/png;base64,iVBO..."`)
|
|
24
|
+
* @returns Raw bytes as `Uint8Array`
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { dataUrlToUint8Array } from 'docx-kit'
|
|
29
|
+
*
|
|
30
|
+
* const bytes = await dataUrlToUint8Array('data:image/png;base64,iVBORw...')
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
async function dataUrlToUint8Array(dataUrl) {
|
|
34
|
+
const base64 = dataUrl.split(",")[1];
|
|
35
|
+
if (typeof atob === "function") {
|
|
36
|
+
const binary = atob(base64);
|
|
37
|
+
const bytes = new Uint8Array(binary.length);
|
|
38
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
39
|
+
return bytes;
|
|
40
|
+
}
|
|
41
|
+
const { Buffer } = await import("node:buffer");
|
|
42
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/utils/image.ts
|
|
46
|
+
/**
|
|
47
|
+
* Shared image utilities for internal use by compilers and plugins.
|
|
48
|
+
*
|
|
49
|
+
* Bridges the `docx` library's `IImageOptions` with `docx-kit`'s
|
|
50
|
+
* own `CreateImageRunOptions`, confining type casts to this single module.
|
|
51
|
+
*
|
|
52
|
+
* @module utils/image
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* Create an `ImageRun` with type-safe defaults.
|
|
56
|
+
*
|
|
57
|
+
* All paths that construct an `ImageRun` should use this factory
|
|
58
|
+
* instead of calling `new ImageRun(...)` directly, ensuring that
|
|
59
|
+
* type casts are confined to a single audited function.
|
|
60
|
+
*
|
|
61
|
+
* @param options - — Image data and layout options
|
|
62
|
+
* @returns A configured `ImageRun` instance
|
|
63
|
+
*/
|
|
64
|
+
function createImageRun(options) {
|
|
65
|
+
return new ImageRun({
|
|
66
|
+
data: options.data,
|
|
67
|
+
floating: options.floating,
|
|
68
|
+
type: options.type,
|
|
69
|
+
transformation: {
|
|
70
|
+
height: options.height ?? 180,
|
|
71
|
+
width: options.width ?? 300
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { dataUrlToUint8Array as n, createImageRun as t };
|