@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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.exportPdf = exportPdf;
|
|
13
13
|
const pdf_writer_1 = require("../core/pdf-writer");
|
|
14
|
+
const pdf_stream_1 = require("../core/pdf-stream");
|
|
14
15
|
const pdf_object_1 = require("../core/pdf-object");
|
|
15
16
|
const font_manager_1 = require("../font/font-manager");
|
|
16
17
|
const ttf_parser_1 = require("../font/ttf-parser");
|
|
@@ -86,8 +87,21 @@ async function finishExport(ctx, workbook, options) {
|
|
|
86
87
|
ensureAtLeastOnePage(allPages, documentOptions, sheets);
|
|
87
88
|
fixPageNumbers(allPages);
|
|
88
89
|
trackFontsForHeaders(allPages, fontManager);
|
|
90
|
+
// Track watermark fonts
|
|
91
|
+
const watermark = documentOptions.watermark;
|
|
92
|
+
if (watermark && watermark.type === "text") {
|
|
93
|
+
const wmFontFamily = watermark.fontFamily ?? "Helvetica";
|
|
94
|
+
const wmBold = watermark.bold ?? false;
|
|
95
|
+
const wmItalic = watermark.italic ?? false;
|
|
96
|
+
if (fontManager.hasEmbeddedFont()) {
|
|
97
|
+
fontManager.trackText(watermark.text);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
fontManager.ensureFont((0, font_manager_1.resolvePdfFontName)(wmFontFamily, wmBold, wmItalic));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
89
103
|
const fontObjectMap = fontManager.writeFontResources(writer);
|
|
90
|
-
const { pageObjNums, sheetFirstPage, pagesTreeObjNum } = await renderAllPages(allPages, fontManager, writer, fontObjectMap);
|
|
104
|
+
const { pageObjNums, sheetFirstPage, pagesTreeObjNum } = await renderAllPages(allPages, fontManager, writer, fontObjectMap, watermark);
|
|
91
105
|
return buildFinalPdf(writer, pageObjNums, pagesTreeObjNum, sheetFirstPage, documentOptions, workbook, options);
|
|
92
106
|
}
|
|
93
107
|
function ensureAtLeastOnePage(allPages, documentOptions, sheets) {
|
|
@@ -136,20 +150,20 @@ function trackFontsForHeaders(allPages, fontManager) {
|
|
|
136
150
|
}
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
|
-
async function renderAllPages(allPages, fontManager, writer, fontObjectMap) {
|
|
153
|
+
async function renderAllPages(allPages, fontManager, writer, fontObjectMap, watermark) {
|
|
140
154
|
const pageObjNums = [];
|
|
141
155
|
const pagesTreeObjNum = writer.allocObject();
|
|
142
156
|
const sheetFirstPage = new Map();
|
|
143
157
|
const totalPages = allPages.length;
|
|
144
158
|
for (let i = 0; i < allPages.length; i++) {
|
|
145
|
-
renderSinglePage(allPages[i], fontManager, writer, fontObjectMap, totalPages, pageObjNums, pagesTreeObjNum, sheetFirstPage);
|
|
159
|
+
renderSinglePage(allPages[i], fontManager, writer, fontObjectMap, totalPages, pageObjNums, pagesTreeObjNum, sheetFirstPage, watermark);
|
|
146
160
|
if (i < allPages.length - 1) {
|
|
147
161
|
await (0, utils_base_1.yieldToEventLoop)();
|
|
148
162
|
}
|
|
149
163
|
}
|
|
150
164
|
return { pageObjNums, sheetFirstPage, pagesTreeObjNum };
|
|
151
165
|
}
|
|
152
|
-
function renderSinglePage(page, fontManager, writer, fontObjectMap, totalPages, pageObjNums, pagesTreeObjNum, sheetFirstPage) {
|
|
166
|
+
function renderSinglePage(page, fontManager, writer, fontObjectMap, totalPages, pageObjNums, pagesTreeObjNum, sheetFirstPage, watermark) {
|
|
153
167
|
try {
|
|
154
168
|
const { stream: contentStream, alphaValues } = (0, page_renderer_1.renderPage)(page, page.options, fontManager, totalPages);
|
|
155
169
|
// Handle images: create XObject Image entries and draw them
|
|
@@ -163,10 +177,46 @@ function renderSinglePage(page, fontManager, writer, fontObjectMap, totalPages,
|
|
|
163
177
|
contentStream.drawImage(imgName, img.rect.x, img.rect.y, img.rect.width, img.rect.height);
|
|
164
178
|
}
|
|
165
179
|
}
|
|
166
|
-
//
|
|
180
|
+
// --- Render watermark into a separate content stream ---
|
|
181
|
+
// PDF supports Contents as an array of stream references. The watermark stream
|
|
182
|
+
// is placed BEFORE the main content stream so it renders behind everything.
|
|
183
|
+
let watermarkContentObjNum;
|
|
184
|
+
const shouldApplyWatermark = watermark && isWatermarkApplicable(watermark, page);
|
|
185
|
+
if (shouldApplyWatermark) {
|
|
186
|
+
const wmContentStream = new pdf_stream_1.PdfContentStream();
|
|
187
|
+
const wmResult = (0, page_renderer_1.renderWatermark)(wmContentStream, page, watermark, fontManager);
|
|
188
|
+
// Register watermark alpha values in the shared set
|
|
189
|
+
for (const alpha of wmResult.alphaValues) {
|
|
190
|
+
alphaValues.add(alpha);
|
|
191
|
+
}
|
|
192
|
+
// Register watermark image XObjects
|
|
193
|
+
for (const wmImg of wmResult.imageXObjects) {
|
|
194
|
+
const imgObjNum = writeImageXObject(writer, wmImg.data, wmImg.format);
|
|
195
|
+
imageXObjects.set(wmImg.name, imgObjNum);
|
|
196
|
+
}
|
|
197
|
+
// Write watermark content stream object
|
|
198
|
+
watermarkContentObjNum = writer.allocObject();
|
|
199
|
+
writer.addStreamObject(watermarkContentObjNum, new pdf_object_1.PdfDict(), wmContentStream);
|
|
200
|
+
}
|
|
201
|
+
// Add main content stream object
|
|
167
202
|
const contentObjNum = writer.allocObject();
|
|
168
|
-
|
|
169
|
-
|
|
203
|
+
writer.addStreamObject(contentObjNum, new pdf_object_1.PdfDict(), contentStream);
|
|
204
|
+
// Build Contents reference — array if watermark exists, single ref otherwise.
|
|
205
|
+
// placement "under" (default): watermark stream first, then content
|
|
206
|
+
// placement "over": content first, then watermark stream on top
|
|
207
|
+
let contentsRef;
|
|
208
|
+
if (watermarkContentObjNum) {
|
|
209
|
+
const placement = watermark?.placement ?? "under";
|
|
210
|
+
if (placement === "over") {
|
|
211
|
+
contentsRef = `[${(0, pdf_object_1.pdfRef)(contentObjNum)} ${(0, pdf_object_1.pdfRef)(watermarkContentObjNum)}]`;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
contentsRef = `[${(0, pdf_object_1.pdfRef)(watermarkContentObjNum)} ${(0, pdf_object_1.pdfRef)(contentObjNum)}]`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
contentsRef = (0, pdf_object_1.pdfRef)(contentObjNum);
|
|
219
|
+
}
|
|
170
220
|
// Add resources dictionary object
|
|
171
221
|
const resourcesObjNum = writer.allocObject();
|
|
172
222
|
const fontDictStr = fontManager.buildFontDictString(fontObjectMap);
|
|
@@ -215,7 +265,7 @@ function renderSinglePage(page, fontManager, writer, fontObjectMap, totalPages,
|
|
|
215
265
|
parentRef: pagesTreeObjNum,
|
|
216
266
|
width: page.width,
|
|
217
267
|
height: page.height,
|
|
218
|
-
contentsRef:
|
|
268
|
+
contentsRef: contentsRef,
|
|
219
269
|
resourcesRef: resourcesObjNum,
|
|
220
270
|
annotRefs: annotRefs.length > 0 ? annotRefs : undefined
|
|
221
271
|
});
|
|
@@ -340,7 +390,8 @@ function resolveOptions(options, sheet) {
|
|
|
340
390
|
title: options?.title ?? "",
|
|
341
391
|
author: options?.author ?? "",
|
|
342
392
|
subject: options?.subject ?? "",
|
|
343
|
-
creator: options?.creator ?? "excelts"
|
|
393
|
+
creator: options?.creator ?? "excelts",
|
|
394
|
+
watermark: options?.watermark
|
|
344
395
|
};
|
|
345
396
|
}
|
|
346
397
|
/** Map PaperSize enum values to PDF page sizes. */
|
|
@@ -431,6 +482,28 @@ function buildOutlines(writer, sheetFirstPage, pageObjNums) {
|
|
|
431
482
|
return outlinesObjNum;
|
|
432
483
|
}
|
|
433
484
|
// =============================================================================
|
|
485
|
+
// Watermark Filtering
|
|
486
|
+
// =============================================================================
|
|
487
|
+
/**
|
|
488
|
+
* Check if a watermark should be applied to a specific page based on
|
|
489
|
+
* optional page number and sheet name filters.
|
|
490
|
+
*/
|
|
491
|
+
function isWatermarkApplicable(watermark, page) {
|
|
492
|
+
if (watermark.pages && watermark.pages.length > 0) {
|
|
493
|
+
if (!watermark.pages.includes(page.pageNumber)) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (watermark.sheets && watermark.sheets.length > 0) {
|
|
498
|
+
// Case-insensitive sheet name matching, consistent with the rest of the API
|
|
499
|
+
const sheetLower = page.sheetName.toLowerCase();
|
|
500
|
+
if (!watermark.sheets.some(s => s.toLowerCase() === sheetLower)) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
// =============================================================================
|
|
434
507
|
// Image XObject
|
|
435
508
|
// =============================================================================
|
|
436
509
|
/**
|
|
@@ -447,7 +520,7 @@ function writeImageXObject(writer, data, format) {
|
|
|
447
520
|
*/
|
|
448
521
|
function writeJpegImageXObject(writer, data) {
|
|
449
522
|
const objNum = writer.allocObject();
|
|
450
|
-
const dims =
|
|
523
|
+
const dims = (0, page_renderer_1.parseImageDimensions)(data, "jpeg");
|
|
451
524
|
const dict = new pdf_object_1.PdfDict()
|
|
452
525
|
.set("Type", "/XObject")
|
|
453
526
|
.set("Subtype", "/Image")
|
|
@@ -489,39 +562,3 @@ function writePngImageXObject(writer, data) {
|
|
|
489
562
|
writer.addStreamObject(objNum, dict, png.pixels);
|
|
490
563
|
return objNum;
|
|
491
564
|
}
|
|
492
|
-
/**
|
|
493
|
-
* Extract width and height from a JPEG file header.
|
|
494
|
-
* Scans for SOF markers (SOF0-SOF3, SOF5-SOF7, SOF9-SOF11, SOF13-SOF15)
|
|
495
|
-
* which all share the same frame header layout.
|
|
496
|
-
* Handles 0xFF padding bytes per JPEG spec.
|
|
497
|
-
*/
|
|
498
|
-
function getJpegDimensions(data) {
|
|
499
|
-
let offset = 2; // skip SOI marker (0xFFD8)
|
|
500
|
-
while (offset < data.length - 1) {
|
|
501
|
-
// Skip any 0xFF fill bytes
|
|
502
|
-
while (offset < data.length && data[offset] === 0xff && data[offset + 1] === 0xff) {
|
|
503
|
-
offset++;
|
|
504
|
-
}
|
|
505
|
-
if (offset >= data.length - 1 || data[offset] !== 0xff) {
|
|
506
|
-
break;
|
|
507
|
-
}
|
|
508
|
-
const marker = data[offset + 1];
|
|
509
|
-
// SOF markers: C0-C3, C5-C7, C9-CB, CD-CF (excluding C4=DHT, C8=JPG, CC=DAC)
|
|
510
|
-
const isSof = marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
|
|
511
|
-
if (isSof) {
|
|
512
|
-
if (offset + 8 < data.length) {
|
|
513
|
-
const height = (data[offset + 5] << 8) | data[offset + 6];
|
|
514
|
-
const width = (data[offset + 7] << 8) | data[offset + 8];
|
|
515
|
-
return { width, height };
|
|
516
|
-
}
|
|
517
|
-
break;
|
|
518
|
-
}
|
|
519
|
-
// Skip segment: 2 byte marker + segment length (includes the 2 length bytes)
|
|
520
|
-
if (offset + 3 >= data.length) {
|
|
521
|
-
break;
|
|
522
|
-
}
|
|
523
|
-
const segLen = (data[offset + 2] << 8) | data[offset + 3];
|
|
524
|
-
offset += 2 + segLen;
|
|
525
|
-
}
|
|
526
|
-
return { width: 1, height: 1 };
|
|
527
|
-
}
|
|
@@ -25,6 +25,8 @@ export * from "./modules/excel/enums.js";
|
|
|
25
25
|
// =============================================================================
|
|
26
26
|
// Export all type definitions from types.ts
|
|
27
27
|
export * from "./modules/excel/types.js";
|
|
28
|
+
// Watermark image generator utility
|
|
29
|
+
export { createTextWatermarkImage } from "./modules/excel/utils/watermark-image.js";
|
|
28
30
|
// =============================================================================
|
|
29
31
|
// Streaming Writer (Browser-compatible)
|
|
30
32
|
// Uses cross-platform base implementation without Node.js fs
|
package/dist/esm/index.js
CHANGED
|
@@ -29,6 +29,8 @@ export * from "./modules/excel/enums.js";
|
|
|
29
29
|
// =============================================================================
|
|
30
30
|
// Export all type definitions from types.ts
|
|
31
31
|
export * from "./modules/excel/types.js";
|
|
32
|
+
// Watermark image generator utility
|
|
33
|
+
export { createTextWatermarkImage } from "./modules/excel/utils/watermark-image.js";
|
|
32
34
|
export { CsvParserStream, CsvFormatterStream, createCsvParserStream, createCsvFormatterStream } from "./modules/csv/stream/index.js";
|
|
33
35
|
// =============================================================================
|
|
34
36
|
// Additional Classes & Types
|
|
@@ -15,6 +15,20 @@ class Image {
|
|
|
15
15
|
type: this.type,
|
|
16
16
|
imageId: this.imageId ?? ""
|
|
17
17
|
};
|
|
18
|
+
case "watermark":
|
|
19
|
+
return {
|
|
20
|
+
type: this.type,
|
|
21
|
+
imageId: this.imageId ?? "",
|
|
22
|
+
opacity: this.opacity
|
|
23
|
+
};
|
|
24
|
+
case "headerImage":
|
|
25
|
+
return {
|
|
26
|
+
type: this.type,
|
|
27
|
+
imageId: this.imageId ?? "",
|
|
28
|
+
headerWidth: this.headerWidth,
|
|
29
|
+
headerHeight: this.headerHeight,
|
|
30
|
+
applyTo: this.applyTo
|
|
31
|
+
};
|
|
18
32
|
case "image": {
|
|
19
33
|
const range = this.range;
|
|
20
34
|
if (!range) {
|
|
@@ -36,9 +50,13 @@ class Image {
|
|
|
36
50
|
throw new ImageError("Invalid Image Type");
|
|
37
51
|
}
|
|
38
52
|
}
|
|
39
|
-
set model({ type, imageId, range, hyperlinks }) {
|
|
53
|
+
set model({ type, imageId, range, hyperlinks, opacity, headerWidth, headerHeight, applyTo }) {
|
|
40
54
|
this.type = type;
|
|
41
55
|
this.imageId = imageId;
|
|
56
|
+
this.opacity = opacity;
|
|
57
|
+
this.headerWidth = headerWidth;
|
|
58
|
+
this.headerHeight = headerHeight;
|
|
59
|
+
this.applyTo = applyTo;
|
|
42
60
|
if (type === "image") {
|
|
43
61
|
if (typeof range === "string") {
|
|
44
62
|
const decoded = colCache.decode(range);
|
|
@@ -67,6 +85,10 @@ class Image {
|
|
|
67
85
|
const cloned = new Image(target);
|
|
68
86
|
cloned.type = this.type;
|
|
69
87
|
cloned.imageId = this.imageId;
|
|
88
|
+
cloned.opacity = this.opacity;
|
|
89
|
+
cloned.headerWidth = this.headerWidth;
|
|
90
|
+
cloned.headerHeight = this.headerHeight;
|
|
91
|
+
cloned.applyTo = this.applyTo;
|
|
70
92
|
if (this.range) {
|
|
71
93
|
cloned.range = {
|
|
72
94
|
tl: this.range.tl.clone(target),
|
|
@@ -151,6 +151,8 @@ class WorksheetWriter {
|
|
|
151
151
|
// auto filter
|
|
152
152
|
this.autoFilter = options.autoFilter ?? null;
|
|
153
153
|
this._media = [];
|
|
154
|
+
// watermark
|
|
155
|
+
this._watermark = null;
|
|
154
156
|
// worksheet protection
|
|
155
157
|
this.sheetProtection = null;
|
|
156
158
|
// start writing to stream now
|
|
@@ -448,6 +450,57 @@ class WorksheetWriter {
|
|
|
448
450
|
getImages() {
|
|
449
451
|
return this._media;
|
|
450
452
|
}
|
|
453
|
+
// =========================================================================
|
|
454
|
+
// Watermark
|
|
455
|
+
/**
|
|
456
|
+
* Add a watermark to the worksheet using an image from `WorkbookWriter.addImage()`.
|
|
457
|
+
* Supports overlay mode (DrawingML with transparency) and header mode (VML behind content).
|
|
458
|
+
*/
|
|
459
|
+
addWatermark(options) {
|
|
460
|
+
// Remove existing watermark entries (both stored type tags)
|
|
461
|
+
this._media = this._media.filter(m => m._watermarkTag !== true);
|
|
462
|
+
const opacity = options.opacity !== undefined ? Math.max(0, Math.min(1, options.opacity)) : 0.15;
|
|
463
|
+
this._watermark = {
|
|
464
|
+
imageId: String(options.imageId),
|
|
465
|
+
mode: options.mode ?? "overlay",
|
|
466
|
+
opacity,
|
|
467
|
+
headerWidth: options.headerWidth,
|
|
468
|
+
headerHeight: options.headerHeight,
|
|
469
|
+
applyTo: options.applyTo
|
|
470
|
+
};
|
|
471
|
+
if (this._watermark.mode === "overlay") {
|
|
472
|
+
// Coverage range is computed lazily during commit() via _resolveWatermarkRange()
|
|
473
|
+
const entry = {
|
|
474
|
+
type: "image",
|
|
475
|
+
imageId: String(options.imageId),
|
|
476
|
+
range: {
|
|
477
|
+
tl: { nativeCol: 0, nativeColOff: 0, nativeRow: 0, nativeRowOff: 0 },
|
|
478
|
+
br: { nativeCol: 100, nativeColOff: 0, nativeRow: 200, nativeRowOff: 0 },
|
|
479
|
+
editAs: "absolute"
|
|
480
|
+
},
|
|
481
|
+
// Internal tag for dedup — not part of the WriterImageModel type
|
|
482
|
+
_watermarkTag: true,
|
|
483
|
+
opacity
|
|
484
|
+
};
|
|
485
|
+
this._media.push(entry);
|
|
486
|
+
}
|
|
487
|
+
// Note: header mode for streaming writer is limited — the VML file generation
|
|
488
|
+
// happens in WorkbookWriter.addWorksheets(), which handles worksheet.headerImage.
|
|
489
|
+
// We store the config in _watermark and it's picked up by the commit path.
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Get the current watermark configuration.
|
|
493
|
+
*/
|
|
494
|
+
getWatermark() {
|
|
495
|
+
return this._watermark;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Remove the watermark from the worksheet.
|
|
499
|
+
*/
|
|
500
|
+
removeWatermark() {
|
|
501
|
+
this._watermark = null;
|
|
502
|
+
this._media = this._media.filter(m => m._watermarkTag !== true);
|
|
503
|
+
}
|
|
451
504
|
/**
|
|
452
505
|
* Parse the user-supplied range into a normalised internal model
|
|
453
506
|
* mirroring what the regular Worksheet / Image class does.
|
|
@@ -626,6 +679,21 @@ class WorksheetWriter {
|
|
|
626
679
|
if (this._media.length === 0) {
|
|
627
680
|
return;
|
|
628
681
|
}
|
|
682
|
+
// Resolve watermark coverage range from actual worksheet dimensions
|
|
683
|
+
// (at commit time, all rows have been flushed so _dimensions is accurate)
|
|
684
|
+
for (const entry of this._media) {
|
|
685
|
+
if (entry._watermarkTag) {
|
|
686
|
+
const dims = this._dimensions.model;
|
|
687
|
+
const maxCol = dims ? Math.max(dims.right ?? 100, 100) : 100;
|
|
688
|
+
const maxRow = dims ? Math.max(dims.bottom ?? 200, 200) : 200;
|
|
689
|
+
entry.range.br = {
|
|
690
|
+
nativeCol: maxCol,
|
|
691
|
+
nativeColOff: 0,
|
|
692
|
+
nativeRow: maxRow,
|
|
693
|
+
nativeRowOff: 0
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}
|
|
629
697
|
// Build the drawing model from the stored images.
|
|
630
698
|
// The drawing XML will be generated later by WorkbookWriterBase.addDrawings().
|
|
631
699
|
const drawingName = `drawing${this.id}`;
|
|
@@ -64,6 +64,11 @@ export function buildDrawingAnchorsAndRels(media, existingRels, options) {
|
|
|
64
64
|
},
|
|
65
65
|
range: medium.range
|
|
66
66
|
};
|
|
67
|
+
// Pass through watermark opacity as alphaModFix
|
|
68
|
+
if (medium.opacity !== undefined) {
|
|
69
|
+
const clamped = Math.max(0, Math.min(1, medium.opacity));
|
|
70
|
+
anchor.picture.alphaModFix = Math.round(clamped * 100000);
|
|
71
|
+
}
|
|
67
72
|
// Handle image hyperlinks
|
|
68
73
|
if (medium.hyperlinks && medium.hyperlinks.hyperlink) {
|
|
69
74
|
const rIdHyperlink = options.nextRId(rels);
|
|
@@ -17,6 +17,7 @@ const mediaFilenameRegex = /^xl\/media\/([a-zA-Z0-9]+[.][a-zA-Z0-9]{3,4})$/;
|
|
|
17
17
|
const drawingXmlRegex = /^xl\/drawings\/(drawing\d+)[.]xml$/;
|
|
18
18
|
const drawingRelsXmlRegex = /^xl\/drawings\/_rels\/(drawing\d+)[.]xml[.]rels$/;
|
|
19
19
|
const vmlDrawingRegex = /^xl\/drawings\/(vmlDrawing\d+)[.]vml$/;
|
|
20
|
+
const vmlDrawingHFRegex = /^xl\/drawings\/(vmlDrawingHF\d+)[.]vml$/;
|
|
20
21
|
const commentsXmlRegex = /^xl\/comments(\d+)[.]xml$/;
|
|
21
22
|
const tableXmlRegex = /^xl\/tables\/(table\d+)[.]xml$/;
|
|
22
23
|
const pivotTableXmlRegex = /^xl\/pivotTables\/(pivotTable\d+)[.]xml$/;
|
|
@@ -72,6 +73,10 @@ export function getVmlDrawingNameFromPath(path) {
|
|
|
72
73
|
const match = vmlDrawingRegex.exec(path);
|
|
73
74
|
return match ? match[1] : undefined;
|
|
74
75
|
}
|
|
76
|
+
export function getVmlDrawingHFNameFromPath(path) {
|
|
77
|
+
const match = vmlDrawingHFRegex.exec(path);
|
|
78
|
+
return match ? match[1] : undefined;
|
|
79
|
+
}
|
|
75
80
|
export function getCommentsIndexFromPath(path) {
|
|
76
81
|
const match = commentsXmlRegex.exec(path);
|
|
77
82
|
return match ? match[1] : undefined;
|
|
@@ -129,6 +134,12 @@ export function commentsPathFromName(commentName) {
|
|
|
129
134
|
export function vmlDrawingPath(sheetId) {
|
|
130
135
|
return `xl/drawings/vmlDrawing${sheetId}.vml`;
|
|
131
136
|
}
|
|
137
|
+
export function vmlDrawingHFPath(sheetId) {
|
|
138
|
+
return `xl/drawings/vmlDrawingHF${sheetId}.vml`;
|
|
139
|
+
}
|
|
140
|
+
export function vmlDrawingHFRelsPath(sheetId) {
|
|
141
|
+
return `xl/drawings/_rels/vmlDrawingHF${sheetId}.vml.rels`;
|
|
142
|
+
}
|
|
132
143
|
export function tablePath(target) {
|
|
133
144
|
return `xl/tables/${target}`;
|
|
134
145
|
}
|
|
@@ -178,6 +189,10 @@ export function vmlDrawingRelTargetFromWorksheet(sheetId) {
|
|
|
178
189
|
// Target inside xl/worksheets/_rels/sheetN.xml.rels (base: xl/worksheets/)
|
|
179
190
|
return `../drawings/vmlDrawing${sheetId}.vml`;
|
|
180
191
|
}
|
|
192
|
+
export function vmlDrawingHFRelTargetFromWorksheet(sheetId) {
|
|
193
|
+
// Target inside xl/worksheets/_rels/sheetN.xml.rels (base: xl/worksheets/)
|
|
194
|
+
return `../drawings/vmlDrawingHF${sheetId}.vml`;
|
|
195
|
+
}
|
|
181
196
|
export function drawingRelTargetFromWorksheet(drawingName) {
|
|
182
197
|
// Target inside xl/worksheets/_rels/sheetN.xml.rels (base: xl/worksheets/)
|
|
183
198
|
return `../drawings/${drawingName}.xml`;
|