@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.
Files changed (60) hide show
  1. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +17 -0
  2. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +23 -1
  3. package/dist/browser/modules/excel/stream/workbook-writer.js +6 -0
  4. package/dist/browser/modules/excel/stream/worksheet-writer.d.ts +6 -0
  5. package/dist/browser/modules/excel/stream/worksheet-writer.js +29 -3
  6. package/dist/browser/modules/excel/types.d.ts +17 -0
  7. package/dist/browser/modules/excel/utils/drawing-utils.d.ts +46 -4
  8. package/dist/browser/modules/excel/utils/drawing-utils.js +64 -6
  9. package/dist/browser/modules/excel/workbook.browser.d.ts +38 -1
  10. package/dist/browser/modules/excel/workbook.browser.js +36 -1
  11. package/dist/browser/modules/excel/worksheet.d.ts +13 -1
  12. package/dist/browser/modules/excel/worksheet.js +35 -4
  13. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +6 -0
  14. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.d.ts +6 -0
  15. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +32 -0
  16. package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +5 -0
  17. package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +14 -6
  18. package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
  19. package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  20. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +14 -10
  21. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +2 -0
  22. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +7 -1
  23. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +22 -0
  24. package/dist/cjs/modules/excel/stream/workbook-writer.js +6 -0
  25. package/dist/cjs/modules/excel/stream/worksheet-writer.js +27 -1
  26. package/dist/cjs/modules/excel/utils/drawing-utils.js +67 -6
  27. package/dist/cjs/modules/excel/workbook.browser.js +36 -1
  28. package/dist/cjs/modules/excel/worksheet.js +34 -3
  29. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +6 -0
  30. package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +32 -0
  31. package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +14 -6
  32. package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  33. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +13 -9
  34. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +6 -0
  35. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +23 -1
  36. package/dist/esm/modules/excel/stream/workbook-writer.js +6 -0
  37. package/dist/esm/modules/excel/stream/worksheet-writer.js +29 -3
  38. package/dist/esm/modules/excel/utils/drawing-utils.js +64 -6
  39. package/dist/esm/modules/excel/workbook.browser.js +36 -1
  40. package/dist/esm/modules/excel/worksheet.js +35 -4
  41. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +6 -0
  42. package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +32 -0
  43. package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +14 -6
  44. package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  45. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +14 -10
  46. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +7 -1
  47. package/dist/iife/excelts.iife.js +195 -26
  48. package/dist/iife/excelts.iife.js.map +1 -1
  49. package/dist/iife/excelts.iife.min.js +35 -35
  50. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +17 -0
  51. package/dist/types/modules/excel/stream/worksheet-writer.d.ts +6 -0
  52. package/dist/types/modules/excel/types.d.ts +17 -0
  53. package/dist/types/modules/excel/utils/drawing-utils.d.ts +46 -4
  54. package/dist/types/modules/excel/workbook.browser.d.ts +38 -1
  55. package/dist/types/modules/excel/worksheet.d.ts +13 -1
  56. package/dist/types/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.d.ts +6 -0
  57. package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +5 -0
  58. package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
  59. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +2 -0
  60. package/package.json +7 -7
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @cj-tech-master/excelts v9.5.9
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: { rId: rIdImage },
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: options.mode ?? "overlay",
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
- "r:embed": model.rId,
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
- "r:embed": model.rId,
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
- this.model = { rId: node.attributes["r:embed"] };
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: options.mode ?? "overlay",
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 = options.media[medium.imageId];
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
- * Add Image to Workbook and return the id
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;