docgen-utils 1.0.12 → 1.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/dist/bundle.js +3189 -1238
- package/dist/bundle.min.js +101 -99
- package/dist/cli.js +2653 -1117
- package/dist/packages/cli/commands/export-docs.d.ts.map +1 -1
- package/dist/packages/cli/commands/export-docs.js +131 -2
- package/dist/packages/cli/commands/export-docs.js.map +1 -1
- package/dist/packages/cli/commands/export-slides.d.ts.map +1 -1
- package/dist/packages/cli/commands/export-slides.js +25 -1
- package/dist/packages/cli/commands/export-slides.js.map +1 -1
- package/dist/packages/docs/common.d.ts +10 -0
- package/dist/packages/docs/common.d.ts.map +1 -1
- package/dist/packages/docs/common.js.map +1 -1
- package/dist/packages/docs/convert.d.ts.map +1 -1
- package/dist/packages/docs/convert.js +246 -218
- package/dist/packages/docs/convert.js.map +1 -1
- package/dist/packages/docs/create-document.d.ts.map +1 -1
- package/dist/packages/docs/create-document.js +43 -3
- package/dist/packages/docs/create-document.js.map +1 -1
- package/dist/packages/docs/export.d.ts +9 -8
- package/dist/packages/docs/export.d.ts.map +1 -1
- package/dist/packages/docs/export.js +23 -36
- package/dist/packages/docs/export.js.map +1 -1
- package/dist/packages/docs/parse-colors.d.ts +37 -0
- package/dist/packages/docs/parse-colors.d.ts.map +1 -0
- package/dist/packages/docs/parse-colors.js +507 -0
- package/dist/packages/docs/parse-colors.js.map +1 -0
- package/dist/packages/docs/parse-css.d.ts +98 -0
- package/dist/packages/docs/parse-css.d.ts.map +1 -0
- package/dist/packages/docs/parse-css.js +1592 -0
- package/dist/packages/docs/parse-css.js.map +1 -0
- package/dist/packages/docs/parse-helpers.d.ts +45 -0
- package/dist/packages/docs/parse-helpers.d.ts.map +1 -0
- package/dist/packages/docs/parse-helpers.js +214 -0
- package/dist/packages/docs/parse-helpers.js.map +1 -0
- package/dist/packages/docs/parse-inline.d.ts +41 -0
- package/dist/packages/docs/parse-inline.d.ts.map +1 -0
- package/dist/packages/docs/parse-inline.js +473 -0
- package/dist/packages/docs/parse-inline.js.map +1 -0
- package/dist/packages/docs/parse-layout.d.ts +57 -0
- package/dist/packages/docs/parse-layout.d.ts.map +1 -0
- package/dist/packages/docs/parse-layout.js +295 -0
- package/dist/packages/docs/parse-layout.js.map +1 -0
- package/dist/packages/docs/parse-special.d.ts +51 -0
- package/dist/packages/docs/parse-special.d.ts.map +1 -0
- package/dist/packages/docs/parse-special.js +251 -0
- package/dist/packages/docs/parse-special.js.map +1 -0
- package/dist/packages/docs/parse-units.d.ts +68 -0
- package/dist/packages/docs/parse-units.d.ts.map +1 -0
- package/dist/packages/docs/parse-units.js +275 -0
- package/dist/packages/docs/parse-units.js.map +1 -0
- package/dist/packages/docs/parse.d.ts.map +1 -1
- package/dist/packages/docs/parse.js +957 -2800
- package/dist/packages/docs/parse.js.map +1 -1
- package/dist/packages/slides/common.d.ts +7 -0
- package/dist/packages/slides/common.d.ts.map +1 -1
- package/dist/packages/slides/convert.d.ts.map +1 -1
- package/dist/packages/slides/convert.js +92 -7
- package/dist/packages/slides/convert.js.map +1 -1
- package/dist/packages/slides/parse.d.ts.map +1 -1
- package/dist/packages/slides/parse.js +723 -40
- package/dist/packages/slides/parse.js.map +1 -1
- package/dist/packages/slides/transform.d.ts.map +1 -1
- package/dist/packages/slides/transform.js +12 -7
- package/dist/packages/slides/transform.js.map +1 -1
- package/package.json +1 -1
|
@@ -19,7 +19,92 @@ const HEADING_SPACING = {
|
|
|
19
19
|
h1: { before: 160, after: 240 }, // 0.67em / 1em
|
|
20
20
|
h2: { before: 480, after: 240 }, // margin-top: 2rem, margin-bottom: 1rem
|
|
21
21
|
h3: { before: 360, after: 240 }, // margin-top: 1.5rem
|
|
22
|
+
h4: { before: 320, after: 200 }, // margin-top: 1.33em
|
|
23
|
+
h5: { before: 320, after: 200 }, // margin-top: 1.33em
|
|
24
|
+
h6: { before: 320, after: 200 }, // margin-top: 1.33em
|
|
22
25
|
};
|
|
26
|
+
/** Invisible border used to suppress default DOCX borders. */
|
|
27
|
+
const NIL_BORDER = { style: BorderStyle.NIL, size: 0, color: "FFFFFF" };
|
|
28
|
+
/** All-sides invisible border for table cells and tables. */
|
|
29
|
+
const NIL_BORDERS = { top: NIL_BORDER, bottom: NIL_BORDER, left: NIL_BORDER, right: NIL_BORDER };
|
|
30
|
+
/** All-sides invisible border including inside borders for tables. */
|
|
31
|
+
const NIL_TABLE_BORDERS = { ...NIL_BORDERS, insideHorizontal: NIL_BORDER, insideVertical: NIL_BORDER };
|
|
32
|
+
/**
|
|
33
|
+
* Apply text-transform (uppercase, lowercase, capitalize) to a string.
|
|
34
|
+
*/
|
|
35
|
+
function applyTextTransform(text, transform) {
|
|
36
|
+
if (!transform)
|
|
37
|
+
return text;
|
|
38
|
+
if (transform === "uppercase")
|
|
39
|
+
return text.toUpperCase();
|
|
40
|
+
if (transform === "lowercase")
|
|
41
|
+
return text.toLowerCase();
|
|
42
|
+
if (transform === "capitalize")
|
|
43
|
+
return text.replace(/\b\w/g, c => c.toUpperCase());
|
|
44
|
+
return text;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Convert underline type string to DOCX UnderlineType enum.
|
|
48
|
+
*/
|
|
49
|
+
function getUnderlineType(type) {
|
|
50
|
+
switch (type) {
|
|
51
|
+
case "single": return UnderlineType.SINGLE;
|
|
52
|
+
case "dotted": return UnderlineType.DOTTED;
|
|
53
|
+
case "double": return UnderlineType.DOUBLE;
|
|
54
|
+
case "wave": return UnderlineType.WAVE;
|
|
55
|
+
default: return UnderlineType.SINGLE;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a chart/SVG placeholder paragraph (for charts that can't be rendered as images).
|
|
60
|
+
*/
|
|
61
|
+
function createChartPlaceholderParagraph(title) {
|
|
62
|
+
const text = title ? `[Chart: ${title}]` : "[Chart]";
|
|
63
|
+
return new Paragraph({
|
|
64
|
+
children: [
|
|
65
|
+
new TextRun({
|
|
66
|
+
text,
|
|
67
|
+
italics: true,
|
|
68
|
+
color: "808080",
|
|
69
|
+
}),
|
|
70
|
+
],
|
|
71
|
+
alignment: AlignmentType.CENTER,
|
|
72
|
+
border: {
|
|
73
|
+
top: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
74
|
+
bottom: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
75
|
+
left: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
76
|
+
right: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
77
|
+
},
|
|
78
|
+
shading: {
|
|
79
|
+
type: ShadingType.CLEAR,
|
|
80
|
+
color: "auto",
|
|
81
|
+
fill: "F9FAFB",
|
|
82
|
+
},
|
|
83
|
+
spacing: {
|
|
84
|
+
before: 200,
|
|
85
|
+
after: 200,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create an image caption paragraph (italic, 10pt, gray, centered).
|
|
91
|
+
*/
|
|
92
|
+
function createCaptionParagraph(caption) {
|
|
93
|
+
return new Paragraph({
|
|
94
|
+
children: [
|
|
95
|
+
new TextRun({
|
|
96
|
+
text: caption,
|
|
97
|
+
italics: true,
|
|
98
|
+
size: 20, // 10pt
|
|
99
|
+
color: "6B7280",
|
|
100
|
+
}),
|
|
101
|
+
],
|
|
102
|
+
alignment: AlignmentType.CENTER,
|
|
103
|
+
spacing: {
|
|
104
|
+
after: 200,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
23
108
|
/**
|
|
24
109
|
* Concrete XmlComponent implementation for building custom XML elements.
|
|
25
110
|
*/
|
|
@@ -75,11 +160,11 @@ class GradientTextFill extends XmlElement {
|
|
|
75
160
|
gradFill.addChildElement(gsLst);
|
|
76
161
|
// Create lin (linear gradient) element
|
|
77
162
|
// Angle is in 1/60000ths of a degree (so 135deg = 135 * 60000 = 8100000)
|
|
78
|
-
// Word uses
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
const wordAngle = ((
|
|
163
|
+
// Word w14 uses the same angle system as DrawingML:
|
|
164
|
+
// 0 = left-to-right (east), 90 = top-to-bottom (south), clockwise
|
|
165
|
+
// CSS angles: 0 = bottom-to-top (north), 90 = left-to-right (east), clockwise
|
|
166
|
+
// Conversion: DML_angle = CSS_angle - 90 (both clockwise, just different zero point)
|
|
167
|
+
const wordAngle = ((gradient.angle - 90 + 360) % 360) * 60000;
|
|
83
168
|
const lin = new XmlElement("w14:lin", {
|
|
84
169
|
"w14:ang": wordAngle.toString(),
|
|
85
170
|
"w14:scaled": "0",
|
|
@@ -271,8 +356,9 @@ class GradientBackgroundDrawing extends XmlElement {
|
|
|
271
356
|
gradFill.addChildElement(gsLst);
|
|
272
357
|
// Linear gradient direction
|
|
273
358
|
// CSS angle: 0deg = to top, 90deg = to right, 135deg = to bottom-right
|
|
274
|
-
// DrawingML: 0 = left to right, angles in 1/60000ths of a degree
|
|
275
|
-
|
|
359
|
+
// DrawingML: 0 = left to right (east), angles in 1/60000ths of a degree, clockwise
|
|
360
|
+
// Conversion: DML_angle = CSS_angle - 90 (both clockwise, just different zero point)
|
|
361
|
+
const normalizedAngle = ((gradient.angle - 90 + 360) % 360);
|
|
276
362
|
const drawingAngle = normalizedAngle * 60000;
|
|
277
363
|
gradFill.addChildElement(new XmlElement("a:lin", {
|
|
278
364
|
ang: drawingAngle.toString(),
|
|
@@ -367,28 +453,6 @@ function textAlignmentToDocx(alignment) {
|
|
|
367
453
|
*/
|
|
368
454
|
function inlineRunsToTextRuns(runs, textTransform) {
|
|
369
455
|
const result = [];
|
|
370
|
-
// Helper to apply text transform
|
|
371
|
-
const applyTransform = (text) => {
|
|
372
|
-
if (!textTransform)
|
|
373
|
-
return text;
|
|
374
|
-
if (textTransform === "uppercase")
|
|
375
|
-
return text.toUpperCase();
|
|
376
|
-
if (textTransform === "lowercase")
|
|
377
|
-
return text.toLowerCase();
|
|
378
|
-
if (textTransform === "capitalize")
|
|
379
|
-
return text.replace(/\b\w/g, c => c.toUpperCase());
|
|
380
|
-
return text;
|
|
381
|
-
};
|
|
382
|
-
// Helper to convert underline type
|
|
383
|
-
const getUnderlineType = (type) => {
|
|
384
|
-
switch (type) {
|
|
385
|
-
case "single": return UnderlineType.SINGLE;
|
|
386
|
-
case "dotted": return UnderlineType.DOTTED;
|
|
387
|
-
case "double": return UnderlineType.DOUBLE;
|
|
388
|
-
case "wave": return UnderlineType.WAVE;
|
|
389
|
-
default: return UnderlineType.SINGLE;
|
|
390
|
-
}
|
|
391
|
-
};
|
|
392
456
|
for (const run of runs) {
|
|
393
457
|
// Split text by newlines to handle <br> tags
|
|
394
458
|
const parts = run.text.split('\n');
|
|
@@ -398,7 +462,7 @@ function inlineRunsToTextRuns(runs, textTransform) {
|
|
|
398
462
|
color: run.underline.color,
|
|
399
463
|
} : undefined;
|
|
400
464
|
for (let i = 0; i < parts.length; i++) {
|
|
401
|
-
const part =
|
|
465
|
+
const part = applyTextTransform(parts[i], textTransform);
|
|
402
466
|
// Add a line break before this part (except for the first part)
|
|
403
467
|
if (i > 0) {
|
|
404
468
|
result.push(new TextRun({
|
|
@@ -410,11 +474,13 @@ function inlineRunsToTextRuns(runs, textTransform) {
|
|
|
410
474
|
font: run.fontFamily,
|
|
411
475
|
superScript: run.superscript,
|
|
412
476
|
subScript: run.subscript,
|
|
477
|
+
strike: run.strike,
|
|
478
|
+
characterSpacing: run.letterSpacing,
|
|
413
479
|
underline: underlineConfig,
|
|
414
480
|
shading: run.backgroundColor ? {
|
|
415
|
-
type: ShadingType.
|
|
481
|
+
type: ShadingType.CLEAR,
|
|
416
482
|
fill: run.backgroundColor,
|
|
417
|
-
color:
|
|
483
|
+
color: "auto",
|
|
418
484
|
} : undefined,
|
|
419
485
|
}));
|
|
420
486
|
}
|
|
@@ -442,11 +508,13 @@ function inlineRunsToTextRuns(runs, textTransform) {
|
|
|
442
508
|
font: run.fontFamily,
|
|
443
509
|
superScript: run.superscript,
|
|
444
510
|
subScript: run.subscript,
|
|
511
|
+
strike: run.strike,
|
|
512
|
+
characterSpacing: run.letterSpacing,
|
|
445
513
|
underline: underlineConfig,
|
|
446
514
|
shading: run.backgroundColor ? {
|
|
447
|
-
type: ShadingType.
|
|
515
|
+
type: ShadingType.CLEAR,
|
|
448
516
|
fill: run.backgroundColor,
|
|
449
|
-
color:
|
|
517
|
+
color: "auto",
|
|
450
518
|
} : undefined,
|
|
451
519
|
}));
|
|
452
520
|
}
|
|
@@ -490,35 +558,38 @@ function createTableRow(cells, isHeaderRow, columnCount, cellPadding, headerBack
|
|
|
490
558
|
font: run.fontFamily,
|
|
491
559
|
superScript: run.superscript,
|
|
492
560
|
subScript: run.subscript,
|
|
561
|
+
strike: run.strike,
|
|
562
|
+
characterSpacing: run.letterSpacing,
|
|
493
563
|
underline: run.underline ? {
|
|
494
564
|
type: getUnderlineTypeForTable(run.underline.type),
|
|
495
565
|
color: run.underline.color,
|
|
496
566
|
} : undefined,
|
|
567
|
+
// Preserve inline background colors for badge/pill styling in table cells
|
|
568
|
+
shading: run.backgroundColor ? {
|
|
569
|
+
type: ShadingType.CLEAR,
|
|
570
|
+
fill: run.backgroundColor,
|
|
571
|
+
color: "auto",
|
|
572
|
+
} : undefined,
|
|
497
573
|
}));
|
|
498
574
|
// Determine cell shading: header row uses headerBackgroundColor, even rows use rowBackgroundColor
|
|
499
575
|
let shading;
|
|
500
576
|
if (isHeaderRow && headerBackgroundColor) {
|
|
501
577
|
shading = {
|
|
502
|
-
type: ShadingType.
|
|
503
|
-
color:
|
|
578
|
+
type: ShadingType.CLEAR,
|
|
579
|
+
color: "auto",
|
|
504
580
|
fill: headerBackgroundColor,
|
|
505
581
|
};
|
|
506
582
|
}
|
|
507
583
|
else if (!isHeaderRow && rowBackgroundColor) {
|
|
508
584
|
shading = {
|
|
509
|
-
type: ShadingType.
|
|
510
|
-
color:
|
|
585
|
+
type: ShadingType.CLEAR,
|
|
586
|
+
color: "auto",
|
|
511
587
|
fill: rowBackgroundColor,
|
|
512
588
|
};
|
|
513
589
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
type: ShadingType.SOLID,
|
|
518
|
-
color: "F9FAFB",
|
|
519
|
-
fill: "F9FAFB",
|
|
520
|
-
};
|
|
521
|
-
}
|
|
590
|
+
// Note: No default header shading is applied when the CSS doesn't specify one.
|
|
591
|
+
// Previously, a light gray F9FAFB was applied to all header rows, but this added
|
|
592
|
+
// unwanted shading to tables in clean/minimal designs.
|
|
522
593
|
// When this row has fewer cells than the table's column count,
|
|
523
594
|
// the last cell must span the remaining grid columns via columnSpan.
|
|
524
595
|
// Without this, the OOXML is structurally invalid (cells don't cover
|
|
@@ -604,16 +675,7 @@ export function convertElementToDocx(element) {
|
|
|
604
675
|
const spacingKey = `h${element.level}`;
|
|
605
676
|
const headingSpacing = HEADING_SPACING[spacingKey] || { before: 240, after: 240 };
|
|
606
677
|
// Apply text-transform if specified
|
|
607
|
-
|
|
608
|
-
if (element.textTransform === "uppercase") {
|
|
609
|
-
displayText = displayText.toUpperCase();
|
|
610
|
-
}
|
|
611
|
-
else if (element.textTransform === "lowercase") {
|
|
612
|
-
displayText = displayText.toLowerCase();
|
|
613
|
-
}
|
|
614
|
-
else if (element.textTransform === "capitalize") {
|
|
615
|
-
displayText = displayText.replace(/\b\w/g, c => c.toUpperCase());
|
|
616
|
-
}
|
|
678
|
+
const displayText = applyTextTransform(element.text, element.textTransform);
|
|
617
679
|
// Use runs array if available (for headings with inline badges/badges)
|
|
618
680
|
// Otherwise fallback to simple text with color
|
|
619
681
|
let children;
|
|
@@ -630,9 +692,17 @@ export function convertElementToDocx(element) {
|
|
|
630
692
|
if (element.fontFamily) {
|
|
631
693
|
textRunOptions.font = element.fontFamily;
|
|
632
694
|
}
|
|
695
|
+
// Apply font-size from CSS (overrides document style defaults)
|
|
696
|
+
if (element.fontSize) {
|
|
697
|
+
textRunOptions.size = element.fontSize;
|
|
698
|
+
}
|
|
699
|
+
// Apply letter-spacing from CSS
|
|
700
|
+
if (element.letterSpacing) {
|
|
701
|
+
textRunOptions.characterSpacing = element.letterSpacing;
|
|
702
|
+
}
|
|
633
703
|
children = [new TextRun(textRunOptions)];
|
|
634
704
|
}
|
|
635
|
-
// Build paragraph options with optional border-bottom
|
|
705
|
+
// Build paragraph options with optional border-bottom and border-left
|
|
636
706
|
const paragraphOptions = {
|
|
637
707
|
children,
|
|
638
708
|
heading: headingLevel,
|
|
@@ -641,30 +711,42 @@ export function convertElementToDocx(element) {
|
|
|
641
711
|
// GENERALIZED: Use lineSpacing from CSS if available, otherwise use default
|
|
642
712
|
line: element.lineSpacing !== undefined ? element.lineSpacing : HEADING_LINE_SPACING.line,
|
|
643
713
|
lineRule: HEADING_LINE_SPACING.lineRule,
|
|
644
|
-
before: headingSpacing.before,
|
|
714
|
+
before: element.spacingBefore !== undefined ? element.spacingBefore : headingSpacing.before,
|
|
645
715
|
after: element.spacingAfter !== undefined ? element.spacingAfter : headingSpacing.after,
|
|
646
716
|
},
|
|
647
717
|
};
|
|
648
718
|
// Add border-bottom if present (heading underline style)
|
|
649
719
|
if (element.borderBottom) {
|
|
650
720
|
paragraphOptions.border = {
|
|
721
|
+
...paragraphOptions.border,
|
|
651
722
|
bottom: { style: BorderStyle.SINGLE, size: 6, color: element.borderBottom },
|
|
652
723
|
};
|
|
653
724
|
}
|
|
725
|
+
// Add border-left if present (heading left accent bar)
|
|
726
|
+
if (element.borderLeft) {
|
|
727
|
+
// Use CSS-extracted border width if available, otherwise default to 24 (3pt ≈ 4px)
|
|
728
|
+
const borderLeftSize = element.borderLeftWidth || 24;
|
|
729
|
+
paragraphOptions.border = {
|
|
730
|
+
...paragraphOptions.border,
|
|
731
|
+
left: { style: BorderStyle.SINGLE, size: borderLeftSize, color: element.borderLeft, space: 8 },
|
|
732
|
+
};
|
|
733
|
+
// Add left indent to give space for the accent bar
|
|
734
|
+
paragraphOptions.indent = { left: convertInchesToTwip(0.15) };
|
|
735
|
+
}
|
|
736
|
+
// Add background color if present (heading with highlight background)
|
|
737
|
+
if (element.backgroundColor) {
|
|
738
|
+
const bgColor = element.backgroundColor.replace("#", "");
|
|
739
|
+
paragraphOptions.shading = {
|
|
740
|
+
type: ShadingType.CLEAR,
|
|
741
|
+
color: "auto",
|
|
742
|
+
fill: bgColor,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
654
745
|
return [new Paragraph(paragraphOptions)];
|
|
655
746
|
}
|
|
656
747
|
case "paragraph": {
|
|
657
748
|
// Apply text-transform if specified
|
|
658
|
-
|
|
659
|
-
if (element.textTransform === "uppercase") {
|
|
660
|
-
displayText = displayText.toUpperCase();
|
|
661
|
-
}
|
|
662
|
-
else if (element.textTransform === "lowercase") {
|
|
663
|
-
displayText = displayText.toLowerCase();
|
|
664
|
-
}
|
|
665
|
-
else if (element.textTransform === "capitalize") {
|
|
666
|
-
displayText = displayText.replace(/\b\w/g, c => c.toUpperCase());
|
|
667
|
-
}
|
|
749
|
+
const displayText = applyTextTransform(element.text, element.textTransform);
|
|
668
750
|
// Use runs array if available for inline formatting, otherwise fallback to simple text
|
|
669
751
|
const children = element.runs
|
|
670
752
|
? inlineRunsToTextRuns(element.runs, element.textTransform)
|
|
@@ -686,12 +768,18 @@ export function convertElementToDocx(element) {
|
|
|
686
768
|
if (element.hangingIndent) {
|
|
687
769
|
// Hanging indent: left margin equals the hanging amount, first line outdented
|
|
688
770
|
indent.hanging = element.hangingIndent;
|
|
689
|
-
indent.left = element.hangingIndent;
|
|
771
|
+
indent.left = element.leftIndent || element.hangingIndent;
|
|
772
|
+
}
|
|
773
|
+
else if (element.leftIndent) {
|
|
774
|
+
// Left indent without hanging (plain padding-left)
|
|
775
|
+
indent.left = element.leftIndent;
|
|
690
776
|
}
|
|
691
777
|
// GENERALIZED: Use element's spacingAfter if provided, otherwise default
|
|
692
778
|
const afterSpacing = element.spacingAfter !== undefined ? element.spacingAfter : PARAGRAPH_SPACING.after;
|
|
693
779
|
// GENERALIZED: Use element's lineSpacing if provided, otherwise default
|
|
694
780
|
const lineSpacing = element.lineSpacing !== undefined ? element.lineSpacing : PARAGRAPH_SPACING.line;
|
|
781
|
+
// GENERALIZED: Use element's spacingBefore if provided
|
|
782
|
+
const beforeSpacing = element.spacingBefore !== undefined ? element.spacingBefore : undefined;
|
|
695
783
|
return [
|
|
696
784
|
new Paragraph({
|
|
697
785
|
children,
|
|
@@ -701,6 +789,7 @@ export function convertElementToDocx(element) {
|
|
|
701
789
|
line: lineSpacing,
|
|
702
790
|
lineRule: PARAGRAPH_SPACING.lineRule,
|
|
703
791
|
after: afterSpacing,
|
|
792
|
+
before: beforeSpacing,
|
|
704
793
|
},
|
|
705
794
|
}),
|
|
706
795
|
];
|
|
@@ -727,7 +816,9 @@ export function convertElementToDocx(element) {
|
|
|
727
816
|
// strict parsers like Azure Document Intelligence reject.
|
|
728
817
|
const maxColumnCount = Math.max(...element.rows.map(row => row.length));
|
|
729
818
|
const tableRows = element.rows.map((row, rowIndex) => createTableRow(row, useHeaderStyling && rowIndex === 0, maxColumnCount, element.cellPadding, element.headerBackgroundColor, element.headerTextColor,
|
|
730
|
-
// Apply even row background color for alternating row styling
|
|
819
|
+
// Apply even row background color for alternating row styling
|
|
820
|
+
// Header is at rowIndex 0, so body rows start at rowIndex 1.
|
|
821
|
+
// CSS nth-child(even) targets the 2nd,4th,6th body rows = rowIndex 2,4,6 (even indices)
|
|
731
822
|
rowIndex > 0 && rowIndex % 2 === 0 ? element.evenRowBackgroundColor : undefined));
|
|
732
823
|
// Default table border color (light gray), or no borders
|
|
733
824
|
const borderColor = "E5E7EB";
|
|
@@ -735,6 +826,9 @@ export function convertElementToDocx(element) {
|
|
|
735
826
|
const borderStyle = element.noBorders
|
|
736
827
|
? noBorderStyle
|
|
737
828
|
: { style: BorderStyle.SINGLE, size: 4, color: borderColor };
|
|
829
|
+
// Support horizontal-only borders (no vertical grid lines)
|
|
830
|
+
const verticalBorderStyle = element.horizontalBordersOnly ? noBorderStyle : borderStyle;
|
|
831
|
+
const sideBorderStyle = element.horizontalBordersOnly ? noBorderStyle : borderStyle;
|
|
738
832
|
const dataTable = new Table({
|
|
739
833
|
rows: tableRows,
|
|
740
834
|
width: {
|
|
@@ -744,10 +838,10 @@ export function convertElementToDocx(element) {
|
|
|
744
838
|
borders: {
|
|
745
839
|
top: borderStyle,
|
|
746
840
|
bottom: borderStyle,
|
|
747
|
-
left:
|
|
748
|
-
right:
|
|
841
|
+
left: sideBorderStyle,
|
|
842
|
+
right: sideBorderStyle,
|
|
749
843
|
insideHorizontal: borderStyle,
|
|
750
|
-
insideVertical:
|
|
844
|
+
insideVertical: verticalBorderStyle,
|
|
751
845
|
},
|
|
752
846
|
});
|
|
753
847
|
// Add spacing paragraphs before and after the table to ensure proper separation
|
|
@@ -779,6 +873,8 @@ export function convertElementToDocx(element) {
|
|
|
779
873
|
right: { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" },
|
|
780
874
|
},
|
|
781
875
|
shading: {
|
|
876
|
+
type: ShadingType.CLEAR,
|
|
877
|
+
color: "auto",
|
|
782
878
|
fill: "F5F5F5",
|
|
783
879
|
},
|
|
784
880
|
}),
|
|
@@ -794,9 +890,13 @@ export function convertElementToDocx(element) {
|
|
|
794
890
|
const isCallout = element.variant === "callout";
|
|
795
891
|
const hasFullBorder = element.borderStyle === "full";
|
|
796
892
|
const hasNoBorder = element.borderStyle === "none";
|
|
797
|
-
// Callout label uses
|
|
798
|
-
|
|
799
|
-
//
|
|
893
|
+
// Callout label uses the border color to match the accent style
|
|
894
|
+
// Previously hardcoded to blue (#1d4ed8), now uses the actual border color
|
|
895
|
+
// so labels match their callout's theme (brown for objectives, blue for examples, etc.)
|
|
896
|
+
// For borderless callouts (style="none"), don't use the gray fallback — let the
|
|
897
|
+
// heading/paragraph use its own color or the document default.
|
|
898
|
+
const labelColor = hasNoBorder ? undefined : borderColor;
|
|
899
|
+
// Convert all inner content to paragraphs (and tables for nested blockquotes) for the cell
|
|
800
900
|
const cellContent = [];
|
|
801
901
|
for (const innerElement of element.content) {
|
|
802
902
|
if (innerElement.type === "paragraph") {
|
|
@@ -812,14 +912,19 @@ export function convertElementToDocx(element) {
|
|
|
812
912
|
bold: innerElement.bold,
|
|
813
913
|
italics: innerElement.italic,
|
|
814
914
|
color: paragraphColor,
|
|
915
|
+
font: innerElement.fontFamily,
|
|
815
916
|
}),
|
|
816
917
|
];
|
|
817
918
|
cellContent.push(new Paragraph({
|
|
818
919
|
children: textChildren,
|
|
920
|
+
alignment: innerElement.alignment ? textAlignmentToDocx(innerElement.alignment) : undefined,
|
|
819
921
|
spacing: {
|
|
820
|
-
line
|
|
922
|
+
// Use paragraph's CSS line-height if available, otherwise default
|
|
923
|
+
line: innerElement.lineSpacing !== undefined ? innerElement.lineSpacing : PARAGRAPH_SPACING.line,
|
|
821
924
|
lineRule: PARAGRAPH_SPACING.lineRule,
|
|
822
|
-
|
|
925
|
+
// Use paragraph's CSS margin-bottom if available, otherwise compact spacing for boxes
|
|
926
|
+
after: innerElement.spacingAfter !== undefined ? innerElement.spacingAfter : 120,
|
|
927
|
+
before: innerElement.spacingBefore,
|
|
823
928
|
}, // Space between elements inside the box
|
|
824
929
|
}));
|
|
825
930
|
}
|
|
@@ -869,8 +974,10 @@ export function convertElementToDocx(element) {
|
|
|
869
974
|
else if (innerElement.type === "heading") {
|
|
870
975
|
// Handle headings inside blockquotes
|
|
871
976
|
// Use the heading's own color if available (from CSS like .key-takeaways h3 { color: ... })
|
|
872
|
-
//
|
|
873
|
-
|
|
977
|
+
// Only fall back to blockquote's border color when the blockquote has a visible border
|
|
978
|
+
// (left accent or full border). For borderless callouts (style="none"), the heading
|
|
979
|
+
// should use its own color or default — not the gray fallback "CCCCCC".
|
|
980
|
+
const headingColor = innerElement.color || (hasNoBorder ? undefined : borderColor);
|
|
874
981
|
const textChildren = innerElement.runs
|
|
875
982
|
? inlineRunsToTextRuns(innerElement.runs)
|
|
876
983
|
: [
|
|
@@ -924,6 +1031,43 @@ export function convertElementToDocx(element) {
|
|
|
924
1031
|
// For regular tables inside blockquotes, we could add full table rendering,
|
|
925
1032
|
// but for now we skip them as nested tables in Word can be problematic
|
|
926
1033
|
}
|
|
1034
|
+
else if (innerElement.type === "image") {
|
|
1035
|
+
// Render images inside blockquote/callout cells
|
|
1036
|
+
if (innerElement.imageData) {
|
|
1037
|
+
// Scale image to fit within the blockquote cell width (approx 580px after padding)
|
|
1038
|
+
const maxCellWidth = 580;
|
|
1039
|
+
let imgWidth = innerElement.imageData.width;
|
|
1040
|
+
let imgHeight = innerElement.imageData.height;
|
|
1041
|
+
if (imgWidth > maxCellWidth) {
|
|
1042
|
+
const scale = maxCellWidth / imgWidth;
|
|
1043
|
+
imgWidth = maxCellWidth;
|
|
1044
|
+
imgHeight = Math.round(imgHeight * scale);
|
|
1045
|
+
}
|
|
1046
|
+
cellContent.push(new Paragraph({
|
|
1047
|
+
children: [
|
|
1048
|
+
new ImageRun({
|
|
1049
|
+
data: innerElement.imageData.data,
|
|
1050
|
+
transformation: {
|
|
1051
|
+
width: imgWidth,
|
|
1052
|
+
height: imgHeight,
|
|
1053
|
+
},
|
|
1054
|
+
type: "png",
|
|
1055
|
+
}),
|
|
1056
|
+
],
|
|
1057
|
+
alignment: AlignmentType.CENTER,
|
|
1058
|
+
spacing: {
|
|
1059
|
+
before: 120,
|
|
1060
|
+
after: 120,
|
|
1061
|
+
},
|
|
1062
|
+
}));
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
else if (innerElement.type === "blockquote") {
|
|
1066
|
+
// Handle nested blockquotes/callouts (e.g., .famous-for inside .country-section)
|
|
1067
|
+
// Render the nested blockquote as its own table-based box within the parent cell
|
|
1068
|
+
const nestedDocx = convertElementToDocx(innerElement);
|
|
1069
|
+
cellContent.push(...nestedDocx);
|
|
1070
|
+
}
|
|
927
1071
|
}
|
|
928
1072
|
// Create table to act as the box
|
|
929
1073
|
// For gradient backgrounds, the DrawingML shape is already inserted in the first paragraph
|
|
@@ -943,6 +1087,7 @@ export function convertElementToDocx(element) {
|
|
|
943
1087
|
let effectiveBackgroundColor = backgroundColor;
|
|
944
1088
|
if (backgroundGradient && hasNoBorder) {
|
|
945
1089
|
// Use the color at ~40% position (second stop in typical gradients)
|
|
1090
|
+
// This provides a mid-tone that better approximates the gradient's overall appearance
|
|
946
1091
|
const sortedStops = [...backgroundGradient.stops].sort((a, b) => a.position - b.position);
|
|
947
1092
|
if (sortedStops.length >= 2) {
|
|
948
1093
|
effectiveBackgroundColor = sortedStops[1]?.color || sortedStops[0]?.color || backgroundColor;
|
|
@@ -952,8 +1097,8 @@ export function convertElementToDocx(element) {
|
|
|
952
1097
|
}
|
|
953
1098
|
}
|
|
954
1099
|
const cellShading = {
|
|
955
|
-
type: ShadingType.
|
|
956
|
-
color:
|
|
1100
|
+
type: ShadingType.CLEAR,
|
|
1101
|
+
color: "auto",
|
|
957
1102
|
fill: effectiveBackgroundColor,
|
|
958
1103
|
};
|
|
959
1104
|
tableRows.push(new TableRow({
|
|
@@ -962,18 +1107,15 @@ export function convertElementToDocx(element) {
|
|
|
962
1107
|
children: finalCellContent,
|
|
963
1108
|
shading: cellShading,
|
|
964
1109
|
margins: {
|
|
965
|
-
top: convertInchesToTwip(0.25), // ~18pt padding top
|
|
966
|
-
bottom: convertInchesToTwip(0.25), // ~18pt padding bottom
|
|
967
|
-
left: convertInchesToTwip(0.35), // ~25pt padding left (after border)
|
|
968
|
-
right: convertInchesToTwip(0.35), // ~25pt padding right
|
|
1110
|
+
top: element.padding?.top ?? convertInchesToTwip(0.25), // ~18pt padding top
|
|
1111
|
+
bottom: element.padding?.bottom ?? convertInchesToTwip(0.25), // ~18pt padding bottom
|
|
1112
|
+
left: element.padding?.left ?? convertInchesToTwip(0.35), // ~25pt padding left (after border)
|
|
1113
|
+
right: element.padding?.right ?? convertInchesToTwip(0.35), // ~25pt padding right
|
|
969
1114
|
},
|
|
970
1115
|
borders: hasNoBorder
|
|
971
1116
|
? {
|
|
972
1117
|
// No border - just background color (for title blocks, hero sections)
|
|
973
|
-
|
|
974
|
-
bottom: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
975
|
-
left: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
976
|
-
right: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1118
|
+
...NIL_BORDERS,
|
|
977
1119
|
}
|
|
978
1120
|
: hasFullBorder
|
|
979
1121
|
? {
|
|
@@ -985,10 +1127,8 @@ export function convertElementToDocx(element) {
|
|
|
985
1127
|
}
|
|
986
1128
|
: {
|
|
987
1129
|
// Left accent border only (callout style)
|
|
988
|
-
|
|
989
|
-
bottom: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1130
|
+
...NIL_BORDERS,
|
|
990
1131
|
left: { style: BorderStyle.SINGLE, size: 24, color: borderColor }, // Thick left border
|
|
991
|
-
right: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
992
1132
|
},
|
|
993
1133
|
}),
|
|
994
1134
|
],
|
|
@@ -1000,14 +1140,7 @@ export function convertElementToDocx(element) {
|
|
|
1000
1140
|
type: WidthType.PERCENTAGE,
|
|
1001
1141
|
},
|
|
1002
1142
|
// Remove outer table borders - we only want the cell's left border
|
|
1003
|
-
borders:
|
|
1004
|
-
top: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1005
|
-
bottom: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1006
|
-
left: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1007
|
-
right: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1008
|
-
insideHorizontal: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1009
|
-
insideVertical: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1010
|
-
},
|
|
1143
|
+
borders: NIL_TABLE_BORDERS,
|
|
1011
1144
|
});
|
|
1012
1145
|
// Add spacing paragraphs before and after the table to ensure proper separation
|
|
1013
1146
|
// from surrounding content (especially other tables)
|
|
@@ -1021,68 +1154,10 @@ export function convertElementToDocx(element) {
|
|
|
1021
1154
|
});
|
|
1022
1155
|
return [spacingBefore, boxTable, spacingAfter];
|
|
1023
1156
|
}
|
|
1024
|
-
case "chart-placeholder":
|
|
1025
|
-
// Render a placeholder for charts that can't be converted
|
|
1026
|
-
const text = element.title ? `[Chart: ${element.title}]` : "[Chart]";
|
|
1027
|
-
return [
|
|
1028
|
-
new Paragraph({
|
|
1029
|
-
children: [
|
|
1030
|
-
new TextRun({
|
|
1031
|
-
text,
|
|
1032
|
-
italics: true,
|
|
1033
|
-
color: "808080",
|
|
1034
|
-
}),
|
|
1035
|
-
],
|
|
1036
|
-
alignment: AlignmentType.CENTER,
|
|
1037
|
-
border: {
|
|
1038
|
-
top: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1039
|
-
bottom: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1040
|
-
left: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1041
|
-
right: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1042
|
-
},
|
|
1043
|
-
shading: {
|
|
1044
|
-
type: ShadingType.SOLID,
|
|
1045
|
-
color: "F9FAFB",
|
|
1046
|
-
fill: "F9FAFB",
|
|
1047
|
-
},
|
|
1048
|
-
spacing: {
|
|
1049
|
-
before: 200,
|
|
1050
|
-
after: 200,
|
|
1051
|
-
},
|
|
1052
|
-
}),
|
|
1053
|
-
];
|
|
1054
|
-
}
|
|
1157
|
+
case "chart-placeholder":
|
|
1055
1158
|
case "svg-chart": {
|
|
1056
|
-
//
|
|
1057
|
-
|
|
1058
|
-
const text = element.title ? `[Chart: ${element.title}]` : "[Chart]";
|
|
1059
|
-
return [
|
|
1060
|
-
new Paragraph({
|
|
1061
|
-
children: [
|
|
1062
|
-
new TextRun({
|
|
1063
|
-
text,
|
|
1064
|
-
italics: true,
|
|
1065
|
-
color: "808080",
|
|
1066
|
-
}),
|
|
1067
|
-
],
|
|
1068
|
-
alignment: AlignmentType.CENTER,
|
|
1069
|
-
border: {
|
|
1070
|
-
top: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1071
|
-
bottom: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1072
|
-
left: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1073
|
-
right: { style: BorderStyle.SINGLE, size: 1, color: "E5E7EB" },
|
|
1074
|
-
},
|
|
1075
|
-
shading: {
|
|
1076
|
-
type: ShadingType.SOLID,
|
|
1077
|
-
color: "F9FAFB",
|
|
1078
|
-
fill: "F9FAFB",
|
|
1079
|
-
},
|
|
1080
|
-
spacing: {
|
|
1081
|
-
before: 200,
|
|
1082
|
-
after: 200,
|
|
1083
|
-
},
|
|
1084
|
-
}),
|
|
1085
|
-
];
|
|
1159
|
+
// Render a placeholder for charts that can't be converted (or SVGs not yet rendered)
|
|
1160
|
+
return [createChartPlaceholderParagraph(element.title)];
|
|
1086
1161
|
}
|
|
1087
1162
|
case "chart-image": {
|
|
1088
1163
|
// Render chart as an embedded image
|
|
@@ -1129,24 +1204,7 @@ export function convertElementToDocx(element) {
|
|
|
1129
1204
|
after: caption ? 80 : 200,
|
|
1130
1205
|
},
|
|
1131
1206
|
}),
|
|
1132
|
-
...(caption
|
|
1133
|
-
? [
|
|
1134
|
-
new Paragraph({
|
|
1135
|
-
children: [
|
|
1136
|
-
new TextRun({
|
|
1137
|
-
text: caption,
|
|
1138
|
-
italics: true,
|
|
1139
|
-
size: 20, // 10pt
|
|
1140
|
-
color: "6B7280",
|
|
1141
|
-
}),
|
|
1142
|
-
],
|
|
1143
|
-
alignment: AlignmentType.CENTER,
|
|
1144
|
-
spacing: {
|
|
1145
|
-
after: 200,
|
|
1146
|
-
},
|
|
1147
|
-
}),
|
|
1148
|
-
]
|
|
1149
|
-
: []),
|
|
1207
|
+
...(caption ? [createCaptionParagraph(caption)] : []),
|
|
1150
1208
|
];
|
|
1151
1209
|
}
|
|
1152
1210
|
// Render the actual image
|
|
@@ -1171,20 +1229,7 @@ export function convertElementToDocx(element) {
|
|
|
1171
1229
|
];
|
|
1172
1230
|
// Add caption if present
|
|
1173
1231
|
if (caption) {
|
|
1174
|
-
paragraphs.push(
|
|
1175
|
-
children: [
|
|
1176
|
-
new TextRun({
|
|
1177
|
-
text: caption,
|
|
1178
|
-
italics: true,
|
|
1179
|
-
size: 20, // 10pt
|
|
1180
|
-
color: "6B7280",
|
|
1181
|
-
}),
|
|
1182
|
-
],
|
|
1183
|
-
alignment: AlignmentType.CENTER,
|
|
1184
|
-
spacing: {
|
|
1185
|
-
after: 200,
|
|
1186
|
-
},
|
|
1187
|
-
}));
|
|
1232
|
+
paragraphs.push(createCaptionParagraph(caption));
|
|
1188
1233
|
}
|
|
1189
1234
|
return paragraphs;
|
|
1190
1235
|
}
|
|
@@ -1271,8 +1316,8 @@ export function convertElementToDocx(element) {
|
|
|
1271
1316
|
right: convertInchesToTwip(0.1),
|
|
1272
1317
|
},
|
|
1273
1318
|
shading: card.backgroundColor ? {
|
|
1274
|
-
type: ShadingType.
|
|
1275
|
-
color:
|
|
1319
|
+
type: ShadingType.CLEAR,
|
|
1320
|
+
color: "auto",
|
|
1276
1321
|
fill: card.backgroundColor,
|
|
1277
1322
|
} : undefined,
|
|
1278
1323
|
borders: card.borderColor ? {
|
|
@@ -1349,8 +1394,8 @@ export function convertElementToDocx(element) {
|
|
|
1349
1394
|
type: WidthType.PERCENTAGE,
|
|
1350
1395
|
},
|
|
1351
1396
|
shading: sidebar.backgroundColor ? {
|
|
1352
|
-
type: ShadingType.
|
|
1353
|
-
color:
|
|
1397
|
+
type: ShadingType.CLEAR,
|
|
1398
|
+
color: "auto",
|
|
1354
1399
|
fill: sidebar.backgroundColor,
|
|
1355
1400
|
} : undefined,
|
|
1356
1401
|
margins: {
|
|
@@ -1359,12 +1404,7 @@ export function convertElementToDocx(element) {
|
|
|
1359
1404
|
left: convertInchesToTwip(0.2),
|
|
1360
1405
|
right: convertInchesToTwip(0.2),
|
|
1361
1406
|
},
|
|
1362
|
-
borders:
|
|
1363
|
-
top: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1364
|
-
bottom: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1365
|
-
left: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1366
|
-
right: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1367
|
-
},
|
|
1407
|
+
borders: NIL_BORDERS,
|
|
1368
1408
|
});
|
|
1369
1409
|
// Create main content cell
|
|
1370
1410
|
const mainCell = new DocxTableCell({
|
|
@@ -1379,12 +1419,7 @@ export function convertElementToDocx(element) {
|
|
|
1379
1419
|
left: convertInchesToTwip(0.3),
|
|
1380
1420
|
right: convertInchesToTwip(0.2),
|
|
1381
1421
|
},
|
|
1382
|
-
borders:
|
|
1383
|
-
top: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1384
|
-
bottom: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1385
|
-
left: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1386
|
-
right: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1387
|
-
},
|
|
1422
|
+
borders: NIL_BORDERS,
|
|
1388
1423
|
});
|
|
1389
1424
|
// Create the two-column table
|
|
1390
1425
|
const twoColumnTable = new Table({
|
|
@@ -1397,14 +1432,7 @@ export function convertElementToDocx(element) {
|
|
|
1397
1432
|
size: 100,
|
|
1398
1433
|
type: WidthType.PERCENTAGE,
|
|
1399
1434
|
},
|
|
1400
|
-
borders:
|
|
1401
|
-
top: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1402
|
-
bottom: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1403
|
-
left: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1404
|
-
right: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1405
|
-
insideHorizontal: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1406
|
-
insideVertical: { style: BorderStyle.NIL, size: 0, color: "FFFFFF" },
|
|
1407
|
-
},
|
|
1435
|
+
borders: NIL_TABLE_BORDERS,
|
|
1408
1436
|
});
|
|
1409
1437
|
return [twoColumnTable];
|
|
1410
1438
|
}
|