@cj-tech-master/excelts 9.5.9 → 9.6.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/modules/excel/stream/workbook-writer.browser.d.ts +17 -0
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +23 -1
- package/dist/browser/modules/excel/stream/workbook-writer.js +6 -0
- package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +6 -0
- package/dist/browser/modules/excel/stream/worksheet-writer.js +29 -3
- package/dist/browser/modules/excel/types.d.ts +17 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +46 -4
- package/dist/browser/modules/excel/utils/drawing-utils.js +64 -6
- package/dist/browser/modules/excel/workbook.browser.d.ts +38 -1
- package/dist/browser/modules/excel/workbook.browser.js +36 -1
- package/dist/browser/modules/excel/worksheet.d.ts +13 -1
- package/dist/browser/modules/excel/worksheet.js +35 -4
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +6 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.d.ts +6 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +32 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +5 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +14 -6
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +14 -10
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +7 -1
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +22 -0
- package/dist/cjs/modules/excel/stream/workbook-writer.js +6 -0
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +27 -1
- package/dist/cjs/modules/excel/utils/drawing-utils.js +67 -6
- package/dist/cjs/modules/excel/workbook.browser.js +36 -1
- package/dist/cjs/modules/excel/worksheet.js +34 -3
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +6 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +32 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +14 -6
- package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +13 -9
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +6 -0
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +23 -1
- package/dist/esm/modules/excel/stream/workbook-writer.js +6 -0
- package/dist/esm/modules/excel/stream/worksheet-writer.js +29 -3
- package/dist/esm/modules/excel/utils/drawing-utils.js +64 -6
- package/dist/esm/modules/excel/workbook.browser.js +36 -1
- package/dist/esm/modules/excel/worksheet.js +35 -4
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +6 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +32 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +14 -6
- package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +14 -10
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +7 -1
- package/dist/iife/excelts.iife.js +195 -26
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +35 -35
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +17 -0
- package/dist/types/modules/excel/stream/worksheet-writer.d.ts +6 -0
- package/dist/types/modules/excel/types.d.ts +17 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +46 -4
- package/dist/types/modules/excel/workbook.browser.d.ts +38 -1
- package/dist/types/modules/excel/worksheet.d.ts +13 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.d.ts +6 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +5 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +2 -0
- package/package.json +7 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @cj-tech-master/excelts v9.
|
|
2
|
+
* @cj-tech-master/excelts v9.6.0
|
|
3
3
|
* Zero-dependency TypeScript toolkit — Excel (XLSX), PDF, CSV, Markdown, XML, ZIP/TAR, and streaming.
|
|
4
4
|
* (c) 2026 cjnoname
|
|
5
5
|
* Released under the MIT License
|
|
@@ -26215,7 +26215,7 @@ self.onmessage = async function(event) {
|
|
|
26215
26215
|
}
|
|
26216
26216
|
write(chunk, callback) {
|
|
26217
26217
|
if (this.ended) {
|
|
26218
|
-
handleError(this, new Error(WRITE_AFTER_END_ERROR), callback);
|
|
26218
|
+
handleError(this, /* @__PURE__ */ new Error(WRITE_AFTER_END_ERROR), callback);
|
|
26219
26219
|
return false;
|
|
26220
26220
|
}
|
|
26221
26221
|
if (chunk.byteLength === 0) {
|
|
@@ -26270,7 +26270,7 @@ self.onmessage = async function(event) {
|
|
|
26270
26270
|
}
|
|
26271
26271
|
write(chunk, callback) {
|
|
26272
26272
|
if (this.ended) {
|
|
26273
|
-
handleError(this, new Error(WRITE_AFTER_END_ERROR), callback);
|
|
26273
|
+
handleError(this, /* @__PURE__ */ new Error(WRITE_AFTER_END_ERROR), callback);
|
|
26274
26274
|
return false;
|
|
26275
26275
|
}
|
|
26276
26276
|
if (chunk.byteLength === 0) {
|
|
@@ -28457,6 +28457,55 @@ self.onmessage = async function(event) {
|
|
|
28457
28457
|
return mediaRelTargetFromRels(medium.name && medium.extension && medium.name.endsWith(`.${medium.extension}`) ? medium.name : `${medium.name}.${medium.extension}`);
|
|
28458
28458
|
}
|
|
28459
28459
|
/**
|
|
28460
|
+
* Determine whether a media entry is an **external (linked) image** rather than
|
|
28461
|
+
* an embedded one. An external image carries a `link` target and supplies no
|
|
28462
|
+
* embedded bytes (`buffer`/`base64`/`filename`). Embedding always takes
|
|
28463
|
+
* precedence: if any byte source is present the image is embedded even if a
|
|
28464
|
+
* `link` was also provided.
|
|
28465
|
+
*/
|
|
28466
|
+
function isExternalImage(medium) {
|
|
28467
|
+
return !!medium.link && medium.buffer == null && medium.base64 == null && medium.filename == null;
|
|
28468
|
+
}
|
|
28469
|
+
/**
|
|
28470
|
+
* Best-effort image extension inference from an external link's path.
|
|
28471
|
+
*
|
|
28472
|
+
* Normalises to the extension vocabulary used by `ImageData`
|
|
28473
|
+
* (`"jpeg" | "png" | "gif"`); unknown extensions fall back to `"png"`.
|
|
28474
|
+
* The extension is advisory only for linked images — the relationship
|
|
28475
|
+
* Target carries the real reference — but keeping it within the documented
|
|
28476
|
+
* set avoids surprising consumers that branch on `medium.extension`.
|
|
28477
|
+
*/
|
|
28478
|
+
function inferExternalImageExtension(link) {
|
|
28479
|
+
const match = /\.([a-zA-Z0-9]{2,5})(?:[?#].*)?$/.exec(link);
|
|
28480
|
+
switch (match ? match[1].toLowerCase() : "") {
|
|
28481
|
+
case "jpg":
|
|
28482
|
+
case "jpeg": return "jpeg";
|
|
28483
|
+
case "gif": return "gif";
|
|
28484
|
+
default: return "png";
|
|
28485
|
+
}
|
|
28486
|
+
}
|
|
28487
|
+
/**
|
|
28488
|
+
* Build an image relationship for the given rId, choosing between an embedded
|
|
28489
|
+
* package target (`../media/imageN.ext`) and an external link target
|
|
28490
|
+
* (`TargetMode="External"`) based on whether the image is external.
|
|
28491
|
+
*
|
|
28492
|
+
* Shared by the drawing, background, and watermark write paths so the
|
|
28493
|
+
* embed-vs-link decision lives in exactly one place.
|
|
28494
|
+
*/
|
|
28495
|
+
function buildImageRel(rId, bookImage) {
|
|
28496
|
+
if (isExternalImage(bookImage)) return {
|
|
28497
|
+
Id: rId,
|
|
28498
|
+
Type: RelType.Image,
|
|
28499
|
+
Target: bookImage.link,
|
|
28500
|
+
TargetMode: "External"
|
|
28501
|
+
};
|
|
28502
|
+
return {
|
|
28503
|
+
Id: rId,
|
|
28504
|
+
Type: RelType.Image,
|
|
28505
|
+
Target: resolveMediaTarget(bookImage)
|
|
28506
|
+
};
|
|
28507
|
+
}
|
|
28508
|
+
/**
|
|
28460
28509
|
* Build the drawing anchors and relationships from a list of image media entries.
|
|
28461
28510
|
*
|
|
28462
28511
|
* This is the core logic shared between:
|
|
@@ -28474,18 +28523,18 @@ self.onmessage = async function(event) {
|
|
|
28474
28523
|
const imageId = String(medium.imageId);
|
|
28475
28524
|
const bookImage = options.getBookImage(medium.imageId);
|
|
28476
28525
|
if (!bookImage) continue;
|
|
28526
|
+
const isExternal = isExternalImage(bookImage);
|
|
28477
28527
|
let rIdImage = imageRIdMap[imageId];
|
|
28478
28528
|
if (!rIdImage) {
|
|
28479
28529
|
rIdImage = options.nextRId(rels);
|
|
28480
28530
|
imageRIdMap[imageId] = rIdImage;
|
|
28481
|
-
rels.push(
|
|
28482
|
-
Id: rIdImage,
|
|
28483
|
-
Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
28484
|
-
Target: resolveMediaTarget(bookImage)
|
|
28485
|
-
});
|
|
28531
|
+
rels.push(buildImageRel(rIdImage, bookImage));
|
|
28486
28532
|
}
|
|
28487
28533
|
const anchor = {
|
|
28488
|
-
picture: {
|
|
28534
|
+
picture: {
|
|
28535
|
+
rId: rIdImage,
|
|
28536
|
+
...isExternal ? { external: true } : {}
|
|
28537
|
+
},
|
|
28489
28538
|
range: medium.range
|
|
28490
28539
|
};
|
|
28491
28540
|
if (medium.opacity !== void 0) {
|
|
@@ -32181,6 +32230,8 @@ self.onmessage = async function(event) {
|
|
|
32181
32230
|
else this.conditionalFormatting = [];
|
|
32182
32231
|
}
|
|
32183
32232
|
addBackgroundImage(imageId) {
|
|
32233
|
+
const bookImage = this._workbook.getImage(Number(imageId));
|
|
32234
|
+
if (bookImage && isExternalImage(bookImage)) throw new ImageError("Background images cannot be external (linked) images. Use an embedded image (buffer/base64/filename). External images are only supported for cell pictures and overlay watermarks.");
|
|
32184
32235
|
this._background = { imageId: Number(imageId) };
|
|
32185
32236
|
}
|
|
32186
32237
|
getBackgroundImageId() {
|
|
@@ -32204,13 +32255,24 @@ self.onmessage = async function(event) {
|
|
|
32204
32255
|
/**
|
|
32205
32256
|
* Add a watermark to the worksheet using an image from `WorkbookWriter.addImage()`.
|
|
32206
32257
|
* Supports overlay mode (DrawingML with transparency) and header mode (VML behind content).
|
|
32258
|
+
*
|
|
32259
|
+
* `mode: "overlay"` supports external (linked) images; `mode: "header"` does
|
|
32260
|
+
* not — VML header/footer images require embedded media, so a linked image
|
|
32261
|
+
* with `mode: "header"` throws an `ImageError`.
|
|
32262
|
+
*
|
|
32263
|
+
* @throws {ImageError} If `mode: "header"` is used with an external (linked) image.
|
|
32207
32264
|
*/
|
|
32208
32265
|
addWatermark(options) {
|
|
32266
|
+
const mode = options.mode ?? "overlay";
|
|
32267
|
+
if (mode === "header") {
|
|
32268
|
+
const bookImage = this._workbook.getImage(Number(options.imageId));
|
|
32269
|
+
if (bookImage && isExternalImage(bookImage)) throw new ImageError("Header watermark images cannot be external (linked) images. Use an embedded image (buffer/base64/filename), or use overlay mode for linked images.");
|
|
32270
|
+
}
|
|
32209
32271
|
this._media = this._media.filter((m) => m._watermarkTag !== true);
|
|
32210
32272
|
const opacity = options.opacity !== void 0 ? Math.max(0, Math.min(1, options.opacity)) : .15;
|
|
32211
32273
|
this._watermark = {
|
|
32212
32274
|
imageId: String(options.imageId),
|
|
32213
|
-
mode
|
|
32275
|
+
mode,
|
|
32214
32276
|
opacity,
|
|
32215
32277
|
headerWidth: options.headerWidth,
|
|
32216
32278
|
headerHeight: options.headerHeight,
|
|
@@ -33110,6 +33172,7 @@ self.onmessage = async function(event) {
|
|
|
33110
33172
|
//#region src/modules/excel/xlsx/xform/core/content-types-xform.ts
|
|
33111
33173
|
var ContentTypesXform;
|
|
33112
33174
|
var init_content_types_xform = __esmMin((() => {
|
|
33175
|
+
init_drawing_utils();
|
|
33113
33176
|
init_ooxml_paths();
|
|
33114
33177
|
init_base_xform();
|
|
33115
33178
|
init_writer();
|
|
@@ -33120,6 +33183,7 @@ self.onmessage = async function(event) {
|
|
|
33120
33183
|
const mediaHash = {};
|
|
33121
33184
|
(model.media ?? []).forEach((medium) => {
|
|
33122
33185
|
if (medium.type === "image") {
|
|
33186
|
+
if (isExternalImage(medium)) return;
|
|
33123
33187
|
const imageType = medium.extension;
|
|
33124
33188
|
if (!mediaHash[imageType]) {
|
|
33125
33189
|
mediaHash[imageType] = true;
|
|
@@ -33525,6 +33589,7 @@ self.onmessage = async function(event) {
|
|
|
33525
33589
|
//#region src/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.ts
|
|
33526
33590
|
var BaseCellAnchorXform;
|
|
33527
33591
|
var init_base_cell_anchor_xform = __esmMin((() => {
|
|
33592
|
+
init_drawing_utils();
|
|
33528
33593
|
init_base_xform();
|
|
33529
33594
|
BaseCellAnchorXform = class extends BaseXform {
|
|
33530
33595
|
parseOpen(node) {
|
|
@@ -33551,6 +33616,7 @@ self.onmessage = async function(event) {
|
|
|
33551
33616
|
if (model && model.rId) {
|
|
33552
33617
|
const rel = options.rels[model.rId];
|
|
33553
33618
|
if (!rel) return;
|
|
33619
|
+
if (rel.TargetMode === "External" || model.external) return this.reconcileExternalPicture(rel.Target, options);
|
|
33554
33620
|
const match = rel.Target.match(/.*\/media\/(.+[.][a-zA-Z]{3,4})/);
|
|
33555
33621
|
if (match) {
|
|
33556
33622
|
const name = match[1];
|
|
@@ -33564,6 +33630,28 @@ self.onmessage = async function(event) {
|
|
|
33564
33630
|
}
|
|
33565
33631
|
}
|
|
33566
33632
|
}
|
|
33633
|
+
/**
|
|
33634
|
+
* Resolve (or create) the media entry for an external linked image. The
|
|
33635
|
+
* synthesized entry is appended to `options.media` and indexed by its link
|
|
33636
|
+
* so repeated references to the same external image share one entry.
|
|
33637
|
+
*/
|
|
33638
|
+
reconcileExternalPicture(link, options) {
|
|
33639
|
+
if (!link) return;
|
|
33640
|
+
const indexKey = `external:${link}`;
|
|
33641
|
+
let mediaId = options.mediaIndex[indexKey];
|
|
33642
|
+
if (mediaId === void 0) {
|
|
33643
|
+
mediaId = options.media.length;
|
|
33644
|
+
const medium = {
|
|
33645
|
+
type: "image",
|
|
33646
|
+
extension: inferExternalImageExtension(link),
|
|
33647
|
+
link,
|
|
33648
|
+
index: mediaId
|
|
33649
|
+
};
|
|
33650
|
+
options.media.push(medium);
|
|
33651
|
+
options.mediaIndex[indexKey] = mediaId;
|
|
33652
|
+
}
|
|
33653
|
+
return options.media[mediaId];
|
|
33654
|
+
}
|
|
33567
33655
|
};
|
|
33568
33656
|
}));
|
|
33569
33657
|
//#endregion
|
|
@@ -33785,25 +33873,32 @@ self.onmessage = async function(event) {
|
|
|
33785
33873
|
return "a:blip";
|
|
33786
33874
|
}
|
|
33787
33875
|
render(xmlStream, model) {
|
|
33876
|
+
const relAttr = model.external ? "r:link" : "r:embed";
|
|
33788
33877
|
if (model.alphaModFix !== void 0 && model.alphaModFix < 1e5) {
|
|
33789
33878
|
xmlStream.openNode(this.tag, {
|
|
33790
33879
|
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
33791
|
-
|
|
33880
|
+
[relAttr]: model.rId,
|
|
33792
33881
|
cstate: "print"
|
|
33793
33882
|
});
|
|
33794
33883
|
xmlStream.leafNode("a:alphaModFix", { amt: String(model.alphaModFix) });
|
|
33795
33884
|
xmlStream.closeNode();
|
|
33796
33885
|
} else xmlStream.leafNode(this.tag, {
|
|
33797
33886
|
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
33798
|
-
|
|
33887
|
+
[relAttr]: model.rId,
|
|
33799
33888
|
cstate: "print"
|
|
33800
33889
|
});
|
|
33801
33890
|
}
|
|
33802
33891
|
parseOpen(node) {
|
|
33803
33892
|
switch (node.name) {
|
|
33804
|
-
case this.tag:
|
|
33805
|
-
|
|
33893
|
+
case this.tag: {
|
|
33894
|
+
const link = node.attributes["r:link"];
|
|
33895
|
+
if (link !== void 0) this.model = {
|
|
33896
|
+
rId: link,
|
|
33897
|
+
external: true
|
|
33898
|
+
};
|
|
33899
|
+
else this.model = { rId: node.attributes["r:embed"] };
|
|
33806
33900
|
return true;
|
|
33901
|
+
}
|
|
33807
33902
|
case "a:alphaModFix":
|
|
33808
33903
|
if (node.attributes.amt) this.model.alphaModFix = parseInt(node.attributes.amt, 10);
|
|
33809
33904
|
return true;
|
|
@@ -34143,7 +34238,8 @@ self.onmessage = async function(event) {
|
|
|
34143
34238
|
this.map["xdr:nvPicPr"].render(xmlStream, model);
|
|
34144
34239
|
this.map["xdr:blipFill"].render(xmlStream, {
|
|
34145
34240
|
rId: model.rId,
|
|
34146
|
-
alphaModFix: model.alphaModFix
|
|
34241
|
+
alphaModFix: model.alphaModFix,
|
|
34242
|
+
external: model.external
|
|
34147
34243
|
});
|
|
34148
34244
|
this.map["xdr:spPr"].render(xmlStream, model);
|
|
34149
34245
|
xmlStream.closeNode();
|
|
@@ -35435,6 +35531,23 @@ self.onmessage = async function(event) {
|
|
|
35435
35531
|
for (let i = 1; i < this._worksheets.length; i++) if (!this._worksheets[i]) return i;
|
|
35436
35532
|
return this._worksheets.length || 1;
|
|
35437
35533
|
}
|
|
35534
|
+
/**
|
|
35535
|
+
* Register an image with the workbook and return its numeric id.
|
|
35536
|
+
*
|
|
35537
|
+
* Supply `buffer`/`base64`/`filename` to **embed** the bytes, or only `link`
|
|
35538
|
+
* (a URL or local file path) to reference it **externally** — in which case
|
|
35539
|
+
* no bytes are written into the package and the relationship is emitted with
|
|
35540
|
+
* `TargetMode="External"`. If both are provided, embedding wins.
|
|
35541
|
+
*
|
|
35542
|
+
* Linked images work with cell pictures and overlay watermarks; worksheet
|
|
35543
|
+
* background images and header/footer (VML) watermarks cannot be linked.
|
|
35544
|
+
*
|
|
35545
|
+
* @example
|
|
35546
|
+
* ```typescript
|
|
35547
|
+
* const id = wb.addImage({ extension: "png", link: "https://example.com/logo.png" });
|
|
35548
|
+
* ws.addImage(id, "B2:D6");
|
|
35549
|
+
* ```
|
|
35550
|
+
*/
|
|
35438
35551
|
addImage(image) {
|
|
35439
35552
|
const id = this.media.length;
|
|
35440
35553
|
const medium = {
|
|
@@ -35558,6 +35671,7 @@ self.onmessage = async function(event) {
|
|
|
35558
35671
|
addMedia() {
|
|
35559
35672
|
return Promise.all(this.media.map(async (medium) => {
|
|
35560
35673
|
if (medium.type === "image") {
|
|
35674
|
+
if (isExternalImage(medium)) return;
|
|
35561
35675
|
const filename = mediaPath(medium.name);
|
|
35562
35676
|
if (medium.buffer) {
|
|
35563
35677
|
this._addFile(medium.buffer, filename);
|
|
@@ -41344,6 +41458,7 @@ self.onmessage = async function(event) {
|
|
|
41344
41458
|
init_cell_format();
|
|
41345
41459
|
init_col_cache();
|
|
41346
41460
|
init_copy_style();
|
|
41461
|
+
init_drawing_utils();
|
|
41347
41462
|
init_merge_borders();
|
|
41348
41463
|
init_sheet_protection();
|
|
41349
41464
|
init_text_metrics();
|
|
@@ -42320,9 +42435,16 @@ self.onmessage = async function(event) {
|
|
|
42320
42435
|
return true;
|
|
42321
42436
|
}
|
|
42322
42437
|
/**
|
|
42323
|
-
* Using the image id from `Workbook.addImage`, set the background to the worksheet
|
|
42438
|
+
* Using the image id from `Workbook.addImage`, set the background to the worksheet.
|
|
42439
|
+
*
|
|
42440
|
+
* The image must be **embedded** (`buffer`/`base64`/`filename`). Worksheet
|
|
42441
|
+
* background pictures (`<picture r:id>`) do not support external (linked)
|
|
42442
|
+
* images — Excel silently drops a background whose relationship uses
|
|
42443
|
+
* `TargetMode="External"`, so this rejects linked images up front.
|
|
42324
42444
|
*/
|
|
42325
42445
|
addBackgroundImage(imageId) {
|
|
42446
|
+
const bookImage = this._workbook.getImage(imageId);
|
|
42447
|
+
if (bookImage && isExternalImage(bookImage)) throw new ImageError("Background images cannot be external (linked) images. Use an embedded image (buffer/base64/filename). External images are only supported for cell pictures and overlay watermarks.");
|
|
42326
42448
|
const model = {
|
|
42327
42449
|
type: "background",
|
|
42328
42450
|
imageId: String(imageId)
|
|
@@ -42345,7 +42467,14 @@ self.onmessage = async function(event) {
|
|
|
42345
42467
|
* Visible in Page Layout view and when printed. Renders behind cell content.
|
|
42346
42468
|
* Transparency must be baked into the image (PNG with alpha channel).
|
|
42347
42469
|
*
|
|
42470
|
+
* **External (linked) images:** `mode: "overlay"` supports external images
|
|
42471
|
+
* (registered via `workbook.addImage({ link })`). `mode: "header"` does
|
|
42472
|
+
* **not** — VML header/footer images require embedded media, so passing a
|
|
42473
|
+
* linked image with `mode: "header"` throws an `ImageError`. Use an embedded
|
|
42474
|
+
* image (`buffer`/`base64`/`filename`) or switch to `mode: "overlay"`.
|
|
42475
|
+
*
|
|
42348
42476
|
* @param options - Watermark configuration
|
|
42477
|
+
* @throws {ImageError} If `mode: "header"` is used with an external (linked) image.
|
|
42349
42478
|
*
|
|
42350
42479
|
* @example Overlay watermark with transparency:
|
|
42351
42480
|
* ```typescript
|
|
@@ -42360,10 +42489,15 @@ self.onmessage = async function(event) {
|
|
|
42360
42489
|
* ```
|
|
42361
42490
|
*/
|
|
42362
42491
|
addWatermark(options) {
|
|
42492
|
+
const mode = options.mode ?? "overlay";
|
|
42493
|
+
if (mode === "header") {
|
|
42494
|
+
const bookImage = this._workbook.getImage(options.imageId);
|
|
42495
|
+
if (bookImage && isExternalImage(bookImage)) throw new ImageError("Header watermark images cannot be external (linked) images. Use an embedded image (buffer/base64/filename), or use overlay mode for linked images.");
|
|
42496
|
+
}
|
|
42363
42497
|
this._media = this._media.filter((m) => m.type !== "watermark" && m.type !== "headerImage");
|
|
42364
42498
|
this._watermark = {
|
|
42365
42499
|
imageId: String(options.imageId),
|
|
42366
|
-
mode
|
|
42500
|
+
mode,
|
|
42367
42501
|
opacity: options.opacity,
|
|
42368
42502
|
headerWidth: options.headerWidth,
|
|
42369
42503
|
headerHeight: options.headerHeight,
|
|
@@ -51104,15 +51238,16 @@ self.onmessage = async function(event) {
|
|
|
51104
51238
|
else if (medium.type === "headerImage") headerImageMedia.push(medium);
|
|
51105
51239
|
});
|
|
51106
51240
|
backgroundMedia.forEach((medium) => {
|
|
51107
|
-
const rId = nextRid(rels);
|
|
51108
51241
|
const bookImage = options.media[medium.imageId];
|
|
51242
|
+
if (!bookImage) return;
|
|
51243
|
+
const rId = nextRid(rels);
|
|
51109
51244
|
rels.push({
|
|
51110
51245
|
Id: rId,
|
|
51111
51246
|
Type: RelType.Image,
|
|
51112
51247
|
Target: resolveMediaTarget(bookImage)
|
|
51113
51248
|
});
|
|
51114
51249
|
model.background = { rId };
|
|
51115
|
-
model.image =
|
|
51250
|
+
model.image = bookImage;
|
|
51116
51251
|
});
|
|
51117
51252
|
if (imageMedia.length > 0) {
|
|
51118
51253
|
let { drawing } = model;
|
|
@@ -51156,12 +51291,9 @@ self.onmessage = async function(event) {
|
|
|
51156
51291
|
for (const medium of watermarkMedia) {
|
|
51157
51292
|
const bookImage = options.media[medium.imageId];
|
|
51158
51293
|
if (!bookImage) continue;
|
|
51294
|
+
const isExternal = isExternalImage(bookImage);
|
|
51159
51295
|
const rIdImage = nextRid(drawing.rels);
|
|
51160
|
-
drawing.rels.push(
|
|
51161
|
-
Id: rIdImage,
|
|
51162
|
-
Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
51163
|
-
Target: resolveMediaTarget(bookImage)
|
|
51164
|
-
});
|
|
51296
|
+
drawing.rels.push(buildImageRel(rIdImage, bookImage));
|
|
51165
51297
|
const rawOpacity = medium.opacity !== void 0 ? medium.opacity : .15;
|
|
51166
51298
|
const alphaModFix = Math.round(Math.max(0, Math.min(1, rawOpacity)) * 1e5);
|
|
51167
51299
|
const dims = model.dimensions;
|
|
@@ -51170,7 +51302,8 @@ self.onmessage = async function(event) {
|
|
|
51170
51302
|
drawing.anchors.push({
|
|
51171
51303
|
picture: {
|
|
51172
51304
|
rId: rIdImage,
|
|
51173
|
-
alphaModFix
|
|
51305
|
+
alphaModFix,
|
|
51306
|
+
...isExternal ? { external: true } : {}
|
|
51174
51307
|
},
|
|
51175
51308
|
range: {
|
|
51176
51309
|
editAs: "absolute",
|
|
@@ -55095,6 +55228,7 @@ self.onmessage = async function(event) {
|
|
|
55095
55228
|
async addMedia(zip, model) {
|
|
55096
55229
|
await Promise.all(model.media.map(async (medium) => {
|
|
55097
55230
|
if (medium.type !== "image") throw new ImageError("Unsupported media");
|
|
55231
|
+
if (isExternalImage(medium)) return;
|
|
55098
55232
|
const filename = mediaPath(`${medium.name ?? "undefined"}.${medium.extension}`);
|
|
55099
55233
|
if (medium.filename) {
|
|
55100
55234
|
if (this.readFileAsync) {
|
|
@@ -58243,7 +58377,42 @@ self.onmessage = async function(event) {
|
|
|
58243
58377
|
this._themes = void 0;
|
|
58244
58378
|
}
|
|
58245
58379
|
/**
|
|
58246
|
-
*
|
|
58380
|
+
* Register an image with the workbook and return its numeric id. Pass the id
|
|
58381
|
+
* to {@link Worksheet.addImage}, {@link Worksheet.addBackgroundImage}, or
|
|
58382
|
+
* {@link Worksheet.addWatermark} to place it.
|
|
58383
|
+
*
|
|
58384
|
+
* The image is either **embedded** or **linked (external)**:
|
|
58385
|
+
*
|
|
58386
|
+
* - **Embedded** — supply `buffer`, `base64`, or `filename`. The bytes are
|
|
58387
|
+
* written into the `.xlsx` package (`xl/media/imageN.ext`). Self-contained,
|
|
58388
|
+
* but inflates file size.
|
|
58389
|
+
* - **Linked (external)** — supply only `link` (a URL or local file path).
|
|
58390
|
+
* No bytes are stored; the package keeps a relationship with
|
|
58391
|
+
* `TargetMode="External"` and the picture is rendered via `<a:blip r:link>`.
|
|
58392
|
+
* Keeps the file small, but the image is resolved by Excel at open time.
|
|
58393
|
+
*
|
|
58394
|
+
* If both bytes and a `link` are provided, **embedding wins**.
|
|
58395
|
+
*
|
|
58396
|
+
* Linked images work with **cell pictures** ({@link Worksheet.addImage}) and
|
|
58397
|
+
* **overlay watermarks** ({@link Worksheet.addWatermark} with `mode:
|
|
58398
|
+
* "overlay"`). Worksheet background images and header/footer (VML) watermarks
|
|
58399
|
+
* cannot be linked — they require an embedded image.
|
|
58400
|
+
*
|
|
58401
|
+
* Note: Excel treats linked images as volatile — a moved/missing target
|
|
58402
|
+
* shows a broken-image placeholder, and modern Excel may not auto-load
|
|
58403
|
+
* remote URLs for security reasons. Prefer embedding for self-contained files.
|
|
58404
|
+
*
|
|
58405
|
+
* @example Embedded image
|
|
58406
|
+
* ```typescript
|
|
58407
|
+
* const id = workbook.addImage({ buffer: pngBytes, extension: "png" });
|
|
58408
|
+
* worksheet.addImage(id, "B2:D6");
|
|
58409
|
+
* ```
|
|
58410
|
+
*
|
|
58411
|
+
* @example Linked (external) image — no bytes stored
|
|
58412
|
+
* ```typescript
|
|
58413
|
+
* const id = workbook.addImage({ extension: "png", link: "https://example.com/logo.png" });
|
|
58414
|
+
* worksheet.addImage(id, "B2:D6");
|
|
58415
|
+
* ```
|
|
58247
58416
|
*/
|
|
58248
58417
|
addImage(image) {
|
|
58249
58418
|
const id = this.media.length;
|