@json-to-office/core-docx 7.6.0 → 7.7.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.
@@ -3,9 +3,9 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
3
3
  var __esm = (fn, res) => function __init() {
4
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
5
  };
6
- var __export = (target, all) => {
6
+ var __export = (target2, all) => {
7
7
  for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
8
+ __defProp(target2, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
11
  // src/themes/defaults.ts
@@ -1666,6 +1666,46 @@ var init_themes = __esm({
1666
1666
  }
1667
1667
  });
1668
1668
 
1669
+ // src/utils/generationContext.ts
1670
+ import { AsyncLocalStorage } from "async_hooks";
1671
+ import { isAbsolute, resolve } from "path";
1672
+ function runWithBaseDir(baseDir, callback) {
1673
+ return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
1674
+ }
1675
+ function getBaseDir() {
1676
+ return baseDirStorage.getStore();
1677
+ }
1678
+ function resolveFromBaseDir(filePath) {
1679
+ const base = baseDirStorage.getStore();
1680
+ if (!base || isAbsolute(filePath)) return filePath;
1681
+ return resolve(base, filePath);
1682
+ }
1683
+ function runWithWarnings(warnings, callback) {
1684
+ return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
1685
+ }
1686
+ function reportWarning(component, code, message, context) {
1687
+ const warnings = warningsStorage.getStore();
1688
+ if (warnings) {
1689
+ warnings.push({
1690
+ component,
1691
+ message,
1692
+ severity: "warning",
1693
+ context: { code, ...context }
1694
+ });
1695
+ return;
1696
+ }
1697
+ console.warn(`[json-to-docx] [${code}] ${message}`);
1698
+ }
1699
+ var generationDateStorage, baseDirStorage, warningsStorage;
1700
+ var init_generationContext = __esm({
1701
+ "src/utils/generationContext.ts"() {
1702
+ "use strict";
1703
+ generationDateStorage = new AsyncLocalStorage();
1704
+ baseDirStorage = new AsyncLocalStorage();
1705
+ warningsStorage = new AsyncLocalStorage();
1706
+ }
1707
+ });
1708
+
1669
1709
  // src/styles/utils/colorUtils.ts
1670
1710
  import {
1671
1711
  DEFAULT_CHART_THEME_COLORS,
@@ -2419,43 +2459,6 @@ var init_widthUtils = __esm({
2419
2459
  }
2420
2460
  });
2421
2461
 
2422
- // src/utils/generationContext.ts
2423
- import { AsyncLocalStorage } from "async_hooks";
2424
- import { isAbsolute, resolve } from "path";
2425
- function runWithBaseDir(baseDir, callback) {
2426
- return baseDir === void 0 ? callback() : baseDirStorage.run(resolve(baseDir), callback);
2427
- }
2428
- function resolveFromBaseDir(filePath) {
2429
- const base = baseDirStorage.getStore();
2430
- if (!base || isAbsolute(filePath)) return filePath;
2431
- return resolve(base, filePath);
2432
- }
2433
- function runWithWarnings(warnings, callback) {
2434
- return warnings === void 0 ? callback() : warningsStorage.run(warnings, callback);
2435
- }
2436
- function reportWarning(component, code, message, context) {
2437
- const warnings = warningsStorage.getStore();
2438
- if (warnings) {
2439
- warnings.push({
2440
- component,
2441
- message,
2442
- severity: "warning",
2443
- context: { code, ...context }
2444
- });
2445
- return;
2446
- }
2447
- console.warn(`[json-to-docx] [${code}] ${message}`);
2448
- }
2449
- var generationDateStorage, baseDirStorage, warningsStorage;
2450
- var init_generationContext = __esm({
2451
- "src/utils/generationContext.ts"() {
2452
- "use strict";
2453
- generationDateStorage = new AsyncLocalStorage();
2454
- baseDirStorage = new AsyncLocalStorage();
2455
- warningsStorage = new AsyncLocalStorage();
2456
- }
2457
- });
2458
-
2459
2462
  // src/utils/imageUtils.ts
2460
2463
  import { readFileSync } from "fs";
2461
2464
  import { availableParallelism } from "os";
@@ -4326,15 +4329,15 @@ function canonicalizeEmbeddedFontKeys(zip) {
4326
4329
  const targets = /* @__PURE__ */ new Map();
4327
4330
  for (const [relationship] of rels.matchAll(/<Relationship\b[^>]*>/g)) {
4328
4331
  const id = /\bId="([^"]+)"/.exec(relationship)?.[1];
4329
- const target = /\bTarget="([^"]+)"/.exec(relationship)?.[1];
4330
- if (id && target) targets.set(id, target);
4332
+ const target2 = /\bTarget="([^"]+)"/.exec(relationship)?.[1];
4333
+ if (id && target2) targets.set(id, target2);
4331
4334
  }
4332
4335
  const rekey = /* @__PURE__ */ new Map();
4333
4336
  const parts = [];
4334
4337
  for (const { id, key } of embeds) {
4335
- const target = targets.get(id);
4336
- if (!target) continue;
4337
- const name = target.startsWith("/") ? target.slice(1) : posix.normalize(posix.join("word", target));
4338
+ const target2 = targets.get(id);
4339
+ if (!target2) continue;
4340
+ const name = target2.startsWith("/") ? target2.slice(1) : posix.normalize(posix.join("word", target2));
4338
4341
  const part = zip.getEntry(name);
4339
4342
  if (!part) continue;
4340
4343
  const font = xorFontKey(part.getData(), key);
@@ -5355,7 +5358,7 @@ function chromeSlots(set, Part, resources) {
5355
5358
  const part = set[slot];
5356
5359
  if (part) {
5357
5360
  out[slot] = new Part({
5358
- children: emitOutsideBody(() => partChildren(part, resources))
5361
+ children: part.children.length > 0 ? emitOutsideBody(() => partChildren(part, resources)) : [new Paragraph2({ style: Part === Header ? "Header" : "Footer" })]
5359
5362
  });
5360
5363
  }
5361
5364
  }
@@ -6591,15 +6594,16 @@ function section(value, ctx, closesDocument = false) {
6591
6594
  grid: { type: "default", linePitch: 360, charSpace: 0 }
6592
6595
  },
6593
6596
  children: sectionChildren2(value, ctx, closesDocument),
6594
- ...value.headers ? { headers: headerFooterSet(value.headers, ctx) } : {},
6595
- ...value.footers ? { footers: headerFooterSet(value.footers, ctx) } : {}
6597
+ ...value.headers ? { headers: headerFooterSet(value.headers, "Header", ctx) } : {},
6598
+ ...value.footers ? { footers: headerFooterSet(value.footers, "Footer", ctx) } : {}
6596
6599
  };
6597
6600
  }
6598
- function headerFooterSet(set, ctx) {
6601
+ function headerFooterSet(set, style, ctx) {
6599
6602
  const out = {};
6600
6603
  for (const slot of ["default", "first", "even"]) {
6601
6604
  const part = set[slot];
6602
- if (part) out[slot] = part.children.map((child) => block(child, ctx));
6605
+ if (!part) continue;
6606
+ out[slot] = part.children.length > 0 ? part.children.map((child) => block(child, ctx)) : [{ paragraph: { style, children: [] } }];
6603
6607
  }
6604
6608
  return out;
6605
6609
  }
@@ -7340,6 +7344,7 @@ async function exportPluginSchema(customComponents, outputPath, options = {}) {
7340
7344
  import { assertRendererSupports } from "@json-to-office/shared/rendering";
7341
7345
 
7342
7346
  // src/core/fontResolution.ts
7347
+ init_generationContext();
7343
7348
  import {
7344
7349
  collectFontNamesFromDocx,
7345
7350
  validateFontReferences,
@@ -7393,8 +7398,14 @@ async function resolveDocumentFonts(document, theme, fonts, warnings, forceMater
7393
7398
  if (!fonts?.onResolved && !forceMaterialize) return [];
7394
7399
  const registry2 = new FontRegistry({
7395
7400
  // Spread keeps baseDir/googleFonts/mode/substitution intact for
7396
- // materializeSource; extraEntries carries the merged registry.
7397
- opts: { ...fonts, extraEntries: registryEntries },
7401
+ // materializeSource; extraEntries carries the merged registry. Without a
7402
+ // `fonts.baseDir` of its own, kind:"file" sources resolve against the
7403
+ // render's baseDir, as images do — not against process.cwd().
7404
+ opts: {
7405
+ ...fonts,
7406
+ baseDir: fonts?.baseDir ?? getBaseDir(),
7407
+ extraEntries: registryEntries
7408
+ },
7398
7409
  fileLoader: loadFileFontSource,
7399
7410
  variableLoader: fetchVariableFontSource,
7400
7411
  diskCache: fonts?.googleFonts?.cacheDir ? new FontDiskCache(fonts.googleFonts.cacheDir) : void 0
@@ -7702,11 +7713,11 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7702
7713
  );
7703
7714
  let fontsRejected = false;
7704
7715
  const requestFonts = () => fontsRejected || !options.fonts?.length ? void 0 : options.fonts;
7705
- const rasterizeOne = async (target) => {
7716
+ const rasterizeOne = async (target2) => {
7706
7717
  const fonts = requestFonts();
7707
7718
  const call = (withFonts) => rasterizeVisualSlide(
7708
- target.presentation,
7709
- target.dpi,
7719
+ target2.presentation,
7720
+ target2.dpi,
7710
7721
  void 0,
7711
7722
  serviceConfig,
7712
7723
  options.baseDir,
@@ -7730,8 +7741,8 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7730
7741
  const rasterizeIndividually = async (chunk) => {
7731
7742
  await Promise.all(
7732
7743
  chunk.map(
7733
- (target) => limit(async () => {
7734
- map.set(target.key, await rasterizeOne(target));
7744
+ (target2) => limit(async () => {
7745
+ map.set(target2.key, await rasterizeOne(target2));
7735
7746
  })
7736
7747
  )
7737
7748
  );
@@ -7740,9 +7751,9 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7740
7751
  for (const chunk of chunksOf(unique, MAX_RASTERIZE_BATCH_SLIDES)) {
7741
7752
  try {
7742
7753
  const response = await serviceConfig.renderBatch({
7743
- slides: chunk.map((target) => ({
7744
- presentation: target.presentation,
7745
- dpi: target.dpi
7754
+ slides: chunk.map((target2) => ({
7755
+ presentation: target2.presentation,
7756
+ dpi: target2.dpi
7746
7757
  })),
7747
7758
  ...options.baseDir !== void 0 && { baseDir: options.baseDir },
7748
7759
  ...options.fonts?.length ? { fonts: [...options.fonts] } : {}
@@ -7757,8 +7768,8 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7757
7768
  await rasterizeIndividually(chunk);
7758
7769
  } else {
7759
7770
  const message = toErrorMessage(error);
7760
- for (const target of chunk) {
7761
- map.set(target.key, { ok: false, error: message });
7771
+ for (const target2 of chunk) {
7772
+ map.set(target2.key, { ok: false, error: message });
7762
7773
  }
7763
7774
  }
7764
7775
  }
@@ -7781,9 +7792,9 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7781
7792
  url: serverUrl,
7782
7793
  path: "/rasterize/batch",
7783
7794
  body: {
7784
- slides: chunk.map((target) => ({
7785
- presentation: target.presentation,
7786
- dpi: target.dpi
7795
+ slides: chunk.map((target2) => ({
7796
+ presentation: target2.presentation,
7797
+ dpi: target2.dpi
7787
7798
  })),
7788
7799
  ...options.baseDir !== void 0 && { baseDir: options.baseDir },
7789
7800
  ...fonts?.length ? { fonts } : {}
@@ -7826,6 +7837,49 @@ async function prerasterizeVisuals(root, serviceConfig, options = {}) {
7826
7837
  return map;
7827
7838
  }
7828
7839
 
7840
+ // src/core/nodeOrigin.ts
7841
+ var NODE_ORIGIN = Symbol("json-to-office.nodeOrigin");
7842
+ function isComponentNode(value) {
7843
+ return typeof value.name === "string" && "props" in value;
7844
+ }
7845
+ function isPlainRecord(value) {
7846
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
7847
+ const proto = Object.getPrototypeOf(value);
7848
+ return proto === Object.prototype || proto === null;
7849
+ }
7850
+ function tagNodeOrigins(root) {
7851
+ const visit = (value, path3) => {
7852
+ if (Array.isArray(value)) {
7853
+ return value.map((item, index) => visit(item, join(path3, index)));
7854
+ }
7855
+ if (!isPlainRecord(value)) return value;
7856
+ const out = {};
7857
+ for (const [key, item] of Object.entries(value)) {
7858
+ out[key] = visit(item, join(path3, key));
7859
+ }
7860
+ if (path3 !== "" && isComponentNode(value)) {
7861
+ out[NODE_ORIGIN] = {
7862
+ standardPath: path3,
7863
+ kind: value.name
7864
+ };
7865
+ }
7866
+ return out;
7867
+ };
7868
+ return visit(root, "");
7869
+ }
7870
+ function join(path3, key) {
7871
+ return path3 === "" ? String(key) : `${path3}.${key}`;
7872
+ }
7873
+ function nodeOrigin(node) {
7874
+ if (!node || typeof node !== "object") return void 0;
7875
+ return node[NODE_ORIGIN];
7876
+ }
7877
+ function withNodeOrigin(original, next) {
7878
+ const origin = nodeOrigin(original);
7879
+ if (origin) next[NODE_ORIGIN] = origin;
7880
+ return next;
7881
+ }
7882
+
7829
7883
  // src/core/componentTransform.ts
7830
7884
  async function transformComponents(doc, replace) {
7831
7885
  return await walk(doc, replace);
@@ -7891,7 +7945,7 @@ function withNodeIdentity(original, replacement) {
7891
7945
  const out = { ...replacement };
7892
7946
  if (original.id !== void 0) out.id = original.id;
7893
7947
  if (original.enabled !== void 0) out.enabled = original.enabled;
7894
- return out;
7948
+ return withNodeOrigin(original, out);
7895
7949
  }
7896
7950
  function containsComponent(root, name) {
7897
7951
  const seen = /* @__PURE__ */ new WeakSet();
@@ -7965,15 +8019,7 @@ async function generateChart(config, servicesConfig, warnings, cache) {
7965
8019
  }
7966
8020
  const serverUrl = effectiveChartServerUrl(config, servicesConfig);
7967
8021
  assertExportServerAllowed(serverUrl, servicesConfig, warnings);
7968
- const requestBody = {
7969
- infile: config.options,
7970
- type: "png",
7971
- b64: true,
7972
- scale: config.scale,
7973
- // Forward resources verbatim only when present so the payload stays
7974
- // byte-identical to before for callers that omit it.
7975
- ...config.resources ? { resources: config.resources } : {}
7976
- };
8022
+ const requestBody = chartRequestBody(config);
7977
8023
  return sendChartRequest({
7978
8024
  cache,
7979
8025
  serverUrl,
@@ -8091,15 +8137,29 @@ function withChartFontFaces(config, theme, faces) {
8091
8137
  theme.fonts.heading.family
8092
8138
  ]);
8093
8139
  }
8094
- async function renderChartToImageProps(props, theme, servicesConfig, chartFonts, warnings, cache) {
8095
- const config = withChartFontFaces(
8140
+ function resolveChartConfig(props, theme, fonts) {
8141
+ return withChartFontFaces(
8096
8142
  withThemeTypography(
8097
8143
  withThemeColors(withThemePresets(props, theme), theme),
8098
8144
  theme
8099
8145
  ),
8100
8146
  theme,
8101
- chartFonts
8147
+ fonts
8102
8148
  );
8149
+ }
8150
+ function chartRequestBody(config) {
8151
+ return {
8152
+ infile: config.options,
8153
+ type: "png",
8154
+ b64: true,
8155
+ scale: config.scale,
8156
+ // Forward resources verbatim only when present so the payload stays
8157
+ // byte-identical to before for callers that omit it.
8158
+ ...config.resources ? { resources: config.resources } : {}
8159
+ };
8160
+ }
8161
+ async function renderChartToImageProps(props, theme, servicesConfig, chartFonts, warnings, cache) {
8162
+ const config = resolveChartConfig(props, theme, chartFonts);
8103
8163
  const chart = await generateChart(config, servicesConfig, warnings, cache);
8104
8164
  const hasConfigDimensions = config.width !== void 0 || config.height !== void 0;
8105
8165
  return {
@@ -10816,12 +10876,12 @@ function parseLinks(normalized, options) {
10816
10876
  continue;
10817
10877
  }
10818
10878
  const [, linkText] = match;
10819
- const target = decodeEscapes(match[2]);
10879
+ const target2 = decodeEscapes(match[2]);
10820
10880
  out.push({
10821
10881
  kind: "hyperlink",
10822
10882
  // A `#anchor` target names a bookmark in this document and needs no
10823
10883
  // relationship; anything else leaves the document.
10824
- target: target.startsWith("#") ? { kind: "bookmark", anchor: target.slice(1) } : { kind: "external", url: target },
10884
+ target: target2.startsWith("#") ? { kind: "bookmark", anchor: target2.slice(1) } : { kind: "external", url: target2 },
10825
10885
  children: parseDecorated(linkText, options)
10826
10886
  });
10827
10887
  }
@@ -11077,6 +11137,7 @@ function compileDocument(structure, layout2, warnings = [], images = /* @__PURE_
11077
11137
  numberedItems: outline.numberedItems,
11078
11138
  tocEntries: outline.entries,
11079
11139
  images,
11140
+ imageOrigins: /* @__PURE__ */ new WeakMap(),
11080
11141
  generatedAt: structure.metadata.date
11081
11142
  };
11082
11143
  ctx.features.require("paragraphs", "sections");
@@ -11130,9 +11191,19 @@ function compileDocument(structure, layout2, warnings = [], images = /* @__PURE_
11130
11191
  ir,
11131
11192
  required: ctx.features.list(),
11132
11193
  warnings: ctx.warnings,
11133
- unsupported: ctx.unsupported
11194
+ unsupported: ctx.unsupported,
11195
+ imageOrigins: ctx.imageOrigins
11134
11196
  };
11135
11197
  }
11198
+ function recordImageOrigin(ctx, component, images) {
11199
+ const origin = nodeOrigin(component);
11200
+ if (!origin) return;
11201
+ for (const image of images) {
11202
+ if (image.kind === "image" || image.kind === "picture") {
11203
+ ctx.imageOrigins.set(image, origin);
11204
+ }
11205
+ }
11206
+ }
11136
11207
  var AFTER_TABLE_TWIPS = 120;
11137
11208
  var BEFORE_TABLE_TWIPS = 240;
11138
11209
  var BODY_STYLE_IDS = /* @__PURE__ */ new Set([void 0, "Normal"]);
@@ -11460,6 +11531,7 @@ function compileChromeImage(component, scope) {
11460
11531
  heightEmu: pixelsToEmu(size.height),
11461
11532
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path3) } : {}
11462
11533
  };
11534
+ recordImageOrigin(ctx, component, [image]);
11463
11535
  return [
11464
11536
  {
11465
11537
  kind: "paragraph",
@@ -12959,6 +13031,7 @@ function compileVisual(component, scope) {
12959
13031
  nativeVisualDeps(ctx, path3)
12960
13032
  );
12961
13033
  if (!group) return [];
13034
+ recordImageOrigin(ctx, component, group.children);
12962
13035
  const size = visualPlacementPixels(props, group.canvas, {
12963
13036
  widthPx: Math.round(twipsToPixels(scope.measureTwips)),
12964
13037
  heightPx: Math.round(twipsToPixels(getAvailableHeightTwips(ctx.theme)))
@@ -13085,6 +13158,7 @@ function compileImage2(component, scope) {
13085
13158
  ...typeof props.alt === "string" && props.alt ? { altText: props.alt } : {},
13086
13159
  ...props.floating ? { floating: compileFloating(props.floating, ctx, path3) } : {}
13087
13160
  };
13161
+ recordImageOrigin(ctx, component, [image]);
13088
13162
  ctx.features.require("images", path3);
13089
13163
  if (props.floating) ctx.features.require("floating-images", path3);
13090
13164
  const spacing = {};
@@ -13849,6 +13923,7 @@ function cellVisual(component, scope) {
13849
13923
  nativeVisualDeps(ctx, path3)
13850
13924
  );
13851
13925
  if (!group) return [];
13926
+ recordImageOrigin(ctx, component, group.children);
13852
13927
  const size = visualPlacementPixels(props, group.canvas, {
13853
13928
  widthPx: CELL_DRAWING_REFERENCE.width,
13854
13929
  heightPx: CELL_DRAWING_REFERENCE.height
@@ -13894,14 +13969,14 @@ function cellImage(component, base, ctx) {
13894
13969
  const mediaType = detectImageType(source, loaded.contentType);
13895
13970
  if (mediaType === "svg") ctx.features.require("svg-images", "table");
13896
13971
  ctx.features.require("images", "table");
13897
- return [
13898
- {
13899
- kind: "image",
13900
- resourceId: declareResource(loaded, mediaType, ctx),
13901
- widthEmu: pixelsToEmu(size.width),
13902
- heightEmu: pixelsToEmu(size.height)
13903
- }
13904
- ];
13972
+ const image = {
13973
+ kind: "image",
13974
+ resourceId: declareResource(loaded, mediaType, ctx),
13975
+ widthEmu: pixelsToEmu(size.width),
13976
+ heightEmu: pixelsToEmu(size.height)
13977
+ };
13978
+ recordImageOrigin(ctx, component, [image]);
13979
+ return [image];
13905
13980
  }
13906
13981
  function placeholderFormatting(base) {
13907
13982
  return {
@@ -16456,7 +16531,7 @@ async function compileDocumentScoped(document, options, warnings) {
16456
16531
  const embeddedFonts = embedding ? selectEmbeddedFonts(resolvedFonts, warnings) : [];
16457
16532
  const visualFonts = hasVisual ? toRasterizeFontFaces(resolvedFonts, warnings) : [];
16458
16533
  const chartFonts = hasHighcharts ? toChartFontFaces(resolvedFonts) : [];
16459
- const desugared = await desugarExternals(context.document, {
16534
+ const desugared = await desugarExternals(tagNodeOrigins(context.document), {
16460
16535
  theme: prepared.model.document.theme,
16461
16536
  ...options.services ? { services: options.services } : {},
16462
16537
  ...options.baseDir !== void 0 ? { baseDir: options.baseDir } : {},
@@ -16494,6 +16569,7 @@ async function compileDocumentScoped(document, options, warnings) {
16494
16569
  warnings: compiled.warnings,
16495
16570
  required: compiled.required,
16496
16571
  unsupported: compiled.unsupported,
16572
+ imageOrigins: compiled.imageOrigins,
16497
16573
  embeddedFonts
16498
16574
  };
16499
16575
  }
@@ -16521,13 +16597,291 @@ async function renderScoped(document, options, warnings) {
16521
16597
  ...compiled.embeddedFonts.length > 0 ? { embeddedFonts: compiled.embeddedFonts } : {},
16522
16598
  warnings: compiled.warnings
16523
16599
  });
16524
- return { buffer: Buffer.from(bytes), warnings: compiled.warnings };
16600
+ return {
16601
+ buffer: Buffer.from(bytes),
16602
+ warnings: compiled.warnings,
16603
+ ir: compiled.ir,
16604
+ imageOrigins: compiled.imageOrigins
16605
+ };
16525
16606
  }
16526
16607
  function toDate(value) {
16527
16608
  return value instanceof Date ? value : new Date(value);
16528
16609
  }
16529
16610
 
16611
+ // src/core/renderManifest.ts
16612
+ init_units();
16613
+ import AdmZip6 from "adm-zip";
16614
+ import { posix as posix2 } from "path";
16615
+ var HEADER_FOOTER_TYPES = ["default", "first", "even"];
16616
+ function buildRenderManifest(buffer, ir, imageOrigins, sourceOrigins = /* @__PURE__ */ new Map()) {
16617
+ try {
16618
+ return {
16619
+ ok: true,
16620
+ manifest: collect(buffer, ir, imageOrigins, sourceOrigins)
16621
+ };
16622
+ } catch (error) {
16623
+ return {
16624
+ ok: false,
16625
+ reason: error instanceof Error ? error.message : String(error)
16626
+ };
16627
+ }
16628
+ }
16629
+ function collect(buffer, ir, imageOrigins, sourceOrigins) {
16630
+ const zip = new AdmZip6(buffer);
16631
+ const read = (entry) => zip.getEntry(entry)?.getData().toString("utf8");
16632
+ const media = /* @__PURE__ */ new Map();
16633
+ const mediaHash = (path3) => {
16634
+ let hash = media.get(path3);
16635
+ if (hash === void 0) {
16636
+ const entry = zip.getEntry(path3);
16637
+ if (!entry) throw new Error(`the package has no ${path3}`);
16638
+ hash = sha256Hex(new Uint8Array(entry.getData()));
16639
+ media.set(path3, hash);
16640
+ }
16641
+ return hash;
16642
+ };
16643
+ const resources = new Map(ir.resources.map((r) => [r.id, r]));
16644
+ const manifest = [];
16645
+ const pair = (part, partPath, xml, images) => {
16646
+ const rels = relationships(read(relsPathOf(partPath)), partPath);
16647
+ const blips = readBlips(xml);
16648
+ if (blips.length !== images.length) {
16649
+ throw new Error(
16650
+ `${partPath} embeds ${blips.length} image(s) where the IR placed ${images.length}`
16651
+ );
16652
+ }
16653
+ blips.forEach((blip, index) => {
16654
+ const image = images[index];
16655
+ const mediaPath = target(rels, blip.embed, partPath);
16656
+ const sha256 = mediaHash(mediaPath);
16657
+ const resource = resources.get(image.resourceId);
16658
+ if (!resource || !matchesResource(resource, blip, sha256, rels, partPath, mediaHash)) {
16659
+ throw new Error(
16660
+ `image ${index + 1} of ${partPath} (${mediaPath}) does not hold the bytes of IR resource ${image.resourceId}`
16661
+ );
16662
+ }
16663
+ const origin = imageOrigins.get(image);
16664
+ if (!origin) {
16665
+ throw new Error(
16666
+ `image ${index + 1} of ${partPath} has no standard-definition origin`
16667
+ );
16668
+ }
16669
+ const source = sourceOriginOf(origin.standardPath, sourceOrigins);
16670
+ manifest.push({
16671
+ part,
16672
+ partPath,
16673
+ mediaPath,
16674
+ sha256,
16675
+ standardPath: origin.standardPath,
16676
+ kind: origin.kind,
16677
+ ...source && {
16678
+ sourcePath: source.sourcePath,
16679
+ sourceKind: source.sourceKind
16680
+ }
16681
+ });
16682
+ });
16683
+ };
16684
+ const documentPath = "word/document.xml";
16685
+ const documentXml = read(documentPath);
16686
+ if (documentXml === void 0)
16687
+ throw new Error(`the package has no ${documentPath}`);
16688
+ pair(
16689
+ "body",
16690
+ documentPath,
16691
+ documentXml,
16692
+ ir.sections.flatMap((section2) => blockImages(section2.children))
16693
+ );
16694
+ const sectPrs = sectionProperties(documentXml);
16695
+ if (sectPrs.length !== ir.sections.length) {
16696
+ throw new Error(
16697
+ `${documentPath} has ${sectPrs.length} section(s) where the IR has ${ir.sections.length}`
16698
+ );
16699
+ }
16700
+ const documentRels = relationships(
16701
+ read(relsPathOf(documentPath)),
16702
+ documentPath
16703
+ );
16704
+ const seen = /* @__PURE__ */ new Set();
16705
+ ir.sections.forEach((section2, index) => {
16706
+ const references = chromeReferences(sectPrs[index]);
16707
+ for (const part of ["header", "footer"]) {
16708
+ const set = part === "header" ? section2.headers : section2.footers;
16709
+ for (const type of HEADER_FOOTER_TYPES) {
16710
+ const id = references.get(`${part}:${type}`);
16711
+ if (id === void 0) continue;
16712
+ const partPath = target(documentRels, id, documentPath);
16713
+ if (seen.has(partPath)) continue;
16714
+ seen.add(partPath);
16715
+ const xml = read(partPath);
16716
+ if (xml === void 0)
16717
+ throw new Error(`the package has no ${partPath}`);
16718
+ pair(part, partPath, xml, blockImages(set?.[type]?.children ?? []));
16719
+ }
16720
+ }
16721
+ });
16722
+ return manifest;
16723
+ }
16724
+ function sourceOriginOf(standardPath, sourceOrigins) {
16725
+ if (sourceOrigins.size === 0) return void 0;
16726
+ const segments = standardPath.split(".");
16727
+ for (let n = segments.length; n > 0; n--) {
16728
+ const hit = sourceOrigins.get(segments.slice(0, n).join("."));
16729
+ if (hit) return hit;
16730
+ }
16731
+ return void 0;
16732
+ }
16733
+ function matchesResource(resource, blip, sha256, rels, partPath, mediaHash) {
16734
+ if (sha256 === resource.sha256) return true;
16735
+ if (resource.mediaType !== "svg" || blip.svgEmbed === void 0) return false;
16736
+ return mediaHash(target(rels, blip.svgEmbed, partPath)) === resource.sha256;
16737
+ }
16738
+ function blockImages(blocks) {
16739
+ const out = [];
16740
+ const visitBlocks = (items) => {
16741
+ for (const block2 of items) {
16742
+ if (block2.kind === "paragraph") visitParagraph(block2);
16743
+ else if (block2.kind === "table") {
16744
+ for (const row of block2.rows) {
16745
+ for (const cell of row.cells) visitBlocks(cell.children);
16746
+ }
16747
+ }
16748
+ }
16749
+ };
16750
+ const visitParagraph = (paragraph2) => visitInlines(paragraph2.children);
16751
+ const visitInlines = (inlines) => {
16752
+ for (const inline of inlines) {
16753
+ switch (inline.kind) {
16754
+ case "image":
16755
+ out.push(inline);
16756
+ break;
16757
+ case "drawingGroup":
16758
+ for (const child of inline.children) {
16759
+ if (child.kind === "picture") out.push(child);
16760
+ }
16761
+ break;
16762
+ case "shape":
16763
+ inline.children.forEach(visitParagraph);
16764
+ break;
16765
+ case "hyperlink":
16766
+ case "revision":
16767
+ visitInlines(inline.children);
16768
+ break;
16769
+ default:
16770
+ break;
16771
+ }
16772
+ }
16773
+ };
16774
+ visitBlocks(blocks);
16775
+ return out;
16776
+ }
16777
+ function readBlips(xml) {
16778
+ const body = xml.replace(/<mc:Fallback\b[\s\S]*?<\/mc:Fallback>/g, "");
16779
+ const blips = [];
16780
+ const pattern = /<a:blip\b([^>]*?)(?:\/>|>([\s\S]*?)<\/a:blip>)/g;
16781
+ for (const match of body.matchAll(pattern)) {
16782
+ const embed = /\br:embed="([^"]+)"/.exec(match[1])?.[1];
16783
+ if (embed === void 0) continue;
16784
+ const svgEmbed = match[2] ? /<asvg:svgBlip\b[^>]*\br:embed="([^"]+)"/.exec(match[2])?.[1] : void 0;
16785
+ blips.push(svgEmbed === void 0 ? { embed } : { embed, svgEmbed });
16786
+ }
16787
+ return blips;
16788
+ }
16789
+ function sectionProperties(xml) {
16790
+ return [...xml.matchAll(/<w:sectPr\b(?:[^>]*\/>|[\s\S]*?<\/w:sectPr>)/g)].map(
16791
+ (m) => m[0]
16792
+ );
16793
+ }
16794
+ function chromeReferences(sectPr) {
16795
+ const out = /* @__PURE__ */ new Map();
16796
+ for (const match of sectPr.matchAll(
16797
+ /<w:(header|footer)Reference\b([^>]*)\/?>/g
16798
+ )) {
16799
+ const type = attribute(match[2], "w:type") ?? "default";
16800
+ const id = attribute(match[2], "r:id");
16801
+ if (id !== void 0) out.set(`${match[1]}:${type}`, id);
16802
+ }
16803
+ return out;
16804
+ }
16805
+ function relsPathOf(partPath) {
16806
+ return posix2.join(
16807
+ posix2.dirname(partPath),
16808
+ "_rels",
16809
+ `${posix2.basename(partPath)}.rels`
16810
+ );
16811
+ }
16812
+ function relationships(xml, partPath) {
16813
+ const out = /* @__PURE__ */ new Map();
16814
+ if (xml === void 0) return out;
16815
+ for (const match of xml.matchAll(/<Relationship\b([^>]*)\/?>/g)) {
16816
+ const id = attribute(match[1], "Id");
16817
+ const target2 = attribute(match[1], "Target");
16818
+ if (id !== void 0 && target2 !== void 0) out.set(id, target2);
16819
+ }
16820
+ if (out.size === 0 && /<Relationship\b/.test(xml)) {
16821
+ throw new Error(`the relationships of ${partPath} could not be read`);
16822
+ }
16823
+ return out;
16824
+ }
16825
+ function target(rels, id, partPath) {
16826
+ const raw = rels.get(id);
16827
+ if (raw === void 0) {
16828
+ throw new Error(`${partPath} references an unknown relationship ${id}`);
16829
+ }
16830
+ return raw.startsWith("/") ? raw.slice(1) : posix2.normalize(posix2.join(posix2.dirname(partPath), raw));
16831
+ }
16832
+ function attribute(attributes, name) {
16833
+ const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16834
+ return new RegExp(`(?:^|\\s)${escaped}="([^"]*)"`).exec(attributes)?.[1];
16835
+ }
16836
+
16530
16837
  // src/plugin/createDocumentGenerator.ts
16838
+ var RENDER_MANIFEST_UNAVAILABLE = "W_DOCX_RENDER_MANIFEST_UNAVAILABLE";
16839
+ var ROOT_POSITION = {
16840
+ standard: "children",
16841
+ standardBase: 0,
16842
+ preserved: "children",
16843
+ preservedBase: 0
16844
+ };
16845
+ var DETACHED_POSITION = {
16846
+ standard: null,
16847
+ standardBase: 0,
16848
+ preserved: null,
16849
+ preservedBase: 0
16850
+ };
16851
+ function joinPath(path3, key) {
16852
+ return path3 === null ? null : `${path3}.${key}`;
16853
+ }
16854
+ function blockSourceOrigins(standard, preserved, preserveSet) {
16855
+ const origins = /* @__PURE__ */ new Map();
16856
+ const visit = (std, kept, path3) => {
16857
+ if (!std || !kept || typeof std !== "object" || typeof kept !== "object")
16858
+ return;
16859
+ const keptNode = kept;
16860
+ const stdNode = std;
16861
+ if (path3 !== "" && typeof keptNode.name === "string" && preserveSet.has(keptNode.name) && stdNode.name === "group") {
16862
+ origins.set(path3, { sourcePath: path3, sourceKind: keptNode.name });
16863
+ return;
16864
+ }
16865
+ for (const [key, value] of Object.entries(keptNode)) {
16866
+ if (key in stdNode) {
16867
+ visit(stdNode[key], value, path3 === "" ? key : `${path3}.${key}`);
16868
+ }
16869
+ }
16870
+ };
16871
+ visit(standard, preserved, "");
16872
+ return origins;
16873
+ }
16874
+ function manifestOf(buffer, ir, imageOrigins, sourceOrigins, warnings) {
16875
+ const built = buildRenderManifest(buffer, ir, imageOrigins, sourceOrigins);
16876
+ if (built.ok) return built.manifest;
16877
+ warnings.push({
16878
+ component: "docx",
16879
+ severity: "warning",
16880
+ message: `No render manifest: ${built.reason}.`,
16881
+ context: { code: RENDER_MANIFEST_UNAVAILABLE, reason: built.reason }
16882
+ });
16883
+ return void 0;
16884
+ }
16531
16885
  function createBuilderImpl(state) {
16532
16886
  const componentMap = new Map(state.components.map((c) => [c.name, c]));
16533
16887
  function resolveDocumentTheme(themeName, warnings, authored) {
@@ -16556,7 +16910,7 @@ function createBuilderImpl(state) {
16556
16910
  warnings
16557
16911
  });
16558
16912
  }
16559
- async function processDocumentComponents(components, preserveSet, warningsCollector, resolvedTheme, validateEmitted, depth = 0) {
16913
+ async function processDocumentComponents(components, preserveSet, warningsCollector, resolvedTheme, validateEmitted, at, origins, depth = 0) {
16560
16914
  if (depth > 20) {
16561
16915
  throw new Error(
16562
16916
  "Maximum component nesting depth exceeded (20). Check for circular component references."
@@ -16564,6 +16918,8 @@ function createBuilderImpl(state) {
16564
16918
  }
16565
16919
  const standardOut = [];
16566
16920
  const preservedOut = [];
16921
+ const nextStandardPath = () => joinPath(at.standard, at.standardBase + standardOut.length);
16922
+ const nextPreservedPath = () => joinPath(at.preserved, at.preservedBase + preservedOut.length);
16567
16923
  for (const componentData of components) {
16568
16924
  const componentName = componentData?.name;
16569
16925
  if (!componentName) {
@@ -16597,6 +16953,8 @@ function createBuilderImpl(state) {
16597
16953
  warningsCollector,
16598
16954
  resolvedTheme,
16599
16955
  validateEmitted,
16956
+ DETACHED_POSITION,
16957
+ origins,
16600
16958
  depth + 1
16601
16959
  );
16602
16960
  nestedChildren = nested.standard;
@@ -16620,16 +16978,34 @@ function createBuilderImpl(state) {
16620
16978
  if (validateEmitted) {
16621
16979
  validateEmitted(resultComponents, versionLabel);
16622
16980
  }
16981
+ const preservedHere = preserveSet?.has(componentName) === true;
16982
+ const preservedPath = nextPreservedPath();
16983
+ const firstIndex = at.standardBase + standardOut.length;
16623
16984
  const processedResult = await processDocumentComponents(
16624
16985
  resultComponents,
16625
16986
  preserveSet,
16626
16987
  warningsCollector,
16627
16988
  resolvedTheme,
16628
16989
  validateEmitted,
16990
+ {
16991
+ standard: at.standard,
16992
+ standardBase: firstIndex,
16993
+ preserved: preservedHere ? null : at.preserved,
16994
+ preservedBase: at.preservedBase + preservedOut.length
16995
+ },
16996
+ origins,
16629
16997
  depth + 1
16630
16998
  );
16999
+ if (preservedHere && at.standard !== null && preservedPath !== null) {
17000
+ processedResult.standard.forEach((_, index) => {
17001
+ origins.set(`${at.standard}.${firstIndex + index}`, {
17002
+ sourcePath: preservedPath,
17003
+ sourceKind: componentName
17004
+ });
17005
+ });
17006
+ }
16631
17007
  standardOut.push(...processedResult.standard);
16632
- if (preserveSet && preserveSet.has(componentName)) {
17008
+ if (preservedHere) {
16633
17009
  preservedOut.push(componentData);
16634
17010
  } else {
16635
17011
  preservedOut.push(...processedResult.preserved);
@@ -16650,12 +17026,21 @@ function createBuilderImpl(state) {
16650
17026
  }
16651
17027
  } else {
16652
17028
  if ("children" in componentData && Array.isArray(componentData.children)) {
17029
+ const standardPath = nextStandardPath();
17030
+ const preservedPath = nextPreservedPath();
16653
17031
  const processedNested = await processDocumentComponents(
16654
17032
  componentData.children,
16655
17033
  preserveSet,
16656
17034
  warningsCollector,
16657
17035
  resolvedTheme,
16658
17036
  validateEmitted,
17037
+ {
17038
+ standard: joinPath(standardPath, "children"),
17039
+ standardBase: 0,
17040
+ preserved: joinPath(preservedPath, "children"),
17041
+ preservedBase: 0
17042
+ },
17043
+ origins,
16659
17044
  depth + 1
16660
17045
  );
16661
17046
  standardOut.push({
@@ -16819,6 +17204,7 @@ function createBuilderImpl(state) {
16819
17204
  );
16820
17205
  const [modedDoc2] = normalizeDocument(expanded.document);
16821
17206
  return {
17207
+ sourceOrigins: preserveSet ? blockSourceOrigins(modedDoc2, expanded.preserved, preserveSet) : /* @__PURE__ */ new Map(),
16822
17208
  modedRoot,
16823
17209
  modedTheme,
16824
17210
  themeName,
@@ -16834,12 +17220,15 @@ function createBuilderImpl(state) {
16834
17220
  }
16835
17221
  };
16836
17222
  }
17223
+ const sourceOrigins = /* @__PURE__ */ new Map();
16837
17224
  const processed = await processDocumentComponents(
16838
17225
  modedRoot.children || [],
16839
17226
  preserveSet,
16840
17227
  warnings,
16841
17228
  modedTheme,
16842
- validateEmitted
17229
+ validateEmitted,
17230
+ ROOT_POSITION,
17231
+ sourceOrigins
16843
17232
  );
16844
17233
  const processedDocument = {
16845
17234
  ...modedRoot,
@@ -16853,7 +17242,8 @@ function createBuilderImpl(state) {
16853
17242
  processed,
16854
17243
  modedDoc,
16855
17244
  warnings,
16856
- preserveSet
17245
+ preserveSet,
17246
+ sourceOrigins
16857
17247
  };
16858
17248
  }
16859
17249
  async function expandStandardDefinition(document, options) {
@@ -16910,22 +17300,33 @@ function createBuilderImpl(state) {
16910
17300
  const deterministic = options?.deterministic ?? state.deterministic;
16911
17301
  const generatedAt = options?.generatedAt ?? state.generatedAt;
16912
17302
  const renderer = options?.renderer ?? state.renderer ?? prepared.renderer;
16913
- const { buffer } = await generateBufferViaIr(context.document, {
16914
- context,
16915
- prepared,
16916
- ...state.services ? { services: state.services } : {},
16917
- ...state.fonts ? { fonts: state.fonts } : {},
16918
- ...baseDir !== void 0 ? { baseDir } : {},
16919
- ...deterministic !== void 0 ? { deterministic } : {},
16920
- ...generatedAt !== void 0 ? { generatedAt } : {},
16921
- ...renderer !== void 0 ? { renderer } : {},
17303
+ const { buffer, ir, imageOrigins } = await generateBufferViaIr(
17304
+ context.document,
17305
+ {
17306
+ context,
17307
+ prepared,
17308
+ ...state.services ? { services: state.services } : {},
17309
+ ...state.fonts ? { fonts: state.fonts } : {},
17310
+ ...baseDir !== void 0 ? { baseDir } : {},
17311
+ ...deterministic !== void 0 ? { deterministic } : {},
17312
+ ...generatedAt !== void 0 ? { generatedAt } : {},
17313
+ ...renderer !== void 0 ? { renderer } : {},
17314
+ warnings
17315
+ }
17316
+ );
17317
+ const renderManifest = manifestOf(
17318
+ buffer,
17319
+ ir,
17320
+ imageOrigins,
17321
+ /* @__PURE__ */ new Map(),
16922
17322
  warnings
16923
- });
17323
+ );
16924
17324
  return {
16925
17325
  buffer,
16926
17326
  warnings: warnings.length > 0 ? warnings : null,
16927
17327
  standardDefinition: context.document,
16928
- preservedDefinition: void 0
17328
+ preservedDefinition: void 0,
17329
+ ...renderManifest && { renderManifest }
16929
17330
  };
16930
17331
  }
16931
17332
  async function generateBuffer(document, options) {
@@ -16941,13 +17342,14 @@ function createBuilderImpl(state) {
16941
17342
  sourceMap,
16942
17343
  blockPaths,
16943
17344
  warnings,
16944
- preserveSet
17345
+ preserveSet,
17346
+ sourceOrigins
16945
17347
  } = await expandDocument(document, options);
16946
17348
  const baseDir = options?.baseDir ?? state.baseDir;
16947
17349
  const deterministic = options?.deterministic ?? state.deterministic;
16948
17350
  const generatedAt = options?.generatedAt ?? state.generatedAt;
16949
17351
  const renderer = options?.renderer ?? state.renderer ?? modedRoot.renderer;
16950
- const { buffer } = await generateBufferViaIr(modedDoc, {
17352
+ const { buffer, ir, imageOrigins } = await generateBufferViaIr(modedDoc, {
16951
17353
  // The prologue already ran: the theme had to be resolved before custom
16952
17354
  // components expanded, because each one's `render` is handed it.
16953
17355
  context: {
@@ -16969,11 +17371,19 @@ function createBuilderImpl(state) {
16969
17371
  ...modedRoot,
16970
17372
  children: processed.preserved
16971
17373
  } : void 0;
17374
+ const renderManifest = manifestOf(
17375
+ buffer,
17376
+ ir,
17377
+ imageOrigins,
17378
+ sourceOrigins,
17379
+ warnings
17380
+ );
16972
17381
  return {
16973
17382
  buffer,
16974
17383
  warnings: warnings.length > 0 ? warnings : null,
16975
17384
  standardDefinition: modedDoc,
16976
- preservedDefinition
17385
+ preservedDefinition,
17386
+ ...renderManifest && { renderManifest }
16977
17387
  };
16978
17388
  } catch (error) {
16979
17389
  if (state.debug) {
@@ -16983,7 +17393,13 @@ function createBuilderImpl(state) {
16983
17393
  }
16984
17394
  }
16985
17395
  async function generateFile(document, outputPath, options) {
16986
- const { buffer, warnings, standardDefinition, preservedDefinition } = await generateBuffer(document, options);
17396
+ const {
17397
+ buffer,
17398
+ warnings,
17399
+ standardDefinition,
17400
+ preservedDefinition,
17401
+ renderManifest
17402
+ } = await generateBuffer(document, options);
16987
17403
  const fs4 = await import("fs/promises");
16988
17404
  await fs4.writeFile(outputPath, new Uint8Array(buffer));
16989
17405
  let preservedOutputPath;
@@ -17004,7 +17420,8 @@ function createBuilderImpl(state) {
17004
17420
  warnings,
17005
17421
  standardDefinition,
17006
17422
  preservedDefinition,
17007
- preservedOutputPath
17423
+ preservedOutputPath,
17424
+ ...renderManifest && { renderManifest }
17008
17425
  };
17009
17426
  }
17010
17427
  function getComponentNames() {