@cj-tech-master/excelts 9.0.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.browser.d.ts +2 -0
- package/dist/browser/index.browser.js +2 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/modules/excel/image.d.ts +27 -2
- package/dist/browser/modules/excel/image.js +23 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +16 -1
- package/dist/browser/modules/excel/stream/worksheet-writer.js +68 -0
- package/dist/browser/modules/excel/types.d.ts +72 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +4 -0
- package/dist/browser/modules/excel/utils/drawing-utils.js +5 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +4 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +15 -0
- package/dist/browser/modules/excel/utils/watermark-image.d.ts +67 -0
- package/dist/browser/modules/excel/utils/watermark-image.js +383 -0
- package/dist/browser/modules/excel/worksheet.d.ts +39 -1
- package/dist/browser/modules/excel/worksheet.js +99 -0
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +3 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +6 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-fill-xform.d.ts +2 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-fill-xform.js +0 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +3 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +22 -6
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +3 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +5 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +19 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +103 -4
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +135 -8
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +53 -1
- package/dist/browser/modules/pdf/core/pdf-writer.d.ts +1 -1
- package/dist/browser/modules/pdf/core/pdf-writer.js +2 -1
- package/dist/browser/modules/pdf/index.d.ts +1 -1
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +29 -1
- package/dist/browser/modules/pdf/render/page-renderer.js +394 -25
- package/dist/browser/modules/pdf/render/pdf-exporter.js +84 -47
- package/dist/browser/modules/pdf/types.d.ts +235 -0
- package/dist/cjs/index.js +5 -2
- package/dist/cjs/modules/excel/image.js +23 -1
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +68 -0
- package/dist/cjs/modules/excel/utils/drawing-utils.js +5 -0
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +19 -0
- package/dist/cjs/modules/excel/utils/watermark-image.js +386 -0
- package/dist/cjs/modules/excel/worksheet.js +99 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +3 -2
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +6 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-fill-xform.js +0 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +22 -6
- package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +5 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +103 -4
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +134 -7
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +52 -0
- package/dist/cjs/modules/pdf/core/pdf-writer.js +2 -1
- package/dist/cjs/modules/pdf/render/page-renderer.js +396 -25
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +83 -46
- package/dist/esm/index.browser.js +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/modules/excel/image.js +23 -1
- package/dist/esm/modules/excel/stream/worksheet-writer.js +68 -0
- package/dist/esm/modules/excel/utils/drawing-utils.js +5 -0
- package/dist/esm/modules/excel/utils/ooxml-paths.js +15 -0
- package/dist/esm/modules/excel/utils/watermark-image.js +383 -0
- package/dist/esm/modules/excel/worksheet.js +99 -0
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +3 -2
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +6 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-fill-xform.js +0 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +22 -6
- package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +5 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +103 -4
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +135 -8
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +53 -1
- package/dist/esm/modules/pdf/core/pdf-writer.js +2 -1
- package/dist/esm/modules/pdf/render/page-renderer.js +394 -25
- package/dist/esm/modules/pdf/render/pdf-exporter.js +84 -47
- package/dist/iife/excelts.iife.js +2390 -469
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +47 -47
- package/dist/types/index.browser.d.ts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/modules/excel/image.d.ts +27 -2
- package/dist/types/modules/excel/stream/worksheet-writer.d.ts +16 -1
- package/dist/types/modules/excel/types.d.ts +72 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +4 -0
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +4 -0
- package/dist/types/modules/excel/utils/watermark-image.d.ts +67 -0
- package/dist/types/modules/excel/worksheet.d.ts +39 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-fill-xform.d.ts +2 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +3 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +3 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +19 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +1 -0
- package/dist/types/modules/pdf/core/pdf-writer.d.ts +1 -1
- package/dist/types/modules/pdf/index.d.ts +1 -1
- package/dist/types/modules/pdf/render/page-renderer.d.ts +29 -1
- package/dist/types/modules/pdf/types.d.ts +235 -0
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ exports.isBinaryEntryPath = isBinaryEntryPath;
|
|
|
12
12
|
exports.getDrawingNameFromPath = getDrawingNameFromPath;
|
|
13
13
|
exports.getDrawingNameFromRelsPath = getDrawingNameFromRelsPath;
|
|
14
14
|
exports.getVmlDrawingNameFromPath = getVmlDrawingNameFromPath;
|
|
15
|
+
exports.getVmlDrawingHFNameFromPath = getVmlDrawingHFNameFromPath;
|
|
15
16
|
exports.getCommentsIndexFromPath = getCommentsIndexFromPath;
|
|
16
17
|
exports.getTableNameFromPath = getTableNameFromPath;
|
|
17
18
|
exports.getPivotTableNameFromPath = getPivotTableNameFromPath;
|
|
@@ -28,6 +29,8 @@ exports.worksheetRelTarget = worksheetRelTarget;
|
|
|
28
29
|
exports.commentsPath = commentsPath;
|
|
29
30
|
exports.commentsPathFromName = commentsPathFromName;
|
|
30
31
|
exports.vmlDrawingPath = vmlDrawingPath;
|
|
32
|
+
exports.vmlDrawingHFPath = vmlDrawingHFPath;
|
|
33
|
+
exports.vmlDrawingHFRelsPath = vmlDrawingHFRelsPath;
|
|
31
34
|
exports.tablePath = tablePath;
|
|
32
35
|
exports.drawingPath = drawingPath;
|
|
33
36
|
exports.drawingRelsPath = drawingRelsPath;
|
|
@@ -41,6 +44,7 @@ exports.pivotCacheDefinitionRelTargetFromPivotTable = pivotCacheDefinitionRelTar
|
|
|
41
44
|
exports.pivotCacheDefinitionRelTargetFromWorkbook = pivotCacheDefinitionRelTargetFromWorkbook;
|
|
42
45
|
exports.commentsRelTargetFromWorksheet = commentsRelTargetFromWorksheet;
|
|
43
46
|
exports.vmlDrawingRelTargetFromWorksheet = vmlDrawingRelTargetFromWorksheet;
|
|
47
|
+
exports.vmlDrawingHFRelTargetFromWorksheet = vmlDrawingHFRelTargetFromWorksheet;
|
|
44
48
|
exports.drawingRelTargetFromWorksheet = drawingRelTargetFromWorksheet;
|
|
45
49
|
exports.vmlDrawingRelTargetFromWorksheetName = vmlDrawingRelTargetFromWorksheetName;
|
|
46
50
|
exports.commentsRelTargetFromWorksheetName = commentsRelTargetFromWorksheetName;
|
|
@@ -70,6 +74,7 @@ const mediaFilenameRegex = /^xl\/media\/([a-zA-Z0-9]+[.][a-zA-Z0-9]{3,4})$/;
|
|
|
70
74
|
const drawingXmlRegex = /^xl\/drawings\/(drawing\d+)[.]xml$/;
|
|
71
75
|
const drawingRelsXmlRegex = /^xl\/drawings\/_rels\/(drawing\d+)[.]xml[.]rels$/;
|
|
72
76
|
const vmlDrawingRegex = /^xl\/drawings\/(vmlDrawing\d+)[.]vml$/;
|
|
77
|
+
const vmlDrawingHFRegex = /^xl\/drawings\/(vmlDrawingHF\d+)[.]vml$/;
|
|
73
78
|
const commentsXmlRegex = /^xl\/comments(\d+)[.]xml$/;
|
|
74
79
|
const tableXmlRegex = /^xl\/tables\/(table\d+)[.]xml$/;
|
|
75
80
|
const pivotTableXmlRegex = /^xl\/pivotTables\/(pivotTable\d+)[.]xml$/;
|
|
@@ -125,6 +130,10 @@ function getVmlDrawingNameFromPath(path) {
|
|
|
125
130
|
const match = vmlDrawingRegex.exec(path);
|
|
126
131
|
return match ? match[1] : undefined;
|
|
127
132
|
}
|
|
133
|
+
function getVmlDrawingHFNameFromPath(path) {
|
|
134
|
+
const match = vmlDrawingHFRegex.exec(path);
|
|
135
|
+
return match ? match[1] : undefined;
|
|
136
|
+
}
|
|
128
137
|
function getCommentsIndexFromPath(path) {
|
|
129
138
|
const match = commentsXmlRegex.exec(path);
|
|
130
139
|
return match ? match[1] : undefined;
|
|
@@ -182,6 +191,12 @@ function commentsPathFromName(commentName) {
|
|
|
182
191
|
function vmlDrawingPath(sheetId) {
|
|
183
192
|
return `xl/drawings/vmlDrawing${sheetId}.vml`;
|
|
184
193
|
}
|
|
194
|
+
function vmlDrawingHFPath(sheetId) {
|
|
195
|
+
return `xl/drawings/vmlDrawingHF${sheetId}.vml`;
|
|
196
|
+
}
|
|
197
|
+
function vmlDrawingHFRelsPath(sheetId) {
|
|
198
|
+
return `xl/drawings/_rels/vmlDrawingHF${sheetId}.vml.rels`;
|
|
199
|
+
}
|
|
185
200
|
function tablePath(target) {
|
|
186
201
|
return `xl/tables/${target}`;
|
|
187
202
|
}
|
|
@@ -231,6 +246,10 @@ function vmlDrawingRelTargetFromWorksheet(sheetId) {
|
|
|
231
246
|
// Target inside xl/worksheets/_rels/sheetN.xml.rels (base: xl/worksheets/)
|
|
232
247
|
return `../drawings/vmlDrawing${sheetId}.vml`;
|
|
233
248
|
}
|
|
249
|
+
function vmlDrawingHFRelTargetFromWorksheet(sheetId) {
|
|
250
|
+
// Target inside xl/worksheets/_rels/sheetN.xml.rels (base: xl/worksheets/)
|
|
251
|
+
return `../drawings/vmlDrawingHF${sheetId}.vml`;
|
|
252
|
+
}
|
|
234
253
|
function drawingRelTargetFromWorksheet(drawingName) {
|
|
235
254
|
// Target inside xl/worksheets/_rels/sheetN.xml.rels (base: xl/worksheets/)
|
|
236
255
|
return `../drawings/${drawingName}.xml`;
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Zero-dependency text-to-PNG watermark image generator.
|
|
4
|
+
*
|
|
5
|
+
* Renders text into a semi-transparent PNG suitable for use as an Excel watermark.
|
|
6
|
+
* Uses a built-in bitmap font for ASCII characters — no Canvas or external fonts required.
|
|
7
|
+
* PNG data is deflate-compressed using the archive module's built-in compressor.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const png = createTextWatermarkImage("CONFIDENTIAL", {
|
|
12
|
+
* fontSize: 48,
|
|
13
|
+
* color: { r: 128, g: 128, b: 128 },
|
|
14
|
+
* opacity: 40,
|
|
15
|
+
* rotation: -45
|
|
16
|
+
* });
|
|
17
|
+
* const imgId = workbook.addImage({ buffer: png, extension: "png" });
|
|
18
|
+
* worksheet.addWatermark({ imageId: imgId });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.createTextWatermarkImage = createTextWatermarkImage;
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// Public API
|
|
25
|
+
// =============================================================================
|
|
26
|
+
const deflate_fallback_1 = require("../../archive/compression/deflate-fallback.js");
|
|
27
|
+
/**
|
|
28
|
+
* Generate a PNG image containing watermark text.
|
|
29
|
+
*
|
|
30
|
+
* The image has an alpha channel so the watermark is semi-transparent.
|
|
31
|
+
* Works in both Node.js and browsers with zero dependencies.
|
|
32
|
+
*/
|
|
33
|
+
function createTextWatermarkImage(text, options) {
|
|
34
|
+
const fontSize = options?.fontSize ?? 48;
|
|
35
|
+
const color = options?.color ?? { r: 128, g: 128, b: 128 };
|
|
36
|
+
const opacity = Math.max(0, Math.min(100, options?.opacity ?? 40));
|
|
37
|
+
const rotation = options?.rotation ?? -45;
|
|
38
|
+
const padding = options?.padding ?? 20;
|
|
39
|
+
// Scale factor: built-in font is 8px tall
|
|
40
|
+
const scale = Math.max(1, Math.round(fontSize / GLYPH_HEIGHT));
|
|
41
|
+
// Render text to unrotated bitmap
|
|
42
|
+
const { width: textW, height: textH, pixels: textPixels } = renderTextBitmap(text, scale);
|
|
43
|
+
// Add padding
|
|
44
|
+
const paddedW = textW + padding * 2;
|
|
45
|
+
const paddedH = textH + padding * 2;
|
|
46
|
+
const paddedPixels = new Uint8Array(paddedW * paddedH);
|
|
47
|
+
for (let y = 0; y < textH; y++) {
|
|
48
|
+
for (let x = 0; x < textW; x++) {
|
|
49
|
+
paddedPixels[(y + padding) * paddedW + (x + padding)] = textPixels[y * textW + x];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Rotate
|
|
53
|
+
const { width: rotW, height: rotH, pixels: rotPixels } = rotateBitmap(paddedPixels, paddedW, paddedH, rotation);
|
|
54
|
+
// Convert to RGBA PNG
|
|
55
|
+
const alpha = Math.round((opacity / 100) * 255);
|
|
56
|
+
const rgba = new Uint8Array(rotW * rotH * 4);
|
|
57
|
+
for (let i = 0; i < rotW * rotH; i++) {
|
|
58
|
+
const a = rotPixels[i];
|
|
59
|
+
if (a > 0) {
|
|
60
|
+
rgba[i * 4] = color.r;
|
|
61
|
+
rgba[i * 4 + 1] = color.g;
|
|
62
|
+
rgba[i * 4 + 2] = color.b;
|
|
63
|
+
rgba[i * 4 + 3] = Math.round((a / 255) * alpha);
|
|
64
|
+
}
|
|
65
|
+
// else fully transparent (already 0)
|
|
66
|
+
}
|
|
67
|
+
return encodePng(rgba, rotW, rotH);
|
|
68
|
+
}
|
|
69
|
+
// =============================================================================
|
|
70
|
+
// Bitmap Font — 8px tall monospace ASCII (CP437-style, printable range 32-126)
|
|
71
|
+
// =============================================================================
|
|
72
|
+
const GLYPH_WIDTH = 6;
|
|
73
|
+
const GLYPH_HEIGHT = 8;
|
|
74
|
+
/**
|
|
75
|
+
* Compact glyph data: each character is 8 bytes (one byte per row, 6 bits used).
|
|
76
|
+
* Bit 5 = leftmost pixel, bit 0 = rightmost pixel.
|
|
77
|
+
*/
|
|
78
|
+
const FONT_DATA = {
|
|
79
|
+
// space
|
|
80
|
+
32: [0, 0, 0, 0, 0, 0, 0, 0],
|
|
81
|
+
// !
|
|
82
|
+
33: [0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x04, 0x00],
|
|
83
|
+
// "
|
|
84
|
+
34: [0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00],
|
|
85
|
+
// #
|
|
86
|
+
35: [0x0a, 0x0a, 0x1f, 0x0a, 0x1f, 0x0a, 0x0a, 0x00],
|
|
87
|
+
// $
|
|
88
|
+
36: [0x04, 0x0f, 0x14, 0x0e, 0x05, 0x1e, 0x04, 0x00],
|
|
89
|
+
// %
|
|
90
|
+
37: [0x18, 0x19, 0x02, 0x04, 0x08, 0x13, 0x03, 0x00],
|
|
91
|
+
// &
|
|
92
|
+
38: [0x0c, 0x12, 0x14, 0x08, 0x15, 0x12, 0x0d, 0x00],
|
|
93
|
+
// '
|
|
94
|
+
39: [0x04, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00],
|
|
95
|
+
// (
|
|
96
|
+
40: [0x02, 0x04, 0x08, 0x08, 0x08, 0x04, 0x02, 0x00],
|
|
97
|
+
// )
|
|
98
|
+
41: [0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00],
|
|
99
|
+
// *
|
|
100
|
+
42: [0x00, 0x04, 0x15, 0x0e, 0x15, 0x04, 0x00, 0x00],
|
|
101
|
+
// +
|
|
102
|
+
43: [0x00, 0x04, 0x04, 0x1f, 0x04, 0x04, 0x00, 0x00],
|
|
103
|
+
// ,
|
|
104
|
+
44: [0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x08],
|
|
105
|
+
// -
|
|
106
|
+
45: [0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00],
|
|
107
|
+
// .
|
|
108
|
+
46: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00],
|
|
109
|
+
// /
|
|
110
|
+
47: [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00],
|
|
111
|
+
// 0-9
|
|
112
|
+
48: [0x0e, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0e, 0x00],
|
|
113
|
+
49: [0x04, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x0e, 0x00],
|
|
114
|
+
50: [0x0e, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1f, 0x00],
|
|
115
|
+
51: [0x1f, 0x02, 0x04, 0x02, 0x01, 0x11, 0x0e, 0x00],
|
|
116
|
+
52: [0x02, 0x06, 0x0a, 0x12, 0x1f, 0x02, 0x02, 0x00],
|
|
117
|
+
53: [0x1f, 0x10, 0x1e, 0x01, 0x01, 0x11, 0x0e, 0x00],
|
|
118
|
+
54: [0x06, 0x08, 0x10, 0x1e, 0x11, 0x11, 0x0e, 0x00],
|
|
119
|
+
55: [0x1f, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08, 0x00],
|
|
120
|
+
56: [0x0e, 0x11, 0x11, 0x0e, 0x11, 0x11, 0x0e, 0x00],
|
|
121
|
+
57: [0x0e, 0x11, 0x11, 0x0f, 0x01, 0x02, 0x0c, 0x00],
|
|
122
|
+
// :
|
|
123
|
+
58: [0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00],
|
|
124
|
+
// ;
|
|
125
|
+
59: [0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x04, 0x08],
|
|
126
|
+
// <
|
|
127
|
+
60: [0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x00],
|
|
128
|
+
// =
|
|
129
|
+
61: [0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00],
|
|
130
|
+
// >
|
|
131
|
+
62: [0x08, 0x04, 0x02, 0x01, 0x02, 0x04, 0x08, 0x00],
|
|
132
|
+
// ?
|
|
133
|
+
63: [0x0e, 0x11, 0x01, 0x02, 0x04, 0x00, 0x04, 0x00],
|
|
134
|
+
// @
|
|
135
|
+
64: [0x0e, 0x11, 0x17, 0x15, 0x17, 0x10, 0x0e, 0x00],
|
|
136
|
+
// A-Z
|
|
137
|
+
65: [0x0e, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, 0x00],
|
|
138
|
+
66: [0x1e, 0x11, 0x11, 0x1e, 0x11, 0x11, 0x1e, 0x00],
|
|
139
|
+
67: [0x0e, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0e, 0x00],
|
|
140
|
+
68: [0x1c, 0x12, 0x11, 0x11, 0x11, 0x12, 0x1c, 0x00],
|
|
141
|
+
69: [0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x1f, 0x00],
|
|
142
|
+
70: [0x1f, 0x10, 0x10, 0x1e, 0x10, 0x10, 0x10, 0x00],
|
|
143
|
+
71: [0x0e, 0x11, 0x10, 0x17, 0x11, 0x11, 0x0f, 0x00],
|
|
144
|
+
72: [0x11, 0x11, 0x11, 0x1f, 0x11, 0x11, 0x11, 0x00],
|
|
145
|
+
73: [0x0e, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e, 0x00],
|
|
146
|
+
74: [0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0c, 0x00],
|
|
147
|
+
75: [0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11, 0x00],
|
|
148
|
+
76: [0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1f, 0x00],
|
|
149
|
+
77: [0x11, 0x1b, 0x15, 0x15, 0x11, 0x11, 0x11, 0x00],
|
|
150
|
+
78: [0x11, 0x19, 0x15, 0x13, 0x11, 0x11, 0x11, 0x00],
|
|
151
|
+
79: [0x0e, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00],
|
|
152
|
+
80: [0x1e, 0x11, 0x11, 0x1e, 0x10, 0x10, 0x10, 0x00],
|
|
153
|
+
81: [0x0e, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0d, 0x00],
|
|
154
|
+
82: [0x1e, 0x11, 0x11, 0x1e, 0x14, 0x12, 0x11, 0x00],
|
|
155
|
+
83: [0x0f, 0x10, 0x10, 0x0e, 0x01, 0x01, 0x1e, 0x00],
|
|
156
|
+
84: [0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00],
|
|
157
|
+
85: [0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0e, 0x00],
|
|
158
|
+
86: [0x11, 0x11, 0x11, 0x11, 0x11, 0x0a, 0x04, 0x00],
|
|
159
|
+
87: [0x11, 0x11, 0x11, 0x15, 0x15, 0x1b, 0x11, 0x00],
|
|
160
|
+
88: [0x11, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x11, 0x00],
|
|
161
|
+
89: [0x11, 0x11, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x00],
|
|
162
|
+
90: [0x1f, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1f, 0x00],
|
|
163
|
+
// [ \ ]
|
|
164
|
+
91: [0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e, 0x00],
|
|
165
|
+
92: [0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00],
|
|
166
|
+
93: [0x0e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x0e, 0x00],
|
|
167
|
+
// ^ _ `
|
|
168
|
+
94: [0x04, 0x0a, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00],
|
|
169
|
+
95: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00],
|
|
170
|
+
96: [0x08, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00],
|
|
171
|
+
// a-z
|
|
172
|
+
97: [0x00, 0x00, 0x0e, 0x01, 0x0f, 0x11, 0x0f, 0x00],
|
|
173
|
+
98: [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1e, 0x00],
|
|
174
|
+
99: [0x00, 0x00, 0x0e, 0x10, 0x10, 0x11, 0x0e, 0x00],
|
|
175
|
+
100: [0x01, 0x01, 0x0d, 0x13, 0x11, 0x11, 0x0f, 0x00],
|
|
176
|
+
101: [0x00, 0x00, 0x0e, 0x11, 0x1f, 0x10, 0x0e, 0x00],
|
|
177
|
+
102: [0x06, 0x09, 0x08, 0x1c, 0x08, 0x08, 0x08, 0x00],
|
|
178
|
+
103: [0x00, 0x00, 0x0f, 0x11, 0x0f, 0x01, 0x0e, 0x00],
|
|
179
|
+
104: [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x11, 0x00],
|
|
180
|
+
105: [0x04, 0x00, 0x0c, 0x04, 0x04, 0x04, 0x0e, 0x00],
|
|
181
|
+
106: [0x02, 0x00, 0x06, 0x02, 0x02, 0x12, 0x0c, 0x00],
|
|
182
|
+
107: [0x10, 0x10, 0x12, 0x14, 0x18, 0x14, 0x12, 0x00],
|
|
183
|
+
108: [0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0e, 0x00],
|
|
184
|
+
109: [0x00, 0x00, 0x1a, 0x15, 0x15, 0x11, 0x11, 0x00],
|
|
185
|
+
110: [0x00, 0x00, 0x16, 0x19, 0x11, 0x11, 0x11, 0x00],
|
|
186
|
+
111: [0x00, 0x00, 0x0e, 0x11, 0x11, 0x11, 0x0e, 0x00],
|
|
187
|
+
112: [0x00, 0x00, 0x1e, 0x11, 0x1e, 0x10, 0x10, 0x00],
|
|
188
|
+
113: [0x00, 0x00, 0x0d, 0x13, 0x0f, 0x01, 0x01, 0x00],
|
|
189
|
+
114: [0x00, 0x00, 0x16, 0x19, 0x10, 0x10, 0x10, 0x00],
|
|
190
|
+
115: [0x00, 0x00, 0x0e, 0x10, 0x0e, 0x01, 0x1e, 0x00],
|
|
191
|
+
116: [0x08, 0x08, 0x1c, 0x08, 0x08, 0x09, 0x06, 0x00],
|
|
192
|
+
117: [0x00, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0d, 0x00],
|
|
193
|
+
118: [0x00, 0x00, 0x11, 0x11, 0x11, 0x0a, 0x04, 0x00],
|
|
194
|
+
119: [0x00, 0x00, 0x11, 0x11, 0x15, 0x15, 0x0a, 0x00],
|
|
195
|
+
120: [0x00, 0x00, 0x11, 0x0a, 0x04, 0x0a, 0x11, 0x00],
|
|
196
|
+
121: [0x00, 0x00, 0x11, 0x11, 0x0f, 0x01, 0x0e, 0x00],
|
|
197
|
+
122: [0x00, 0x00, 0x1f, 0x02, 0x04, 0x08, 0x1f, 0x00],
|
|
198
|
+
// { | } ~
|
|
199
|
+
123: [0x02, 0x04, 0x04, 0x08, 0x04, 0x04, 0x02, 0x00],
|
|
200
|
+
124: [0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00],
|
|
201
|
+
125: [0x08, 0x04, 0x04, 0x02, 0x04, 0x04, 0x08, 0x00],
|
|
202
|
+
126: [0x00, 0x00, 0x08, 0x15, 0x02, 0x00, 0x00, 0x00]
|
|
203
|
+
};
|
|
204
|
+
// =============================================================================
|
|
205
|
+
// Bitmap Rendering
|
|
206
|
+
// =============================================================================
|
|
207
|
+
/** Render text string to a grayscale bitmap (0 = transparent, 255 = opaque). */
|
|
208
|
+
function renderTextBitmap(text, scale) {
|
|
209
|
+
const charW = GLYPH_WIDTH * scale;
|
|
210
|
+
const charH = GLYPH_HEIGHT * scale;
|
|
211
|
+
const width = text.length * charW;
|
|
212
|
+
const height = charH;
|
|
213
|
+
const pixels = new Uint8Array(width * height);
|
|
214
|
+
for (let ci = 0; ci < text.length; ci++) {
|
|
215
|
+
const code = text.charCodeAt(ci);
|
|
216
|
+
const glyph = FONT_DATA[code] ?? FONT_DATA[63]; // fallback to '?'
|
|
217
|
+
const xOff = ci * charW;
|
|
218
|
+
for (let row = 0; row < GLYPH_HEIGHT; row++) {
|
|
219
|
+
const bits = glyph[row];
|
|
220
|
+
for (let col = 0; col < GLYPH_WIDTH; col++) {
|
|
221
|
+
if (bits & (1 << (GLYPH_WIDTH - 1 - col))) {
|
|
222
|
+
// Fill scaled pixel block
|
|
223
|
+
for (let sy = 0; sy < scale; sy++) {
|
|
224
|
+
for (let sx = 0; sx < scale; sx++) {
|
|
225
|
+
const px = xOff + col * scale + sx;
|
|
226
|
+
const py = row * scale + sy;
|
|
227
|
+
if (px < width && py < height) {
|
|
228
|
+
pixels[py * width + px] = 255;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { width, height, pixels };
|
|
237
|
+
}
|
|
238
|
+
/** Rotate a grayscale bitmap by the given angle in degrees. */
|
|
239
|
+
function rotateBitmap(pixels, srcW, srcH, angleDeg) {
|
|
240
|
+
if (angleDeg === 0) {
|
|
241
|
+
return { width: srcW, height: srcH, pixels };
|
|
242
|
+
}
|
|
243
|
+
const rad = (angleDeg * Math.PI) / 180;
|
|
244
|
+
const cos = Math.cos(rad);
|
|
245
|
+
const sin = Math.sin(rad);
|
|
246
|
+
// Compute bounding box of rotated rectangle
|
|
247
|
+
const corners = [
|
|
248
|
+
{ x: 0, y: 0 },
|
|
249
|
+
{ x: srcW, y: 0 },
|
|
250
|
+
{ x: srcW, y: srcH },
|
|
251
|
+
{ x: 0, y: srcH }
|
|
252
|
+
];
|
|
253
|
+
let minX = Infinity;
|
|
254
|
+
let minY = Infinity;
|
|
255
|
+
let maxX = -Infinity;
|
|
256
|
+
let maxY = -Infinity;
|
|
257
|
+
for (const c of corners) {
|
|
258
|
+
const rx = c.x * cos - c.y * sin;
|
|
259
|
+
const ry = c.x * sin + c.y * cos;
|
|
260
|
+
minX = Math.min(minX, rx);
|
|
261
|
+
minY = Math.min(minY, ry);
|
|
262
|
+
maxX = Math.max(maxX, rx);
|
|
263
|
+
maxY = Math.max(maxY, ry);
|
|
264
|
+
}
|
|
265
|
+
const dstW = Math.ceil(maxX - minX);
|
|
266
|
+
const dstH = Math.ceil(maxY - minY);
|
|
267
|
+
const dst = new Uint8Array(dstW * dstH);
|
|
268
|
+
// Inverse rotation: for each dst pixel, find the source pixel
|
|
269
|
+
const invCos = cos; // cos(-θ) = cos(θ)
|
|
270
|
+
const invSin = -sin; // sin(-θ) = -sin(θ)
|
|
271
|
+
for (let dy = 0; dy < dstH; dy++) {
|
|
272
|
+
for (let dx = 0; dx < dstW; dx++) {
|
|
273
|
+
// Map dst to world, then inverse-rotate to source
|
|
274
|
+
const wx = dx + minX;
|
|
275
|
+
const wy = dy + minY;
|
|
276
|
+
const sx = Math.round(wx * invCos - wy * invSin);
|
|
277
|
+
const sy = Math.round(wx * invSin + wy * invCos);
|
|
278
|
+
if (sx >= 0 && sx < srcW && sy >= 0 && sy < srcH) {
|
|
279
|
+
dst[dy * dstW + dx] = pixels[sy * srcW + sx];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return { width: dstW, height: dstH, pixels: dst };
|
|
284
|
+
}
|
|
285
|
+
// =============================================================================
|
|
286
|
+
// PNG Encoder (RGBA, deflate-compressed, with alpha)
|
|
287
|
+
// =============================================================================
|
|
288
|
+
/** Encode RGBA pixel data to a PNG file. */
|
|
289
|
+
function encodePng(rgba, width, height) {
|
|
290
|
+
// Build IDAT data: filter byte (0 = None) + raw RGBA for each row
|
|
291
|
+
const rawRowSize = 1 + width * 4; // filter byte + pixels
|
|
292
|
+
const rawData = new Uint8Array(rawRowSize * height);
|
|
293
|
+
for (let y = 0; y < height; y++) {
|
|
294
|
+
rawData[y * rawRowSize] = 0; // filter: None
|
|
295
|
+
rawData.set(rgba.subarray(y * width * 4, (y + 1) * width * 4), y * rawRowSize + 1);
|
|
296
|
+
}
|
|
297
|
+
// Wrap in zlib stream with deflate compression
|
|
298
|
+
const deflated = zlibCompress(rawData);
|
|
299
|
+
// PNG signature
|
|
300
|
+
const sig = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
301
|
+
// IHDR chunk
|
|
302
|
+
const ihdr = new Uint8Array(13);
|
|
303
|
+
writeU32BE(ihdr, 0, width);
|
|
304
|
+
writeU32BE(ihdr, 4, height);
|
|
305
|
+
ihdr[8] = 8; // bit depth
|
|
306
|
+
ihdr[9] = 6; // color type: RGBA
|
|
307
|
+
ihdr[10] = 0; // compression
|
|
308
|
+
ihdr[11] = 0; // filter
|
|
309
|
+
ihdr[12] = 0; // interlace
|
|
310
|
+
const ihdrChunk = pngChunk(0x49484452, ihdr);
|
|
311
|
+
// IDAT chunk
|
|
312
|
+
const idatChunk = pngChunk(0x49444154, deflated);
|
|
313
|
+
// IEND chunk
|
|
314
|
+
const iendChunk = pngChunk(0x49454e44, new Uint8Array(0));
|
|
315
|
+
// Concatenate
|
|
316
|
+
const result = new Uint8Array(sig.length + ihdrChunk.length + idatChunk.length + iendChunk.length);
|
|
317
|
+
let offset = 0;
|
|
318
|
+
result.set(sig, offset);
|
|
319
|
+
offset += sig.length;
|
|
320
|
+
result.set(ihdrChunk, offset);
|
|
321
|
+
offset += ihdrChunk.length;
|
|
322
|
+
result.set(idatChunk, offset);
|
|
323
|
+
offset += idatChunk.length;
|
|
324
|
+
result.set(iendChunk, offset);
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
/** Build a PNG chunk: length(4) + type(4) + data + crc32(4). */
|
|
328
|
+
function pngChunk(type, data) {
|
|
329
|
+
const chunk = new Uint8Array(12 + data.length);
|
|
330
|
+
writeU32BE(chunk, 0, data.length);
|
|
331
|
+
writeU32BE(chunk, 4, type);
|
|
332
|
+
chunk.set(data, 8);
|
|
333
|
+
// CRC32 over type + data
|
|
334
|
+
const crc = crc32(chunk.subarray(4, 8 + data.length));
|
|
335
|
+
writeU32BE(chunk, 8 + data.length, crc);
|
|
336
|
+
return chunk;
|
|
337
|
+
}
|
|
338
|
+
/** Write a 32-bit big-endian unsigned int. */
|
|
339
|
+
function writeU32BE(buf, offset, value) {
|
|
340
|
+
buf[offset] = (value >>> 24) & 0xff;
|
|
341
|
+
buf[offset + 1] = (value >>> 16) & 0xff;
|
|
342
|
+
buf[offset + 2] = (value >>> 8) & 0xff;
|
|
343
|
+
buf[offset + 3] = value & 0xff;
|
|
344
|
+
}
|
|
345
|
+
/** Wrap raw data in a zlib stream with deflate compression. */
|
|
346
|
+
function zlibCompress(data) {
|
|
347
|
+
// Zlib header: CMF=0x78, FLG=0x01 (deflate, no dict, check bits)
|
|
348
|
+
const deflated = (0, deflate_fallback_1.deflateRawCompressed)(data, 6);
|
|
349
|
+
const adler = adler32(data);
|
|
350
|
+
const result = new Uint8Array(2 + deflated.length + 4);
|
|
351
|
+
result[0] = 0x78;
|
|
352
|
+
result[1] = 0x01;
|
|
353
|
+
result.set(deflated, 2);
|
|
354
|
+
writeU32BE(result, 2 + deflated.length, adler);
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
357
|
+
/** Compute Adler-32 checksum. */
|
|
358
|
+
function adler32(data) {
|
|
359
|
+
let a = 1;
|
|
360
|
+
let b = 0;
|
|
361
|
+
for (let i = 0; i < data.length; i++) {
|
|
362
|
+
a = (a + data[i]) % 65521;
|
|
363
|
+
b = (b + a) % 65521;
|
|
364
|
+
}
|
|
365
|
+
return (b << 16) | a;
|
|
366
|
+
}
|
|
367
|
+
/** CRC32 lookup table. */
|
|
368
|
+
const CRC_TABLE = /* @__PURE__ */ (() => {
|
|
369
|
+
const table = new Uint32Array(256);
|
|
370
|
+
for (let n = 0; n < 256; n++) {
|
|
371
|
+
let c = n;
|
|
372
|
+
for (let k = 0; k < 8; k++) {
|
|
373
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
374
|
+
}
|
|
375
|
+
table[n] = c;
|
|
376
|
+
}
|
|
377
|
+
return table;
|
|
378
|
+
})();
|
|
379
|
+
/** Compute CRC32 checksum. */
|
|
380
|
+
function crc32(data) {
|
|
381
|
+
let crc = 0xffffffff;
|
|
382
|
+
for (let i = 0; i < data.length; i++) {
|
|
383
|
+
crc = CRC_TABLE[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);
|
|
384
|
+
}
|
|
385
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
386
|
+
}
|
|
@@ -107,6 +107,8 @@ class Worksheet {
|
|
|
107
107
|
this.conditionalFormattings = [];
|
|
108
108
|
// for form controls (legacy checkboxes, etc.)
|
|
109
109
|
this.formControls = [];
|
|
110
|
+
// watermark configuration
|
|
111
|
+
this._watermark = null;
|
|
110
112
|
}
|
|
111
113
|
get name() {
|
|
112
114
|
return this._name;
|
|
@@ -981,6 +983,79 @@ class Worksheet {
|
|
|
981
983
|
return image && image.imageId;
|
|
982
984
|
}
|
|
983
985
|
// =========================================================================
|
|
986
|
+
// Watermark
|
|
987
|
+
/**
|
|
988
|
+
* Add a watermark to the worksheet using an image from `workbook.addImage()`.
|
|
989
|
+
*
|
|
990
|
+
* The watermark can be placed in one of two modes:
|
|
991
|
+
*
|
|
992
|
+
* - **overlay** (default): Places the watermark image as a drawing on top of cells.
|
|
993
|
+
* Visible on screen AND when printed. Supports transparency via DrawingML `alphaModFix`.
|
|
994
|
+
*
|
|
995
|
+
* - **header**: Places the watermark image in the page header using VML.
|
|
996
|
+
* Visible in Page Layout view and when printed. Renders behind cell content.
|
|
997
|
+
* Transparency must be baked into the image (PNG with alpha channel).
|
|
998
|
+
*
|
|
999
|
+
* @param options - Watermark configuration
|
|
1000
|
+
*
|
|
1001
|
+
* @example Overlay watermark with transparency:
|
|
1002
|
+
* ```typescript
|
|
1003
|
+
* const imgId = workbook.addImage({ buffer: pngData, extension: "png" });
|
|
1004
|
+
* worksheet.addWatermark({ imageId: imgId, opacity: 0.15 });
|
|
1005
|
+
* ```
|
|
1006
|
+
*
|
|
1007
|
+
* @example Header watermark (behind content):
|
|
1008
|
+
* ```typescript
|
|
1009
|
+
* const imgId = workbook.addImage({ buffer: pngData, extension: "png" });
|
|
1010
|
+
* worksheet.addWatermark({ imageId: imgId, mode: "header" });
|
|
1011
|
+
* ```
|
|
1012
|
+
*/
|
|
1013
|
+
addWatermark(options) {
|
|
1014
|
+
// Remove any existing watermark media entries first
|
|
1015
|
+
this._media = this._media.filter(m => m.type !== "watermark" && m.type !== "headerImage");
|
|
1016
|
+
this._watermark = {
|
|
1017
|
+
imageId: String(options.imageId),
|
|
1018
|
+
mode: options.mode ?? "overlay",
|
|
1019
|
+
opacity: options.opacity,
|
|
1020
|
+
headerWidth: options.headerWidth,
|
|
1021
|
+
headerHeight: options.headerHeight,
|
|
1022
|
+
applyTo: options.applyTo
|
|
1023
|
+
};
|
|
1024
|
+
if (this._watermark.mode === "overlay") {
|
|
1025
|
+
// Add as a special "watermark" media entry for the drawing pipeline
|
|
1026
|
+
const model = {
|
|
1027
|
+
type: "watermark",
|
|
1028
|
+
imageId: String(options.imageId),
|
|
1029
|
+
opacity: options.opacity
|
|
1030
|
+
};
|
|
1031
|
+
this._media.push(new image_1.Image(this, model));
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
// Header mode: add as a "headerImage" media entry for the VML pipeline
|
|
1035
|
+
const model = {
|
|
1036
|
+
type: "headerImage",
|
|
1037
|
+
imageId: String(options.imageId),
|
|
1038
|
+
headerWidth: options.headerWidth,
|
|
1039
|
+
headerHeight: options.headerHeight,
|
|
1040
|
+
applyTo: options.applyTo
|
|
1041
|
+
};
|
|
1042
|
+
this._media.push(new image_1.Image(this, model));
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Get the current watermark configuration, or null if none is set.
|
|
1047
|
+
*/
|
|
1048
|
+
getWatermark() {
|
|
1049
|
+
return this._watermark;
|
|
1050
|
+
}
|
|
1051
|
+
/**
|
|
1052
|
+
* Remove the watermark from the worksheet.
|
|
1053
|
+
*/
|
|
1054
|
+
removeWatermark() {
|
|
1055
|
+
this._watermark = null;
|
|
1056
|
+
this._media = this._media.filter(m => m.type !== "watermark" && m.type !== "headerImage");
|
|
1057
|
+
}
|
|
1058
|
+
// =========================================================================
|
|
984
1059
|
// Form Controls (Legacy Checkboxes)
|
|
985
1060
|
/**
|
|
986
1061
|
* Add a form control checkbox to the worksheet.
|
|
@@ -1296,6 +1371,7 @@ class Worksheet {
|
|
|
1296
1371
|
pivotTables: this.pivotTables,
|
|
1297
1372
|
conditionalFormattings: this.conditionalFormattings,
|
|
1298
1373
|
formControls: this.formControls.map(fc => fc.model),
|
|
1374
|
+
watermark: this._watermark,
|
|
1299
1375
|
drawing: this._drawing
|
|
1300
1376
|
};
|
|
1301
1377
|
// =================================================
|
|
@@ -1351,6 +1427,29 @@ class Worksheet {
|
|
|
1351
1427
|
this.views = value.views;
|
|
1352
1428
|
this.autoFilter = value.autoFilter;
|
|
1353
1429
|
this._media = value.media.map(medium => new image_1.Image(this, medium));
|
|
1430
|
+
// Restore watermark state from media entries
|
|
1431
|
+
this._watermark = value.watermark ?? null;
|
|
1432
|
+
if (!this._watermark) {
|
|
1433
|
+
for (const medium of this._media) {
|
|
1434
|
+
if (medium.type === "watermark") {
|
|
1435
|
+
this._watermark = {
|
|
1436
|
+
imageId: medium.imageId ?? "",
|
|
1437
|
+
mode: "overlay",
|
|
1438
|
+
opacity: medium.opacity
|
|
1439
|
+
};
|
|
1440
|
+
break;
|
|
1441
|
+
}
|
|
1442
|
+
else if (medium.type === "headerImage") {
|
|
1443
|
+
this._watermark = {
|
|
1444
|
+
imageId: medium.imageId ?? "",
|
|
1445
|
+
mode: "header",
|
|
1446
|
+
headerWidth: medium.headerWidth,
|
|
1447
|
+
headerHeight: medium.headerHeight
|
|
1448
|
+
};
|
|
1449
|
+
break;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1354
1453
|
this.sheetProtection = value.sheetProtection;
|
|
1355
1454
|
this.tables = value.tables.reduce((tables, table) => {
|
|
1356
1455
|
const t = new table_1.Table(this, table);
|
|
@@ -107,10 +107,11 @@ class ContentTypesXform extends base_xform_1.BaseXform {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
// VML extension is needed for comments or
|
|
110
|
+
// VML extension is needed for comments, form controls, or header watermarks
|
|
111
111
|
const hasComments = model.commentRefs && model.commentRefs.length > 0;
|
|
112
112
|
const hasFormControls = model.formControlRefs && model.formControlRefs.length > 0;
|
|
113
|
-
|
|
113
|
+
const hasHeaderWatermark = model.hasHeaderWatermark === true;
|
|
114
|
+
if (hasComments || hasFormControls || hasHeaderWatermark) {
|
|
114
115
|
xmlStream.leafNode("Default", {
|
|
115
116
|
Extension: "vml",
|
|
116
117
|
ContentType: "application/vnd.openxmlformats-officedocument.vmlDrawing"
|
|
@@ -38,7 +38,12 @@ class BaseCellAnchorXform extends base_xform_1.BaseXform {
|
|
|
38
38
|
if (match) {
|
|
39
39
|
const name = match[1];
|
|
40
40
|
const mediaId = options.mediaIndex[name];
|
|
41
|
-
|
|
41
|
+
const medium = options.media[mediaId];
|
|
42
|
+
// Preserve alphaModFix (transparency) from the picture model if present
|
|
43
|
+
if (medium && model.alphaModFix !== undefined) {
|
|
44
|
+
return { ...medium, alphaModFix: model.alphaModFix };
|
|
45
|
+
}
|
|
46
|
+
return medium;
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
return undefined;
|
|
@@ -16,7 +16,6 @@ class BlipFillXform extends base_xform_1.BaseXform {
|
|
|
16
16
|
render(xmlStream, model) {
|
|
17
17
|
xmlStream.openNode(this.tag);
|
|
18
18
|
this.map["a:blip"].render(xmlStream, model);
|
|
19
|
-
// TODO: options for this + parsing
|
|
20
19
|
xmlStream.openNode("a:stretch");
|
|
21
20
|
xmlStream.leafNode("a:fillRect");
|
|
22
21
|
xmlStream.closeNode();
|
|
@@ -11,12 +11,23 @@ class BlipXform extends base_xform_1.BaseXform {
|
|
|
11
11
|
return "a:blip";
|
|
12
12
|
}
|
|
13
13
|
render(xmlStream, model) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
if (model.alphaModFix !== undefined && model.alphaModFix < 100000) {
|
|
15
|
+
// Render as open/close node with a:alphaModFix child
|
|
16
|
+
xmlStream.openNode(this.tag, {
|
|
17
|
+
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
18
|
+
"r:embed": model.rId,
|
|
19
|
+
cstate: "print"
|
|
20
|
+
});
|
|
21
|
+
xmlStream.leafNode("a:alphaModFix", { amt: String(model.alphaModFix) });
|
|
22
|
+
xmlStream.closeNode();
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
xmlStream.leafNode(this.tag, {
|
|
26
|
+
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
27
|
+
"r:embed": model.rId,
|
|
28
|
+
cstate: "print"
|
|
29
|
+
});
|
|
30
|
+
}
|
|
20
31
|
}
|
|
21
32
|
parseOpen(node) {
|
|
22
33
|
switch (node.name) {
|
|
@@ -25,6 +36,11 @@ class BlipXform extends base_xform_1.BaseXform {
|
|
|
25
36
|
rId: node.attributes["r:embed"]
|
|
26
37
|
};
|
|
27
38
|
return true;
|
|
39
|
+
case "a:alphaModFix":
|
|
40
|
+
if (node.attributes.amt) {
|
|
41
|
+
this.model.alphaModFix = parseInt(node.attributes.amt, 10);
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
28
44
|
default:
|
|
29
45
|
return true;
|
|
30
46
|
}
|
|
@@ -24,7 +24,11 @@ class PicXform extends base_xform_1.BaseXform {
|
|
|
24
24
|
render(xmlStream, model) {
|
|
25
25
|
xmlStream.openNode(this.tag);
|
|
26
26
|
this.map["xdr:nvPicPr"].render(xmlStream, model);
|
|
27
|
-
|
|
27
|
+
// Pass alphaModFix through to blipFill → blip
|
|
28
|
+
this.map["xdr:blipFill"].render(xmlStream, {
|
|
29
|
+
rId: model.rId,
|
|
30
|
+
alphaModFix: model.alphaModFix
|
|
31
|
+
});
|
|
28
32
|
this.map["xdr:spPr"].render(xmlStream, model);
|
|
29
33
|
xmlStream.closeNode();
|
|
30
34
|
}
|