@cj-tech-master/excelts 9.5.0 → 9.5.1
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/browser/modules/pdf/excel-bridge.js +27 -1
- package/dist/browser/modules/pdf/render/layout-engine.js +74 -9
- package/dist/browser/modules/pdf/render/style-converter.d.ts +1 -1
- package/dist/browser/modules/pdf/render/style-converter.js +98 -1
- package/dist/browser/modules/pdf/types.d.ts +1 -0
- package/dist/browser/modules/word/color-utils.d.ts +18 -0
- package/dist/browser/modules/word/color-utils.js +94 -0
- package/dist/browser/modules/word/content-types.d.ts +15 -15
- package/dist/browser/modules/word/content-types.js +39 -43
- package/dist/browser/modules/word/crypto.d.ts +17 -0
- package/dist/browser/modules/word/crypto.js +18 -0
- package/dist/browser/modules/word/document-io.d.ts +58 -0
- package/dist/browser/modules/word/document-io.js +239 -0
- package/dist/browser/modules/word/document.d.ts +64 -135
- package/dist/browser/modules/word/document.js +207 -469
- package/dist/browser/modules/word/docx-packager.js +90 -90
- package/dist/browser/modules/word/html-renderer.js +1 -1
- package/dist/browser/modules/word/html.d.ts +13 -0
- package/dist/browser/modules/word/html.js +12 -0
- package/dist/browser/modules/word/index.base.d.ts +6 -9
- package/dist/browser/modules/word/index.base.js +7 -10
- package/dist/browser/modules/word/namespaces.d.ts +159 -0
- package/dist/browser/modules/word/namespaces.js +189 -0
- package/dist/browser/modules/word/relationships.d.ts +15 -16
- package/dist/browser/modules/word/relationships.js +37 -45
- package/dist/cjs/modules/pdf/excel-bridge.js +27 -1
- package/dist/cjs/modules/pdf/render/layout-engine.js +74 -9
- package/dist/cjs/modules/pdf/render/style-converter.js +98 -1
- package/dist/cjs/modules/word/color-utils.js +97 -0
- package/dist/cjs/modules/word/content-types.js +44 -45
- package/dist/cjs/modules/word/crypto.js +34 -0
- package/dist/cjs/modules/word/document-io.js +244 -0
- package/dist/cjs/modules/word/document.js +209 -473
- package/dist/cjs/modules/word/docx-packager.js +88 -88
- package/dist/cjs/modules/word/html-renderer.js +2 -2
- package/dist/cjs/modules/word/html.js +16 -0
- package/dist/cjs/modules/word/index.base.js +17 -27
- package/dist/cjs/modules/word/namespaces.js +192 -0
- package/dist/cjs/modules/word/relationships.js +42 -47
- package/dist/esm/modules/pdf/excel-bridge.js +27 -1
- package/dist/esm/modules/pdf/render/layout-engine.js +74 -9
- package/dist/esm/modules/pdf/render/style-converter.js +98 -1
- package/dist/esm/modules/word/color-utils.js +94 -0
- package/dist/esm/modules/word/content-types.js +39 -43
- package/dist/esm/modules/word/crypto.js +18 -0
- package/dist/esm/modules/word/document-io.js +239 -0
- package/dist/esm/modules/word/document.js +207 -469
- package/dist/esm/modules/word/docx-packager.js +90 -90
- package/dist/esm/modules/word/html-renderer.js +1 -1
- package/dist/esm/modules/word/html.js +12 -0
- package/dist/esm/modules/word/index.base.js +7 -10
- package/dist/esm/modules/word/namespaces.js +189 -0
- package/dist/esm/modules/word/relationships.js +37 -45
- package/dist/iife/excelts.iife.js +153 -11
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +4 -4
- package/dist/types/modules/pdf/render/style-converter.d.ts +1 -1
- package/dist/types/modules/pdf/types.d.ts +1 -0
- package/dist/types/modules/word/color-utils.d.ts +18 -0
- package/dist/types/modules/word/content-types.d.ts +15 -15
- package/dist/types/modules/word/crypto.d.ts +17 -0
- package/dist/types/modules/word/document-io.d.ts +58 -0
- package/dist/types/modules/word/document.d.ts +64 -135
- package/dist/types/modules/word/html.d.ts +13 -0
- package/dist/types/modules/word/index.base.d.ts +6 -9
- package/dist/types/modules/word/namespaces.d.ts +159 -0
- package/dist/types/modules/word/relationships.d.ts +15 -16
- package/package.json +1 -1
|
@@ -187,6 +187,31 @@ async function convertSheet(ws, workbook) {
|
|
|
187
187
|
right: dimensions.model.right
|
|
188
188
|
}
|
|
189
189
|
: { top: 0, left: 0, bottom: 0, right: 0 };
|
|
190
|
+
// Expand bounds to include cells that only have styles (borders, fills, fonts)
|
|
191
|
+
// but no values — these are not tracked by dimensions.
|
|
192
|
+
if (hasData) {
|
|
193
|
+
for (let r = bounds.top; r <= bounds.bottom; r++) {
|
|
194
|
+
const row = ws.findRow(r);
|
|
195
|
+
if (!row) {
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
row.eachCell({ includeEmpty: true }, cell => {
|
|
199
|
+
if (cell.col > bounds.right) {
|
|
200
|
+
const hasStyle = cell.style &&
|
|
201
|
+
((cell.style.border &&
|
|
202
|
+
(cell.style.border.top ||
|
|
203
|
+
cell.style.border.right ||
|
|
204
|
+
cell.style.border.bottom ||
|
|
205
|
+
cell.style.border.left)) ||
|
|
206
|
+
cell.style.fill ||
|
|
207
|
+
cell.style.font);
|
|
208
|
+
if (hasStyle || (cell.type !== ValueType.Null && cell.type !== ValueType.Merge)) {
|
|
209
|
+
bounds.right = cell.col;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
190
215
|
// Convert columns
|
|
191
216
|
const columns = new Map();
|
|
192
217
|
if (hasData) {
|
|
@@ -480,7 +505,8 @@ function convertColor(color) {
|
|
|
480
505
|
return {
|
|
481
506
|
argb: color.argb,
|
|
482
507
|
theme: color.theme,
|
|
483
|
-
tint: color.tint
|
|
508
|
+
tint: color.tint,
|
|
509
|
+
indexed: color.indexed
|
|
484
510
|
};
|
|
485
511
|
}
|
|
486
512
|
function convertFill(fill) {
|
|
@@ -551,7 +551,7 @@ function countWrapLines(cell, fontSize, scaleFactor, sheet, fontManager, options
|
|
|
551
551
|
if (value && typeof value === "object" && "richText" in value) {
|
|
552
552
|
const runs = value.richText;
|
|
553
553
|
if (runs.length > 0) {
|
|
554
|
-
const wrappedCount = countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontManager, options);
|
|
554
|
+
const wrappedCount = countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontManager, options, cell.style?.font);
|
|
555
555
|
return Math.max(lineCount, wrappedCount);
|
|
556
556
|
}
|
|
557
557
|
}
|
|
@@ -571,7 +571,10 @@ function countWrapLines(cell, fontSize, scaleFactor, sheet, fontManager, options
|
|
|
571
571
|
* This mirrors the logic in wrapRichTextLines (page-renderer) so that
|
|
572
572
|
* the row height calculation matches the actual rendering.
|
|
573
573
|
*/
|
|
574
|
-
function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontManager, options) {
|
|
574
|
+
function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontManager, options, cellFont) {
|
|
575
|
+
// Use cell-level font as fallback for runs without their own font
|
|
576
|
+
const defaultFamily = cellFont?.name ?? options.defaultFontFamily;
|
|
577
|
+
const defaultSize = cellFont?.size ?? options.defaultFontSize;
|
|
575
578
|
// Build character-to-run mapping
|
|
576
579
|
const runForChar = [];
|
|
577
580
|
for (let ri = 0; ri < runs.length; ri++) {
|
|
@@ -579,9 +582,20 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
|
|
|
579
582
|
runForChar.push(ri);
|
|
580
583
|
}
|
|
581
584
|
}
|
|
582
|
-
// Resolve font resources for each run
|
|
585
|
+
// Resolve font resources for each run (with cell font inheritance)
|
|
583
586
|
const runResources = runs.map(run => {
|
|
584
|
-
const
|
|
587
|
+
const effectiveRunFont = run.font
|
|
588
|
+
? {
|
|
589
|
+
name: run.font.name ?? cellFont?.name,
|
|
590
|
+
size: run.font.size ?? cellFont?.size,
|
|
591
|
+
bold: run.font.bold ?? cellFont?.bold,
|
|
592
|
+
italic: run.font.italic ?? cellFont?.italic,
|
|
593
|
+
strike: run.font.strike ?? cellFont?.strike,
|
|
594
|
+
underline: run.font.underline ?? cellFont?.underline,
|
|
595
|
+
color: run.font.color ?? cellFont?.color
|
|
596
|
+
}
|
|
597
|
+
: cellFont;
|
|
598
|
+
const fontProps = extractFontProperties(effectiveRunFont, defaultFamily, defaultSize);
|
|
585
599
|
const pdfFontName = resolvePdfFontName(fontProps.fontFamily, fontProps.bold, fontProps.italic);
|
|
586
600
|
return fontManager.hasEmbeddedFont()
|
|
587
601
|
? fontManager.getEmbeddedResourceName()
|
|
@@ -589,7 +603,15 @@ function countRichTextWrapLines(text, runs, scaleFactor, effectiveWidth, fontMan
|
|
|
589
603
|
});
|
|
590
604
|
// Resolve scaled font sizes for each run
|
|
591
605
|
const runFontSizes = runs.map(run => {
|
|
592
|
-
const
|
|
606
|
+
const effectiveRunFont = run.font
|
|
607
|
+
? {
|
|
608
|
+
name: run.font.name ?? cellFont?.name,
|
|
609
|
+
size: run.font.size ?? cellFont?.size,
|
|
610
|
+
bold: run.font.bold ?? cellFont?.bold,
|
|
611
|
+
italic: run.font.italic ?? cellFont?.italic
|
|
612
|
+
}
|
|
613
|
+
: cellFont;
|
|
614
|
+
const fontProps = extractFontProperties(effectiveRunFont, defaultFamily, defaultSize);
|
|
593
615
|
return fontProps.fontSize * scaleFactor;
|
|
594
616
|
});
|
|
595
617
|
// Measure a range of fullText using per-character run font sizes
|
|
@@ -861,8 +883,10 @@ function buildLayoutCell(cell, x, y, width, height, colSpan, rowSpan, options, f
|
|
|
861
883
|
// Track non-WinAnsi code points for Type3 fallback font generation
|
|
862
884
|
fontManager.trackText(text);
|
|
863
885
|
}
|
|
864
|
-
// Rich text runs
|
|
865
|
-
|
|
886
|
+
// Rich text runs — pass cell-level font as the fallback for runs without
|
|
887
|
+
// their own font definition (e.g. the first run often has no font object
|
|
888
|
+
// and should inherit the cell's style font including bold/italic).
|
|
889
|
+
const richText = buildRichTextRuns(cell, options, fontManager, scaleFactor, style.font);
|
|
866
890
|
const borders = excelBordersToPdf(style.border);
|
|
867
891
|
return {
|
|
868
892
|
text,
|
|
@@ -1305,6 +1329,30 @@ function computeTextOverflows(cellGrid, rowPage, colGroup, visibleRows, visibleC
|
|
|
1305
1329
|
}
|
|
1306
1330
|
if (overflowAvailable > 0) {
|
|
1307
1331
|
cell.textOverflowWidth = Math.min(overflowNeeded, overflowAvailable);
|
|
1332
|
+
// Hide internal vertical borders in the overflow region.
|
|
1333
|
+
// In Excel, when text overflows into adjacent empty cells, the shared
|
|
1334
|
+
// vertical borders between them are not drawn (the text appears to
|
|
1335
|
+
// span across seamlessly). We suppress:
|
|
1336
|
+
// - The overflowing cell's right border
|
|
1337
|
+
// - Each covered neighbor's left border (and right border if fully covered)
|
|
1338
|
+
let accumulated = 0;
|
|
1339
|
+
const actualOverflow = cell.textOverflowWidth;
|
|
1340
|
+
// Remove the source cell's right border if text overflows
|
|
1341
|
+
cell.borders.right = null;
|
|
1342
|
+
for (let j = gci + 1; j < colGroup.length; j++) {
|
|
1343
|
+
const neighborCell = cellGrid.get(`${ri}:${j}`);
|
|
1344
|
+
if (!neighborCell) {
|
|
1345
|
+
break;
|
|
1346
|
+
}
|
|
1347
|
+
// Remove the neighbor's left border (shared edge with previous cell)
|
|
1348
|
+
neighborCell.borders.left = null;
|
|
1349
|
+
accumulated += groupColWidths[j];
|
|
1350
|
+
if (accumulated >= actualOverflow) {
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
// If fully covered, also remove the neighbor's right border
|
|
1354
|
+
neighborCell.borders.right = null;
|
|
1355
|
+
}
|
|
1308
1356
|
}
|
|
1309
1357
|
}
|
|
1310
1358
|
}
|
|
@@ -1316,7 +1364,7 @@ function computeTextOverflows(cellGrid, rowPage, colGroup, visibleRows, visibleC
|
|
|
1316
1364
|
* Build rich text runs from a RichText cell.
|
|
1317
1365
|
* Returns null for non-RichText cells.
|
|
1318
1366
|
*/
|
|
1319
|
-
function buildRichTextRuns(cell, options, fontManager, scaleFactor) {
|
|
1367
|
+
function buildRichTextRuns(cell, options, fontManager, scaleFactor, cellFont) {
|
|
1320
1368
|
if (!cell || cell.type !== PdfCellType.RichText) {
|
|
1321
1369
|
return null;
|
|
1322
1370
|
}
|
|
@@ -1328,8 +1376,25 @@ function buildRichTextRuns(cell, options, fontManager, scaleFactor) {
|
|
|
1328
1376
|
if (runs.length === 0) {
|
|
1329
1377
|
return null;
|
|
1330
1378
|
}
|
|
1379
|
+
// Use cell-level font as fallback for runs without their own font,
|
|
1380
|
+
// falling back to global defaults only if cell font is not available.
|
|
1381
|
+
const defaultFamily = cellFont?.name ?? options.defaultFontFamily;
|
|
1382
|
+
const defaultSize = cellFont?.size ?? options.defaultFontSize;
|
|
1331
1383
|
return runs.map(run => {
|
|
1332
|
-
|
|
1384
|
+
// When a run has no font at all, use cell font entirely.
|
|
1385
|
+
// When a run has a partial font, merge with cell font for missing properties.
|
|
1386
|
+
const effectiveFont = run.font
|
|
1387
|
+
? {
|
|
1388
|
+
name: run.font.name ?? cellFont?.name,
|
|
1389
|
+
size: run.font.size ?? cellFont?.size,
|
|
1390
|
+
bold: run.font.bold ?? cellFont?.bold,
|
|
1391
|
+
italic: run.font.italic ?? cellFont?.italic,
|
|
1392
|
+
strike: run.font.strike ?? cellFont?.strike,
|
|
1393
|
+
underline: run.font.underline ?? cellFont?.underline,
|
|
1394
|
+
color: run.font.color ?? cellFont?.color
|
|
1395
|
+
}
|
|
1396
|
+
: cellFont;
|
|
1397
|
+
const fontProps = extractFontProperties(effectiveFont, defaultFamily, defaultSize);
|
|
1333
1398
|
// Register font for this run
|
|
1334
1399
|
if (fontManager.hasEmbeddedFont()) {
|
|
1335
1400
|
fontManager.trackText(run.text);
|
|
@@ -16,7 +16,7 @@ import type { PdfColor, LayoutBorders, PdfColorData, PdfFontStyle, PdfFillData,
|
|
|
16
16
|
export declare function argbToPdfColor(argb: string | undefined): PdfColor | null;
|
|
17
17
|
/**
|
|
18
18
|
* Convert a color data object to PDF color.
|
|
19
|
-
* Handles
|
|
19
|
+
* Handles ARGB, theme-based, and indexed colors.
|
|
20
20
|
*/
|
|
21
21
|
export declare function excelColorToPdf(color: Partial<PdfColorData> | undefined): PdfColor | null;
|
|
22
22
|
/**
|
|
@@ -50,7 +50,7 @@ export function argbToPdfColor(argb) {
|
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Convert a color data object to PDF color.
|
|
53
|
-
* Handles
|
|
53
|
+
* Handles ARGB, theme-based, and indexed colors.
|
|
54
54
|
*/
|
|
55
55
|
export function excelColorToPdf(color) {
|
|
56
56
|
if (!color) {
|
|
@@ -72,6 +72,10 @@ export function excelColorToPdf(color) {
|
|
|
72
72
|
}
|
|
73
73
|
return base;
|
|
74
74
|
}
|
|
75
|
+
// Indexed colors (legacy Excel color palette)
|
|
76
|
+
if (color.indexed !== undefined) {
|
|
77
|
+
return indexedColorToPdf(color.indexed);
|
|
78
|
+
}
|
|
75
79
|
return null;
|
|
76
80
|
}
|
|
77
81
|
/**
|
|
@@ -97,6 +101,99 @@ function themeColorToPdf(themeIndex) {
|
|
|
97
101
|
}
|
|
98
102
|
return null;
|
|
99
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Standard Excel indexed color palette (56 colors + system colors).
|
|
106
|
+
* Index 0–7: legacy base colors
|
|
107
|
+
* Index 8–63: standard palette (indices 8–63)
|
|
108
|
+
* Index 64: system foreground (black)
|
|
109
|
+
* Index 65: system background (white)
|
|
110
|
+
*
|
|
111
|
+
* @see ECMA-376 §18.8.27 — indexedColors
|
|
112
|
+
*/
|
|
113
|
+
const INDEXED_COLORS = [
|
|
114
|
+
// 0–7: legacy base colors (same as 8–15 but less commonly used directly)
|
|
115
|
+
"000000", // 0: Black
|
|
116
|
+
"FFFFFF", // 1: White
|
|
117
|
+
"FF0000", // 2: Red
|
|
118
|
+
"00FF00", // 3: Green
|
|
119
|
+
"0000FF", // 4: Blue
|
|
120
|
+
"FFFF00", // 5: Yellow
|
|
121
|
+
"FF00FF", // 6: Magenta
|
|
122
|
+
"00FFFF", // 7: Cyan
|
|
123
|
+
// 8–63: standard palette
|
|
124
|
+
"000000", // 8: Black
|
|
125
|
+
"FFFFFF", // 9: White
|
|
126
|
+
"FF0000", // 10: Red
|
|
127
|
+
"00FF00", // 11: Green
|
|
128
|
+
"0000FF", // 12: Blue
|
|
129
|
+
"FFFF00", // 13: Yellow
|
|
130
|
+
"FF00FF", // 14: Magenta
|
|
131
|
+
"00FFFF", // 15: Cyan
|
|
132
|
+
"800000", // 16: Dark Red
|
|
133
|
+
"008000", // 17: Dark Green
|
|
134
|
+
"000080", // 18: Dark Blue (Navy)
|
|
135
|
+
"808000", // 19: Dark Yellow (Olive)
|
|
136
|
+
"800080", // 20: Purple
|
|
137
|
+
"008080", // 21: Teal
|
|
138
|
+
"C0C0C0", // 22: Silver
|
|
139
|
+
"808080", // 23: Gray
|
|
140
|
+
"9999FF", // 24: Periwinkle
|
|
141
|
+
"993366", // 25: Plum
|
|
142
|
+
"FFFFCC", // 26: Ivory
|
|
143
|
+
"CCFFFF", // 27: Light Cyan
|
|
144
|
+
"660066", // 28: Dark Purple
|
|
145
|
+
"FF8080", // 29: Coral
|
|
146
|
+
"0066CC", // 30: Ocean Blue
|
|
147
|
+
"CCCCFF", // 31: Ice Blue
|
|
148
|
+
"000080", // 32: Dark Blue
|
|
149
|
+
"FF00FF", // 33: Pink
|
|
150
|
+
"FFFF00", // 34: Yellow
|
|
151
|
+
"00FFFF", // 35: Cyan
|
|
152
|
+
"800080", // 36: Purple
|
|
153
|
+
"800000", // 37: Dark Red
|
|
154
|
+
"008080", // 38: Teal
|
|
155
|
+
"0000FF", // 39: Blue
|
|
156
|
+
"00CCFF", // 40: Sky Blue
|
|
157
|
+
"CCFFFF", // 41: Light Turquoise
|
|
158
|
+
"CCFFCC", // 42: Light Green
|
|
159
|
+
"FFFF99", // 43: Light Yellow
|
|
160
|
+
"99CCFF", // 44: Pale Blue
|
|
161
|
+
"FF99CC", // 45: Rose
|
|
162
|
+
"CC99FF", // 46: Lavender
|
|
163
|
+
"FFCC99", // 47: Tan
|
|
164
|
+
"3366FF", // 48: Light Blue
|
|
165
|
+
"33CCCC", // 49: Aqua
|
|
166
|
+
"99CC00", // 50: Lime
|
|
167
|
+
"FFCC00", // 51: Gold
|
|
168
|
+
"FF9900", // 52: Light Orange
|
|
169
|
+
"FF6600", // 53: Orange
|
|
170
|
+
"666699", // 54: Blue Gray
|
|
171
|
+
"969696", // 55: Gray 40%
|
|
172
|
+
"003366", // 56: Dark Teal
|
|
173
|
+
"339966", // 57: Sea Green
|
|
174
|
+
"003300", // 58: Very Dark Green
|
|
175
|
+
"333300", // 59: Dark Olive
|
|
176
|
+
"993300", // 60: Brown
|
|
177
|
+
"993366", // 61: Plum
|
|
178
|
+
"333399", // 62: Indigo
|
|
179
|
+
"333333" // 63: Gray 80%
|
|
180
|
+
];
|
|
181
|
+
/**
|
|
182
|
+
* Convert an indexed color to PDF color.
|
|
183
|
+
* Index 64 = system foreground (black), 65 = system background (white).
|
|
184
|
+
*/
|
|
185
|
+
function indexedColorToPdf(index) {
|
|
186
|
+
if (index === 64) {
|
|
187
|
+
return { r: 0, g: 0, b: 0 }; // System foreground (black)
|
|
188
|
+
}
|
|
189
|
+
if (index === 65) {
|
|
190
|
+
return { r: 1, g: 1, b: 1 }; // System background (white)
|
|
191
|
+
}
|
|
192
|
+
if (index >= 0 && index < INDEXED_COLORS.length) {
|
|
193
|
+
return argbToPdfColor(INDEXED_COLORS[index]) ?? null;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
100
197
|
/**
|
|
101
198
|
* Apply a tint value to a color.
|
|
102
199
|
* Tint range: -1.0 (fully dark) to +1.0 (fully light).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Theme Color Utilities
|
|
3
|
+
*
|
|
4
|
+
* Resolves OOXML theme colors with tint/shade transformations.
|
|
5
|
+
* Extracted to a standalone file so that html-renderer and document.ts
|
|
6
|
+
* can both import it without creating circular heavy dependencies.
|
|
7
|
+
*/
|
|
8
|
+
import type { DocumentTheme, ColorSpec, HexColor } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a ColorSpec to an actual hex RGB color using the document theme.
|
|
11
|
+
*
|
|
12
|
+
* Applies theme color lookup + tint/shade transformations per OOXML spec.
|
|
13
|
+
*
|
|
14
|
+
* @param color - The color value (HexColor string or ColorSpec).
|
|
15
|
+
* @param theme - The document theme (from `doc.theme`).
|
|
16
|
+
* @returns Resolved hex color string (6 chars, no #), or undefined if unresolvable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveThemeColor(color: HexColor | ColorSpec | undefined, theme?: DocumentTheme): HexColor | undefined;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Theme Color Utilities
|
|
3
|
+
*
|
|
4
|
+
* Resolves OOXML theme colors with tint/shade transformations.
|
|
5
|
+
* Extracted to a standalone file so that html-renderer and document.ts
|
|
6
|
+
* can both import it without creating circular heavy dependencies.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Map OOXML theme color attribute names to theme color scheme keys.
|
|
10
|
+
* Word uses different names in run/paragraph properties vs the theme XML.
|
|
11
|
+
*/
|
|
12
|
+
const THEME_COLOR_MAP = {
|
|
13
|
+
dark1: "dk1",
|
|
14
|
+
light1: "lt1",
|
|
15
|
+
dark2: "dk2",
|
|
16
|
+
light2: "lt2",
|
|
17
|
+
accent1: "accent1",
|
|
18
|
+
accent2: "accent2",
|
|
19
|
+
accent3: "accent3",
|
|
20
|
+
accent4: "accent4",
|
|
21
|
+
accent5: "accent5",
|
|
22
|
+
accent6: "accent6",
|
|
23
|
+
hyperlink: "hlink",
|
|
24
|
+
followedHyperlink: "folHlink",
|
|
25
|
+
// Direct names also work
|
|
26
|
+
dk1: "dk1",
|
|
27
|
+
lt1: "lt1",
|
|
28
|
+
dk2: "dk2",
|
|
29
|
+
lt2: "lt2",
|
|
30
|
+
hlink: "hlink",
|
|
31
|
+
folHlink: "folHlink"
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Resolve a ColorSpec to an actual hex RGB color using the document theme.
|
|
35
|
+
*
|
|
36
|
+
* Applies theme color lookup + tint/shade transformations per OOXML spec.
|
|
37
|
+
*
|
|
38
|
+
* @param color - The color value (HexColor string or ColorSpec).
|
|
39
|
+
* @param theme - The document theme (from `doc.theme`).
|
|
40
|
+
* @returns Resolved hex color string (6 chars, no #), or undefined if unresolvable.
|
|
41
|
+
*/
|
|
42
|
+
export function resolveThemeColor(color, theme) {
|
|
43
|
+
if (color === undefined) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
if (typeof color === "string") {
|
|
47
|
+
return color;
|
|
48
|
+
}
|
|
49
|
+
// ColorSpec with val — use directly
|
|
50
|
+
if (color.val && color.val !== "auto") {
|
|
51
|
+
return color.val;
|
|
52
|
+
}
|
|
53
|
+
// Resolve via theme
|
|
54
|
+
if (!color.themeColor || !theme) {
|
|
55
|
+
return color.val;
|
|
56
|
+
}
|
|
57
|
+
const key = THEME_COLOR_MAP[color.themeColor] ?? color.themeColor;
|
|
58
|
+
const base = theme.colorScheme.colors[key];
|
|
59
|
+
if (!base) {
|
|
60
|
+
return color.val;
|
|
61
|
+
}
|
|
62
|
+
// Apply tint or shade
|
|
63
|
+
if (color.themeTint) {
|
|
64
|
+
return applyTint(base, parseInt(color.themeTint, 16) / 255);
|
|
65
|
+
}
|
|
66
|
+
if (color.themeShade) {
|
|
67
|
+
return applyShade(base, parseInt(color.themeShade, 16) / 255);
|
|
68
|
+
}
|
|
69
|
+
return base;
|
|
70
|
+
}
|
|
71
|
+
/** Apply tint to a hex color. tint=1 → white, tint=0 → original. */
|
|
72
|
+
function applyTint(hex, tint) {
|
|
73
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
74
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
75
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
76
|
+
const nr = Math.round(r + (255 - r) * tint);
|
|
77
|
+
const ng = Math.round(g + (255 - g) * tint);
|
|
78
|
+
const nb = Math.round(b + (255 - b) * tint);
|
|
79
|
+
return toHex2(nr) + toHex2(ng) + toHex2(nb);
|
|
80
|
+
}
|
|
81
|
+
/** Apply shade to a hex color. shade=1 → original, shade=0 → black. */
|
|
82
|
+
function applyShade(hex, shade) {
|
|
83
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
84
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
85
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
86
|
+
const nr = Math.round(r * shade);
|
|
87
|
+
const ng = Math.round(g * shade);
|
|
88
|
+
const nb = Math.round(b * shade);
|
|
89
|
+
return toHex2(nr) + toHex2(ng) + toHex2(nb);
|
|
90
|
+
}
|
|
91
|
+
function toHex2(n) {
|
|
92
|
+
const h = Math.max(0, Math.min(255, n)).toString(16);
|
|
93
|
+
return h.length < 2 ? "0" + h : h;
|
|
94
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* DOCX Module - Content Types Generator
|
|
3
3
|
*
|
|
4
4
|
* Generates [Content_Types].xml for the DOCX package.
|
|
5
|
+
* Uses a plain data record + free functions for tree-shakeability.
|
|
5
6
|
*/
|
|
6
7
|
import type { XmlSink } from "../xml/types.js";
|
|
7
8
|
/** Content type override entry. */
|
|
@@ -9,19 +10,18 @@ export interface ContentTypeOverride {
|
|
|
9
10
|
readonly partName: string;
|
|
10
11
|
readonly contentType: string;
|
|
11
12
|
}
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
private readonly _defaults;
|
|
17
|
-
private readonly _overrides;
|
|
18
|
-
constructor();
|
|
19
|
-
/** Add a default content type for a file extension. */
|
|
20
|
-
addDefault(extension: string, contentType: string): void;
|
|
21
|
-
/** Add an override content type for a specific part. */
|
|
22
|
-
addOverride(partName: string, contentType: string): void;
|
|
23
|
-
/** Add image extension defaults from a set of used extensions. */
|
|
24
|
-
addImageDefaults(extensions: Iterable<string>): void;
|
|
25
|
-
/** Render the [Content_Types].xml to a sink. */
|
|
26
|
-
render(xml: XmlSink): void;
|
|
13
|
+
/** Internal state for content types (plain record, not a class). */
|
|
14
|
+
export interface ContentTypesState {
|
|
15
|
+
readonly defaults: Map<string, string>;
|
|
16
|
+
readonly overrides: ContentTypeOverride[];
|
|
27
17
|
}
|
|
18
|
+
/** Create a new ContentTypesState with standard defaults (rels, xml). */
|
|
19
|
+
export declare function createContentTypes(): ContentTypesState;
|
|
20
|
+
/** Add a default content type for a file extension. */
|
|
21
|
+
export declare function addContentTypeDefault(state: ContentTypesState, extension: string, contentType: string): void;
|
|
22
|
+
/** Add an override content type for a specific part. */
|
|
23
|
+
export declare function addContentTypeOverride(state: ContentTypesState, partName: string, contentType: string): void;
|
|
24
|
+
/** Add image extension defaults from a set of used extensions. */
|
|
25
|
+
export declare function addImageContentTypeDefaults(state: ContentTypesState, extensions: Iterable<string>): void;
|
|
26
|
+
/** Render the [Content_Types].xml to a sink. */
|
|
27
|
+
export declare function renderContentTypes(state: ContentTypesState, xml: XmlSink): void;
|
|
@@ -2,52 +2,48 @@
|
|
|
2
2
|
* DOCX Module - Content Types Generator
|
|
3
3
|
*
|
|
4
4
|
* Generates [Content_Types].xml for the DOCX package.
|
|
5
|
+
* Uses a plain data record + free functions for tree-shakeability.
|
|
5
6
|
*/
|
|
6
7
|
import { NS_CONTENT_TYPES, STD_DOC_ATTRIBUTES, ContentType, IMAGE_CONTENT_TYPES } from "./constants.js";
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
for (const ext of extensions) {
|
|
32
|
-
const ct = IMAGE_CONTENT_TYPES[ext.toLowerCase()];
|
|
33
|
-
if (ct) {
|
|
34
|
-
this._defaults.set(ext.toLowerCase(), ct);
|
|
35
|
-
}
|
|
8
|
+
/** Create a new ContentTypesState with standard defaults (rels, xml). */
|
|
9
|
+
export function createContentTypes() {
|
|
10
|
+
const defaults = new Map();
|
|
11
|
+
defaults.set("rels", ContentType.Relationships);
|
|
12
|
+
defaults.set("xml", ContentType.Xml);
|
|
13
|
+
return { defaults, overrides: [] };
|
|
14
|
+
}
|
|
15
|
+
/** Add a default content type for a file extension. */
|
|
16
|
+
export function addContentTypeDefault(state, extension, contentType) {
|
|
17
|
+
state.defaults.set(extension, contentType);
|
|
18
|
+
}
|
|
19
|
+
/** Add an override content type for a specific part. */
|
|
20
|
+
export function addContentTypeOverride(state, partName, contentType) {
|
|
21
|
+
state.overrides.push({
|
|
22
|
+
partName: partName.startsWith("/") ? partName : `/${partName}`,
|
|
23
|
+
contentType
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** Add image extension defaults from a set of used extensions. */
|
|
27
|
+
export function addImageContentTypeDefaults(state, extensions) {
|
|
28
|
+
for (const ext of extensions) {
|
|
29
|
+
const ct = IMAGE_CONTENT_TYPES[ext.toLowerCase()];
|
|
30
|
+
if (ct) {
|
|
31
|
+
state.defaults.set(ext.toLowerCase(), ct);
|
|
36
32
|
}
|
|
37
33
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
xml.closeNode();
|
|
34
|
+
}
|
|
35
|
+
/** Render the [Content_Types].xml to a sink. */
|
|
36
|
+
export function renderContentTypes(state, xml) {
|
|
37
|
+
xml.openXml(STD_DOC_ATTRIBUTES);
|
|
38
|
+
xml.openNode("Types", { xmlns: NS_CONTENT_TYPES });
|
|
39
|
+
// Defaults sorted by extension
|
|
40
|
+
const sortedDefaults = [...state.defaults.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
41
|
+
for (const [ext, ct] of sortedDefaults) {
|
|
42
|
+
xml.leafNode("Default", { Extension: ext, ContentType: ct });
|
|
43
|
+
}
|
|
44
|
+
// Overrides in order
|
|
45
|
+
for (const override of state.overrides) {
|
|
46
|
+
xml.leafNode("Override", { PartName: override.partName, ContentType: override.contentType });
|
|
52
47
|
}
|
|
48
|
+
xml.closeNode();
|
|
53
49
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Encryption & Digital Signatures (Subpath Export)
|
|
3
|
+
*
|
|
4
|
+
* Import separately to avoid pulling crypto code into the bundle
|
|
5
|
+
* when only core document building is needed.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { isEncryptedDocx, decryptPackage } from "excelts/word/crypto";
|
|
10
|
+
* import { extractSignatures } from "excelts/word/crypto";
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export { isEncryptedDocx, verifyPassword, decryptPackage, parseEncryptionInfoXml, deriveEncryptionKey, AGILE_BLOCK_KEYS } from "./encryption.js";
|
|
14
|
+
export type { AgileEncryptionInfo } from "./encryption.js";
|
|
15
|
+
export { hasDigitalSignatures, parseSignatureXml, extractSignatures, isWellFormedSignature } from "./digital-signatures.js";
|
|
16
|
+
export type { DigitalSignatureInfo } from "./digital-signatures.js";
|
|
17
|
+
export { deobfuscateFont, obfuscateFont, generateFontKey } from "./font-obfuscation.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Encryption & Digital Signatures (Subpath Export)
|
|
3
|
+
*
|
|
4
|
+
* Import separately to avoid pulling crypto code into the bundle
|
|
5
|
+
* when only core document building is needed.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { isEncryptedDocx, decryptPackage } from "excelts/word/crypto";
|
|
10
|
+
* import { extractSignatures } from "excelts/word/crypto";
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
// Encryption utilities
|
|
14
|
+
export { isEncryptedDocx, verifyPassword, decryptPackage, parseEncryptionInfoXml, deriveEncryptionKey, AGILE_BLOCK_KEYS } from "./encryption.js";
|
|
15
|
+
// Digital signature utilities
|
|
16
|
+
export { hasDigitalSignatures, parseSignatureXml, extractSignatures, isWellFormedSignature } from "./digital-signatures.js";
|
|
17
|
+
// Font obfuscation utilities
|
|
18
|
+
export { deobfuscateFont, obfuscateFont, generateFontKey } from "./font-obfuscation.js";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Document IO
|
|
3
|
+
*
|
|
4
|
+
* IO operations that depend on docx-packager and docx-reader.
|
|
5
|
+
* Separated from document.ts so that builder helpers can be imported
|
|
6
|
+
* without pulling in archive/xml/writer code.
|
|
7
|
+
*/
|
|
8
|
+
import type { DocxDocument, Paragraph, Table, ImageDef } from "./types.js";
|
|
9
|
+
/** Package a DocxDocument model to DOCX bytes. */
|
|
10
|
+
export declare function toBuffer(doc: DocxDocument, compressionLevel?: number): Promise<Uint8Array>;
|
|
11
|
+
/** Package a DocxDocument model to base64 string. */
|
|
12
|
+
export declare function toBase64(doc: DocxDocument, compressionLevel?: number): Promise<string>;
|
|
13
|
+
/** Type of content to patch into a placeholder. */
|
|
14
|
+
export type PatchContent = {
|
|
15
|
+
readonly type: "text";
|
|
16
|
+
readonly text: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: "paragraph";
|
|
19
|
+
readonly children: readonly Paragraph[];
|
|
20
|
+
} | {
|
|
21
|
+
readonly type: "table";
|
|
22
|
+
readonly table: Table;
|
|
23
|
+
} | {
|
|
24
|
+
readonly type: "image";
|
|
25
|
+
readonly image: ImageDef;
|
|
26
|
+
readonly width: number;
|
|
27
|
+
readonly height: number;
|
|
28
|
+
};
|
|
29
|
+
/** A single patch operation mapping a placeholder to replacement content. */
|
|
30
|
+
export interface PatchOperation {
|
|
31
|
+
/** Placeholder string to find (e.g. "{{name}}"). */
|
|
32
|
+
readonly placeholder: string;
|
|
33
|
+
/** Content to replace the placeholder with. */
|
|
34
|
+
readonly content: PatchContent;
|
|
35
|
+
}
|
|
36
|
+
/** Options for patchDocument. */
|
|
37
|
+
export interface PatchOptions {
|
|
38
|
+
/** Compression level (0-9). Default: 6. */
|
|
39
|
+
readonly compressionLevel?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read an existing DOCX file, replace placeholders with content, and produce a new DOCX.
|
|
43
|
+
*
|
|
44
|
+
* Placeholders are strings like `{{name}}` embedded in the document text.
|
|
45
|
+
* They may span across multiple runs — the patcher handles cross-run matching.
|
|
46
|
+
*
|
|
47
|
+
* Supported patch content types:
|
|
48
|
+
* - `text` — simple text replacement (preserves formatting of the first run)
|
|
49
|
+
* - `paragraph` — replaces the entire paragraph containing the placeholder
|
|
50
|
+
* - `table` — replaces the entire paragraph with a table
|
|
51
|
+
* - `image` — replaces the placeholder with an inline image
|
|
52
|
+
*
|
|
53
|
+
* @param buffer - The source DOCX file as a Uint8Array.
|
|
54
|
+
* @param patches - Array of patch operations to apply.
|
|
55
|
+
* @param options - Optional compression settings.
|
|
56
|
+
* @returns New DOCX file as a Uint8Array.
|
|
57
|
+
*/
|
|
58
|
+
export declare function patchDocument(buffer: Uint8Array, patches: readonly PatchOperation[], options?: PatchOptions): Promise<Uint8Array>;
|